Skip to main content

oxilean_codegen/php_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::HashMap;
6
7use super::functions::*;
8use std::collections::{HashSet, VecDeque};
9
10/// PHP function/method parameter.
11#[derive(Debug, Clone, PartialEq)]
12pub struct PHPParam {
13    /// Parameter name (without `$`)
14    pub name: std::string::String,
15    /// Optional type hint
16    pub ty: Option<PHPType>,
17    /// Optional default value
18    pub default: Option<PHPExpr>,
19    /// Whether this is a reference parameter (`&$name`)
20    pub by_ref: bool,
21    /// Whether this is a variadic parameter (`...$name`)
22    pub variadic: bool,
23    /// Whether this is a promoted constructor property (PHP 8.0+)
24    pub promoted: Option<PHPVisibility>,
25}
26impl PHPParam {
27    /// Create a simple parameter with a name.
28    pub fn simple(name: impl Into<std::string::String>) -> Self {
29        PHPParam {
30            name: name.into(),
31            ty: None,
32            default: None,
33            by_ref: false,
34            variadic: false,
35            promoted: None,
36        }
37    }
38    /// Create a typed parameter.
39    pub fn typed(name: impl Into<std::string::String>, ty: PHPType) -> Self {
40        PHPParam {
41            name: name.into(),
42            ty: Some(ty),
43            default: None,
44            by_ref: false,
45            variadic: false,
46            promoted: None,
47        }
48    }
49    /// Create a parameter with a default value.
50    pub fn with_default(
51        name: impl Into<std::string::String>,
52        ty: Option<PHPType>,
53        default: PHPExpr,
54    ) -> Self {
55        PHPParam {
56            name: name.into(),
57            ty,
58            default: Some(default),
59            by_ref: false,
60            variadic: false,
61            promoted: None,
62        }
63    }
64}
65/// A complete PHP script / file.
66#[derive(Debug, Clone)]
67pub struct PHPScript {
68    /// Whether to include `declare(strict_types=1)`
69    pub strict_types: bool,
70    /// Namespace (optional)
71    pub namespace: Option<std::string::String>,
72    /// `use` import statements
73    pub uses: Vec<(std::string::String, Option<std::string::String>)>,
74    /// Top-level functions
75    pub functions: Vec<PHPFunction>,
76    /// Top-level classes
77    pub classes: Vec<PHPClass>,
78    /// Interfaces
79    pub interfaces: Vec<PHPInterface>,
80    /// Traits
81    pub traits: Vec<PHPTrait>,
82    /// Enums
83    pub enums: Vec<PHPEnum>,
84    /// Top-level statements (main body)
85    pub main: Vec<std::string::String>,
86}
87impl PHPScript {
88    /// Create a new PHP script.
89    pub fn new() -> Self {
90        PHPScript {
91            strict_types: true,
92            namespace: None,
93            uses: vec![],
94            functions: vec![],
95            classes: vec![],
96            interfaces: vec![],
97            traits: vec![],
98            enums: vec![],
99            main: vec![],
100        }
101    }
102}
103/// Statistics for PHPExt passes.
104#[allow(dead_code)]
105#[derive(Debug, Clone, Default)]
106pub struct PHPExtPassStats {
107    pub iterations: usize,
108    pub changed: bool,
109    pub nodes_visited: usize,
110    pub nodes_modified: usize,
111    pub time_ms: u64,
112    pub memory_bytes: usize,
113    pub errors: usize,
114}
115impl PHPExtPassStats {
116    #[allow(dead_code)]
117    pub fn new() -> Self {
118        Self::default()
119    }
120    #[allow(dead_code)]
121    pub fn visit(&mut self) {
122        self.nodes_visited += 1;
123    }
124    #[allow(dead_code)]
125    pub fn modify(&mut self) {
126        self.nodes_modified += 1;
127        self.changed = true;
128    }
129    #[allow(dead_code)]
130    pub fn iterate(&mut self) {
131        self.iterations += 1;
132    }
133    #[allow(dead_code)]
134    pub fn error(&mut self) {
135        self.errors += 1;
136    }
137    #[allow(dead_code)]
138    pub fn efficiency(&self) -> f64 {
139        if self.nodes_visited == 0 {
140            0.0
141        } else {
142            self.nodes_modified as f64 / self.nodes_visited as f64
143        }
144    }
145    #[allow(dead_code)]
146    pub fn merge(&mut self, o: &PHPExtPassStats) {
147        self.iterations += o.iterations;
148        self.changed |= o.changed;
149        self.nodes_visited += o.nodes_visited;
150        self.nodes_modified += o.nodes_modified;
151        self.time_ms += o.time_ms;
152        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
153        self.errors += o.errors;
154    }
155}
156/// Worklist for PHPExt.
157#[allow(dead_code)]
158#[derive(Debug, Clone)]
159pub struct PHPExtWorklist {
160    pub(super) items: std::collections::VecDeque<usize>,
161    pub(super) present: Vec<bool>,
162}
163impl PHPExtWorklist {
164    #[allow(dead_code)]
165    pub fn new(capacity: usize) -> Self {
166        Self {
167            items: std::collections::VecDeque::new(),
168            present: vec![false; capacity],
169        }
170    }
171    #[allow(dead_code)]
172    pub fn push(&mut self, id: usize) {
173        if id < self.present.len() && !self.present[id] {
174            self.present[id] = true;
175            self.items.push_back(id);
176        }
177    }
178    #[allow(dead_code)]
179    pub fn push_front(&mut self, id: usize) {
180        if id < self.present.len() && !self.present[id] {
181            self.present[id] = true;
182            self.items.push_front(id);
183        }
184    }
185    #[allow(dead_code)]
186    pub fn pop(&mut self) -> Option<usize> {
187        let id = self.items.pop_front()?;
188        if id < self.present.len() {
189            self.present[id] = false;
190        }
191        Some(id)
192    }
193    #[allow(dead_code)]
194    pub fn is_empty(&self) -> bool {
195        self.items.is_empty()
196    }
197    #[allow(dead_code)]
198    pub fn len(&self) -> usize {
199        self.items.len()
200    }
201    #[allow(dead_code)]
202    pub fn contains(&self, id: usize) -> bool {
203        id < self.present.len() && self.present[id]
204    }
205    #[allow(dead_code)]
206    pub fn drain_all(&mut self) -> Vec<usize> {
207        let v: Vec<usize> = self.items.drain(..).collect();
208        for &id in &v {
209            if id < self.present.len() {
210                self.present[id] = false;
211            }
212        }
213        v
214    }
215}
216/// PHP trait declaration.
217#[derive(Debug, Clone, PartialEq)]
218pub struct PHPTrait {
219    /// Trait name
220    pub name: std::string::String,
221    /// Properties
222    pub properties: Vec<PHPProperty>,
223    /// Methods
224    pub methods: Vec<PHPFunction>,
225}
226impl PHPTrait {
227    /// Create a new trait.
228    pub fn new(name: impl Into<std::string::String>) -> Self {
229        PHPTrait {
230            name: name.into(),
231            properties: vec![],
232            methods: vec![],
233        }
234    }
235}
236#[allow(dead_code)]
237#[derive(Debug, Clone)]
238pub struct PHPAnalysisCache {
239    pub(super) entries: std::collections::HashMap<String, PHPCacheEntry>,
240    pub(super) max_size: usize,
241    pub(super) hits: u64,
242    pub(super) misses: u64,
243}
244impl PHPAnalysisCache {
245    #[allow(dead_code)]
246    pub fn new(max_size: usize) -> Self {
247        PHPAnalysisCache {
248            entries: std::collections::HashMap::new(),
249            max_size,
250            hits: 0,
251            misses: 0,
252        }
253    }
254    #[allow(dead_code)]
255    pub fn get(&mut self, key: &str) -> Option<&PHPCacheEntry> {
256        if self.entries.contains_key(key) {
257            self.hits += 1;
258            self.entries.get(key)
259        } else {
260            self.misses += 1;
261            None
262        }
263    }
264    #[allow(dead_code)]
265    pub fn insert(&mut self, key: String, data: Vec<u8>) {
266        if self.entries.len() >= self.max_size {
267            if let Some(oldest) = self.entries.keys().next().cloned() {
268                self.entries.remove(&oldest);
269            }
270        }
271        self.entries.insert(
272            key.clone(),
273            PHPCacheEntry {
274                key,
275                data,
276                timestamp: 0,
277                valid: true,
278            },
279        );
280    }
281    #[allow(dead_code)]
282    pub fn invalidate(&mut self, key: &str) {
283        if let Some(entry) = self.entries.get_mut(key) {
284            entry.valid = false;
285        }
286    }
287    #[allow(dead_code)]
288    pub fn clear(&mut self) {
289        self.entries.clear();
290    }
291    #[allow(dead_code)]
292    pub fn hit_rate(&self) -> f64 {
293        let total = self.hits + self.misses;
294        if total == 0 {
295            return 0.0;
296        }
297        self.hits as f64 / total as f64
298    }
299    #[allow(dead_code)]
300    pub fn size(&self) -> usize {
301        self.entries.len()
302    }
303}
304/// PHP class declaration.
305#[derive(Debug, Clone, PartialEq)]
306pub struct PHPClass {
307    /// Class name
308    pub name: std::string::String,
309    /// Optional parent class
310    pub parent: Option<std::string::String>,
311    /// Implemented interfaces
312    pub interfaces: Vec<std::string::String>,
313    /// Used traits
314    pub traits: Vec<std::string::String>,
315    /// Whether this is abstract
316    pub is_abstract: bool,
317    /// Whether this is final
318    pub is_final: bool,
319    /// Whether this is readonly (PHP 8.2+)
320    pub is_readonly: bool,
321    /// Properties
322    pub properties: Vec<PHPProperty>,
323    /// Methods
324    pub methods: Vec<PHPFunction>,
325    /// Class constants
326    pub constants: Vec<(std::string::String, PHPType, std::string::String)>,
327}
328impl PHPClass {
329    /// Create a new empty class.
330    pub fn new(name: impl Into<std::string::String>) -> Self {
331        PHPClass {
332            name: name.into(),
333            parent: None,
334            interfaces: vec![],
335            traits: vec![],
336            is_abstract: false,
337            is_final: false,
338            is_readonly: false,
339            properties: vec![],
340            methods: vec![],
341            constants: vec![],
342        }
343    }
344    /// Create an abstract class.
345    pub fn abstract_class(name: impl Into<std::string::String>) -> Self {
346        let mut cls = PHPClass::new(name);
347        cls.is_abstract = true;
348        cls
349    }
350}
351#[allow(dead_code)]
352#[derive(Debug, Clone)]
353pub struct PHPWorklist {
354    pub(super) items: std::collections::VecDeque<u32>,
355    pub(super) in_worklist: std::collections::HashSet<u32>,
356}
357impl PHPWorklist {
358    #[allow(dead_code)]
359    pub fn new() -> Self {
360        PHPWorklist {
361            items: std::collections::VecDeque::new(),
362            in_worklist: std::collections::HashSet::new(),
363        }
364    }
365    #[allow(dead_code)]
366    pub fn push(&mut self, item: u32) -> bool {
367        if self.in_worklist.insert(item) {
368            self.items.push_back(item);
369            true
370        } else {
371            false
372        }
373    }
374    #[allow(dead_code)]
375    pub fn pop(&mut self) -> Option<u32> {
376        let item = self.items.pop_front()?;
377        self.in_worklist.remove(&item);
378        Some(item)
379    }
380    #[allow(dead_code)]
381    pub fn is_empty(&self) -> bool {
382        self.items.is_empty()
383    }
384    #[allow(dead_code)]
385    pub fn len(&self) -> usize {
386        self.items.len()
387    }
388    #[allow(dead_code)]
389    pub fn contains(&self, item: u32) -> bool {
390        self.in_worklist.contains(&item)
391    }
392}
393#[allow(dead_code)]
394pub struct PHPPassRegistry {
395    pub(super) configs: Vec<PHPPassConfig>,
396    pub(super) stats: std::collections::HashMap<String, PHPPassStats>,
397}
398impl PHPPassRegistry {
399    #[allow(dead_code)]
400    pub fn new() -> Self {
401        PHPPassRegistry {
402            configs: Vec::new(),
403            stats: std::collections::HashMap::new(),
404        }
405    }
406    #[allow(dead_code)]
407    pub fn register(&mut self, config: PHPPassConfig) {
408        self.stats
409            .insert(config.pass_name.clone(), PHPPassStats::new());
410        self.configs.push(config);
411    }
412    #[allow(dead_code)]
413    pub fn enabled_passes(&self) -> Vec<&PHPPassConfig> {
414        self.configs.iter().filter(|c| c.enabled).collect()
415    }
416    #[allow(dead_code)]
417    pub fn get_stats(&self, name: &str) -> Option<&PHPPassStats> {
418        self.stats.get(name)
419    }
420    #[allow(dead_code)]
421    pub fn total_passes(&self) -> usize {
422        self.configs.len()
423    }
424    #[allow(dead_code)]
425    pub fn enabled_count(&self) -> usize {
426        self.enabled_passes().len()
427    }
428    #[allow(dead_code)]
429    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
430        if let Some(stats) = self.stats.get_mut(name) {
431            stats.record_run(changes, time_ms, iter);
432        }
433    }
434}
435#[allow(dead_code)]
436#[derive(Debug, Clone)]
437pub struct PHPLivenessInfo {
438    pub live_in: Vec<std::collections::HashSet<u32>>,
439    pub live_out: Vec<std::collections::HashSet<u32>>,
440    pub defs: Vec<std::collections::HashSet<u32>>,
441    pub uses: Vec<std::collections::HashSet<u32>>,
442}
443impl PHPLivenessInfo {
444    #[allow(dead_code)]
445    pub fn new(block_count: usize) -> Self {
446        PHPLivenessInfo {
447            live_in: vec![std::collections::HashSet::new(); block_count],
448            live_out: vec![std::collections::HashSet::new(); block_count],
449            defs: vec![std::collections::HashSet::new(); block_count],
450            uses: vec![std::collections::HashSet::new(); block_count],
451        }
452    }
453    #[allow(dead_code)]
454    pub fn add_def(&mut self, block: usize, var: u32) {
455        if block < self.defs.len() {
456            self.defs[block].insert(var);
457        }
458    }
459    #[allow(dead_code)]
460    pub fn add_use(&mut self, block: usize, var: u32) {
461        if block < self.uses.len() {
462            self.uses[block].insert(var);
463        }
464    }
465    #[allow(dead_code)]
466    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
467        self.live_in
468            .get(block)
469            .map(|s| s.contains(&var))
470            .unwrap_or(false)
471    }
472    #[allow(dead_code)]
473    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
474        self.live_out
475            .get(block)
476            .map(|s| s.contains(&var))
477            .unwrap_or(false)
478    }
479}
480/// Pass registry for PHPExt.
481#[allow(dead_code)]
482#[derive(Debug, Default)]
483pub struct PHPExtPassRegistry {
484    pub(super) configs: Vec<PHPExtPassConfig>,
485    pub(super) stats: Vec<PHPExtPassStats>,
486}
487impl PHPExtPassRegistry {
488    #[allow(dead_code)]
489    pub fn new() -> Self {
490        Self::default()
491    }
492    #[allow(dead_code)]
493    pub fn register(&mut self, c: PHPExtPassConfig) {
494        self.stats.push(PHPExtPassStats::new());
495        self.configs.push(c);
496    }
497    #[allow(dead_code)]
498    pub fn len(&self) -> usize {
499        self.configs.len()
500    }
501    #[allow(dead_code)]
502    pub fn is_empty(&self) -> bool {
503        self.configs.is_empty()
504    }
505    #[allow(dead_code)]
506    pub fn get(&self, i: usize) -> Option<&PHPExtPassConfig> {
507        self.configs.get(i)
508    }
509    #[allow(dead_code)]
510    pub fn get_stats(&self, i: usize) -> Option<&PHPExtPassStats> {
511        self.stats.get(i)
512    }
513    #[allow(dead_code)]
514    pub fn enabled_passes(&self) -> Vec<&PHPExtPassConfig> {
515        self.configs.iter().filter(|c| c.enabled).collect()
516    }
517    #[allow(dead_code)]
518    pub fn passes_in_phase(&self, ph: &PHPExtPassPhase) -> Vec<&PHPExtPassConfig> {
519        self.configs
520            .iter()
521            .filter(|c| c.enabled && &c.phase == ph)
522            .collect()
523    }
524    #[allow(dead_code)]
525    pub fn total_nodes_visited(&self) -> usize {
526        self.stats.iter().map(|s| s.nodes_visited).sum()
527    }
528    #[allow(dead_code)]
529    pub fn any_changed(&self) -> bool {
530        self.stats.iter().any(|s| s.changed)
531    }
532}
533#[allow(dead_code)]
534#[derive(Debug, Clone)]
535pub struct PHPDominatorTree {
536    pub idom: Vec<Option<u32>>,
537    pub dom_children: Vec<Vec<u32>>,
538    pub dom_depth: Vec<u32>,
539}
540impl PHPDominatorTree {
541    #[allow(dead_code)]
542    pub fn new(size: usize) -> Self {
543        PHPDominatorTree {
544            idom: vec![None; size],
545            dom_children: vec![Vec::new(); size],
546            dom_depth: vec![0; size],
547        }
548    }
549    #[allow(dead_code)]
550    pub fn set_idom(&mut self, node: usize, idom: u32) {
551        self.idom[node] = Some(idom);
552    }
553    #[allow(dead_code)]
554    pub fn dominates(&self, a: usize, b: usize) -> bool {
555        if a == b {
556            return true;
557        }
558        let mut cur = b;
559        loop {
560            match self.idom[cur] {
561                Some(parent) if parent as usize == a => return true,
562                Some(parent) if parent as usize == cur => return false,
563                Some(parent) => cur = parent as usize,
564                None => return false,
565            }
566        }
567    }
568    #[allow(dead_code)]
569    pub fn depth(&self, node: usize) -> u32 {
570        self.dom_depth.get(node).copied().unwrap_or(0)
571    }
572}
573/// Liveness analysis for PHPExt.
574#[allow(dead_code)]
575#[derive(Debug, Clone, Default)]
576pub struct PHPExtLiveness {
577    pub live_in: Vec<Vec<usize>>,
578    pub live_out: Vec<Vec<usize>>,
579    pub defs: Vec<Vec<usize>>,
580    pub uses: Vec<Vec<usize>>,
581}
582impl PHPExtLiveness {
583    #[allow(dead_code)]
584    pub fn new(n: usize) -> Self {
585        Self {
586            live_in: vec![Vec::new(); n],
587            live_out: vec![Vec::new(); n],
588            defs: vec![Vec::new(); n],
589            uses: vec![Vec::new(); n],
590        }
591    }
592    #[allow(dead_code)]
593    pub fn live_in(&self, b: usize, v: usize) -> bool {
594        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
595    }
596    #[allow(dead_code)]
597    pub fn live_out(&self, b: usize, v: usize) -> bool {
598        self.live_out
599            .get(b)
600            .map(|s| s.contains(&v))
601            .unwrap_or(false)
602    }
603    #[allow(dead_code)]
604    pub fn add_def(&mut self, b: usize, v: usize) {
605        if let Some(s) = self.defs.get_mut(b) {
606            if !s.contains(&v) {
607                s.push(v);
608            }
609        }
610    }
611    #[allow(dead_code)]
612    pub fn add_use(&mut self, b: usize, v: usize) {
613        if let Some(s) = self.uses.get_mut(b) {
614            if !s.contains(&v) {
615                s.push(v);
616            }
617        }
618    }
619    #[allow(dead_code)]
620    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
621        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
622    }
623    #[allow(dead_code)]
624    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
625        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
626    }
627}
628/// Constant folding helper for PHPExt.
629#[allow(dead_code)]
630#[derive(Debug, Clone, Default)]
631pub struct PHPExtConstFolder {
632    pub(super) folds: usize,
633    pub(super) failures: usize,
634    pub(super) enabled: bool,
635}
636impl PHPExtConstFolder {
637    #[allow(dead_code)]
638    pub fn new() -> Self {
639        Self {
640            folds: 0,
641            failures: 0,
642            enabled: true,
643        }
644    }
645    #[allow(dead_code)]
646    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
647        self.folds += 1;
648        a.checked_add(b)
649    }
650    #[allow(dead_code)]
651    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
652        self.folds += 1;
653        a.checked_sub(b)
654    }
655    #[allow(dead_code)]
656    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
657        self.folds += 1;
658        a.checked_mul(b)
659    }
660    #[allow(dead_code)]
661    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
662        if b == 0 {
663            self.failures += 1;
664            None
665        } else {
666            self.folds += 1;
667            a.checked_div(b)
668        }
669    }
670    #[allow(dead_code)]
671    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
672        if b == 0 {
673            self.failures += 1;
674            None
675        } else {
676            self.folds += 1;
677            a.checked_rem(b)
678        }
679    }
680    #[allow(dead_code)]
681    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
682        self.folds += 1;
683        a.checked_neg()
684    }
685    #[allow(dead_code)]
686    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
687        if s >= 64 {
688            self.failures += 1;
689            None
690        } else {
691            self.folds += 1;
692            a.checked_shl(s)
693        }
694    }
695    #[allow(dead_code)]
696    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
697        if s >= 64 {
698            self.failures += 1;
699            None
700        } else {
701            self.folds += 1;
702            a.checked_shr(s)
703        }
704    }
705    #[allow(dead_code)]
706    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
707        self.folds += 1;
708        a & b
709    }
710    #[allow(dead_code)]
711    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
712        self.folds += 1;
713        a | b
714    }
715    #[allow(dead_code)]
716    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
717        self.folds += 1;
718        a ^ b
719    }
720    #[allow(dead_code)]
721    pub fn not_i64(&mut self, a: i64) -> i64 {
722        self.folds += 1;
723        !a
724    }
725    #[allow(dead_code)]
726    pub fn fold_count(&self) -> usize {
727        self.folds
728    }
729    #[allow(dead_code)]
730    pub fn failure_count(&self) -> usize {
731        self.failures
732    }
733    #[allow(dead_code)]
734    pub fn enable(&mut self) {
735        self.enabled = true;
736    }
737    #[allow(dead_code)]
738    pub fn disable(&mut self) {
739        self.enabled = false;
740    }
741    #[allow(dead_code)]
742    pub fn is_enabled(&self) -> bool {
743        self.enabled
744    }
745}
746/// A PHP top-level function or method.
747#[derive(Debug, Clone, PartialEq)]
748pub struct PHPFunction {
749    /// Function name
750    pub name: std::string::String,
751    /// Parameters
752    pub params: Vec<PHPParam>,
753    /// Optional return type
754    pub return_type: Option<PHPType>,
755    /// Body lines (raw PHP code)
756    pub body: Vec<std::string::String>,
757    /// Whether this is a static method
758    pub is_static: bool,
759    /// Whether this is abstract
760    pub is_abstract: bool,
761    /// Visibility (for methods)
762    pub visibility: Option<PHPVisibility>,
763    /// Docblock comment
764    pub doc_comment: Option<std::string::String>,
765}
766impl PHPFunction {
767    /// Create a simple function with a name and body.
768    pub fn new(name: impl Into<std::string::String>, body: Vec<std::string::String>) -> Self {
769        PHPFunction {
770            name: name.into(),
771            params: vec![],
772            return_type: None,
773            body,
774            is_static: false,
775            is_abstract: false,
776            visibility: None,
777            doc_comment: None,
778        }
779    }
780    /// Create a method with visibility.
781    pub fn method(
782        name: impl Into<std::string::String>,
783        visibility: PHPVisibility,
784        params: Vec<PHPParam>,
785        return_type: Option<PHPType>,
786        body: Vec<std::string::String>,
787    ) -> Self {
788        PHPFunction {
789            name: name.into(),
790            params,
791            return_type,
792            body,
793            is_static: false,
794            is_abstract: false,
795            visibility: Some(visibility),
796            doc_comment: None,
797        }
798    }
799}
800/// PHP member visibility modifier.
801#[derive(Debug, Clone, PartialEq, Eq)]
802pub enum PHPVisibility {
803    Public,
804    Protected,
805    Private,
806}
807/// PHP 8.1 backed enum variant.
808#[derive(Debug, Clone, PartialEq)]
809pub struct PHPEnumCase {
810    /// Variant name
811    pub name: std::string::String,
812    /// Backing value for backed enums
813    pub value: Option<std::string::String>,
814}
815/// PHP type for type hints and declarations.
816#[derive(Debug, Clone, PartialEq, Eq, Hash)]
817pub enum PHPType {
818    /// `int`
819    Int,
820    /// `float`
821    Float,
822    /// `string`
823    String,
824    /// `bool`
825    Bool,
826    /// `array`
827    Array,
828    /// `null`
829    Null,
830    /// `mixed`
831    Mixed,
832    /// `callable`
833    Callable,
834    /// `void`
835    Void,
836    /// `never`
837    Never,
838    /// `object`
839    Object,
840    /// `iterable`
841    Iterable,
842    /// `?T` (nullable type)
843    Nullable(Box<PHPType>),
844    /// `T1|T2|...` (union type)
845    Union(Vec<PHPType>),
846    /// `T1&T2&...` (intersection type, PHP 8.1+)
847    Intersection(Vec<PHPType>),
848    /// Named class/interface type
849    Named(std::string::String),
850    /// `self`
851    Self_,
852    /// `static`
853    Static,
854    /// `parent`
855    Parent,
856}
857#[allow(dead_code)]
858#[derive(Debug, Clone)]
859pub struct PHPCacheEntry {
860    pub key: String,
861    pub data: Vec<u8>,
862    pub timestamp: u64,
863    pub valid: bool,
864}
865/// Pass execution phase for PHPExt.
866#[allow(dead_code)]
867#[derive(Debug, Clone, PartialEq, Eq, Hash)]
868pub enum PHPExtPassPhase {
869    Early,
870    Middle,
871    Late,
872    Finalize,
873}
874impl PHPExtPassPhase {
875    #[allow(dead_code)]
876    pub fn is_early(&self) -> bool {
877        matches!(self, Self::Early)
878    }
879    #[allow(dead_code)]
880    pub fn is_middle(&self) -> bool {
881        matches!(self, Self::Middle)
882    }
883    #[allow(dead_code)]
884    pub fn is_late(&self) -> bool {
885        matches!(self, Self::Late)
886    }
887    #[allow(dead_code)]
888    pub fn is_finalize(&self) -> bool {
889        matches!(self, Self::Finalize)
890    }
891    #[allow(dead_code)]
892    pub fn order(&self) -> u32 {
893        match self {
894            Self::Early => 0,
895            Self::Middle => 1,
896            Self::Late => 2,
897            Self::Finalize => 3,
898        }
899    }
900    #[allow(dead_code)]
901    pub fn from_order(n: u32) -> Option<Self> {
902        match n {
903            0 => Some(Self::Early),
904            1 => Some(Self::Middle),
905            2 => Some(Self::Late),
906            3 => Some(Self::Finalize),
907            _ => None,
908        }
909    }
910}
911#[allow(dead_code)]
912#[derive(Debug, Clone, PartialEq)]
913pub enum PHPPassPhase {
914    Analysis,
915    Transformation,
916    Verification,
917    Cleanup,
918}
919impl PHPPassPhase {
920    #[allow(dead_code)]
921    pub fn name(&self) -> &str {
922        match self {
923            PHPPassPhase::Analysis => "analysis",
924            PHPPassPhase::Transformation => "transformation",
925            PHPPassPhase::Verification => "verification",
926            PHPPassPhase::Cleanup => "cleanup",
927        }
928    }
929    #[allow(dead_code)]
930    pub fn is_modifying(&self) -> bool {
931        matches!(self, PHPPassPhase::Transformation | PHPPassPhase::Cleanup)
932    }
933}
934/// Configuration for PHPExt passes.
935#[allow(dead_code)]
936#[derive(Debug, Clone)]
937pub struct PHPExtPassConfig {
938    pub name: String,
939    pub phase: PHPExtPassPhase,
940    pub enabled: bool,
941    pub max_iterations: usize,
942    pub debug: u32,
943    pub timeout_ms: Option<u64>,
944}
945impl PHPExtPassConfig {
946    #[allow(dead_code)]
947    pub fn new(name: impl Into<String>) -> Self {
948        Self {
949            name: name.into(),
950            phase: PHPExtPassPhase::Middle,
951            enabled: true,
952            max_iterations: 100,
953            debug: 0,
954            timeout_ms: None,
955        }
956    }
957    #[allow(dead_code)]
958    pub fn with_phase(mut self, phase: PHPExtPassPhase) -> Self {
959        self.phase = phase;
960        self
961    }
962    #[allow(dead_code)]
963    pub fn with_max_iter(mut self, n: usize) -> Self {
964        self.max_iterations = n;
965        self
966    }
967    #[allow(dead_code)]
968    pub fn with_debug(mut self, d: u32) -> Self {
969        self.debug = d;
970        self
971    }
972    #[allow(dead_code)]
973    pub fn disabled(mut self) -> Self {
974        self.enabled = false;
975        self
976    }
977    #[allow(dead_code)]
978    pub fn with_timeout(mut self, ms: u64) -> Self {
979        self.timeout_ms = Some(ms);
980        self
981    }
982    #[allow(dead_code)]
983    pub fn is_debug_enabled(&self) -> bool {
984        self.debug > 0
985    }
986}
987/// Analysis cache for PHPExt.
988#[allow(dead_code)]
989#[derive(Debug)]
990pub struct PHPExtCache {
991    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
992    pub(super) cap: usize,
993    pub(super) total_hits: u64,
994    pub(super) total_misses: u64,
995}
996impl PHPExtCache {
997    #[allow(dead_code)]
998    pub fn new(cap: usize) -> Self {
999        Self {
1000            entries: Vec::new(),
1001            cap,
1002            total_hits: 0,
1003            total_misses: 0,
1004        }
1005    }
1006    #[allow(dead_code)]
1007    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1008        for e in self.entries.iter_mut() {
1009            if e.0 == key && e.2 {
1010                e.3 += 1;
1011                self.total_hits += 1;
1012                return Some(&e.1);
1013            }
1014        }
1015        self.total_misses += 1;
1016        None
1017    }
1018    #[allow(dead_code)]
1019    pub fn put(&mut self, key: u64, data: Vec<u8>) {
1020        if self.entries.len() >= self.cap {
1021            self.entries.retain(|e| e.2);
1022            if self.entries.len() >= self.cap {
1023                self.entries.remove(0);
1024            }
1025        }
1026        self.entries.push((key, data, true, 0));
1027    }
1028    #[allow(dead_code)]
1029    pub fn invalidate(&mut self) {
1030        for e in self.entries.iter_mut() {
1031            e.2 = false;
1032        }
1033    }
1034    #[allow(dead_code)]
1035    pub fn hit_rate(&self) -> f64 {
1036        let t = self.total_hits + self.total_misses;
1037        if t == 0 {
1038            0.0
1039        } else {
1040            self.total_hits as f64 / t as f64
1041        }
1042    }
1043    #[allow(dead_code)]
1044    pub fn live_count(&self) -> usize {
1045        self.entries.iter().filter(|e| e.2).count()
1046    }
1047}
1048/// PHP 8.x code generation backend for OxiLean.
1049pub struct PHPBackend {
1050    /// Indent string (default: 4 spaces)
1051    pub(super) indent: std::string::String,
1052    /// Name mangling table
1053    pub(super) mangle_cache: HashMap<std::string::String, std::string::String>,
1054    /// Whether to emit docblocks
1055    pub(super) emit_docs: bool,
1056}
1057impl PHPBackend {
1058    /// Create a new PHPBackend with default settings.
1059    pub fn new() -> Self {
1060        PHPBackend {
1061            indent: "    ".to_string(),
1062            mangle_cache: HashMap::new(),
1063            emit_docs: true,
1064        }
1065    }
1066    /// Create a PHPBackend with a custom indent string.
1067    pub fn with_indent(indent: impl Into<std::string::String>) -> Self {
1068        PHPBackend {
1069            indent: indent.into(),
1070            mangle_cache: HashMap::new(),
1071            emit_docs: true,
1072        }
1073    }
1074    /// Emit a PHP type hint as a string.
1075    pub fn emit_type(&self, ty: &PHPType) -> std::string::String {
1076        format!("{}", ty)
1077    }
1078    /// Emit a PHP expression as a string.
1079    pub fn emit_expr(&self, expr: &PHPExpr) -> std::string::String {
1080        format!("{}", expr)
1081    }
1082    /// Mangle an OxiLean name to a valid PHP identifier.
1083    ///
1084    /// PHP identifiers must match `[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*`.
1085    pub fn mangle_name(&self, name: &str) -> std::string::String {
1086        if let Some(cached) = self.mangle_cache.get(name) {
1087            return cached.clone();
1088        }
1089        let mut result = std::string::String::new();
1090        let mut first = true;
1091        for c in name.chars() {
1092            if first {
1093                if c.is_alphabetic() || c == '_' {
1094                    result.push(c);
1095                } else {
1096                    result.push('_');
1097                    if c.is_alphanumeric() {
1098                        result.push(c);
1099                    }
1100                }
1101                first = false;
1102            } else {
1103                match c {
1104                    'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => result.push(c),
1105                    '.' | ':' | '\'' => result.push('_'),
1106                    '-' => result.push('_'),
1107                    _ => {
1108                        let code = c as u32;
1109                        result.push_str(&format!("_u{:04X}_", code));
1110                    }
1111                }
1112            }
1113        }
1114        let reserved = [
1115            "abstract",
1116            "and",
1117            "array",
1118            "as",
1119            "break",
1120            "callable",
1121            "case",
1122            "catch",
1123            "class",
1124            "clone",
1125            "const",
1126            "continue",
1127            "declare",
1128            "default",
1129            "die",
1130            "do",
1131            "echo",
1132            "else",
1133            "elseif",
1134            "empty",
1135            "enddeclare",
1136            "endfor",
1137            "endforeach",
1138            "endif",
1139            "endswitch",
1140            "endwhile",
1141            "enum",
1142            "eval",
1143            "exit",
1144            "extends",
1145            "final",
1146            "finally",
1147            "fn",
1148            "for",
1149            "foreach",
1150            "function",
1151            "global",
1152            "goto",
1153            "if",
1154            "implements",
1155            "include",
1156            "include_once",
1157            "instanceof",
1158            "insteadof",
1159            "interface",
1160            "isset",
1161            "list",
1162            "match",
1163            "namespace",
1164            "new",
1165            "null",
1166            "or",
1167            "print",
1168            "private",
1169            "protected",
1170            "public",
1171            "readonly",
1172            "require",
1173            "require_once",
1174            "return",
1175            "static",
1176            "switch",
1177            "throw",
1178            "trait",
1179            "try",
1180            "unset",
1181            "use",
1182            "var",
1183            "while",
1184            "xor",
1185            "yield",
1186        ];
1187        if reserved.contains(&result.as_str()) {
1188            result.push_str("_ox");
1189        }
1190        result
1191    }
1192    /// Emit a parameter declaration.
1193    pub(super) fn emit_param(&self, param: &PHPParam) -> std::string::String {
1194        format_param(param)
1195    }
1196    /// Emit a PHP function (top-level or standalone).
1197    pub fn emit_function(&self, func: &PHPFunction) -> std::string::String {
1198        let mut out = std::string::String::new();
1199        if self.emit_docs {
1200            if let Some(doc) = &func.doc_comment {
1201                out.push_str("/**\n");
1202                for line in doc.lines() {
1203                    out.push_str(&format!(" * {}\n", line));
1204                }
1205                out.push_str(" */\n");
1206            }
1207        }
1208        if let Some(vis) = &func.visibility {
1209            out.push_str(&format!("{} ", vis));
1210        }
1211        if func.is_static {
1212            out.push_str("static ");
1213        }
1214        if func.is_abstract {
1215            out.push_str("abstract ");
1216        }
1217        let params_s: Vec<std::string::String> =
1218            func.params.iter().map(|p| self.emit_param(p)).collect();
1219        out.push_str(&format!("function {}({})", func.name, params_s.join(", ")));
1220        if let Some(ret) = &func.return_type {
1221            out.push_str(&format!(": {}", ret));
1222        }
1223        if func.is_abstract {
1224            out.push_str(";\n");
1225        } else {
1226            out.push_str("\n{\n");
1227            for line in &func.body {
1228                out.push_str(&format!("{}{}\n", self.indent, line));
1229            }
1230            out.push_str("}\n");
1231        }
1232        out
1233    }
1234    /// Emit a PHP property declaration.
1235    pub(super) fn emit_property(&self, prop: &PHPProperty) -> std::string::String {
1236        let mut s = format!("{} ", prop.visibility);
1237        if prop.is_static {
1238            s.push_str("static ");
1239        }
1240        if prop.readonly {
1241            s.push_str("readonly ");
1242        }
1243        if let Some(ty) = &prop.ty {
1244            s.push_str(&format!("{} ", ty));
1245        }
1246        s.push_str(&format!("${}", prop.name));
1247        if let Some(default) = &prop.default {
1248            s.push_str(&format!(" = {}", default));
1249        }
1250        s.push(';');
1251        s
1252    }
1253    /// Emit a PHP interface declaration.
1254    pub fn emit_interface(&self, iface: &PHPInterface) -> std::string::String {
1255        let mut out = std::string::String::new();
1256        out.push_str(&format!("interface {}", iface.name));
1257        if !iface.extends.is_empty() {
1258            out.push_str(&format!(" extends {}", iface.extends.join(", ")));
1259        }
1260        out.push_str("\n{\n");
1261        for (name, val) in &iface.constants {
1262            out.push_str(&format!("{}const {} = {};\n", self.indent, name, val));
1263        }
1264        for method in &iface.methods {
1265            out.push_str(&self.indent_block(&self.emit_function(method)));
1266        }
1267        out.push_str("}\n");
1268        out
1269    }
1270    /// Emit a PHP trait declaration.
1271    pub fn emit_trait(&self, tr: &PHPTrait) -> std::string::String {
1272        let mut out = std::string::String::new();
1273        out.push_str(&format!("trait {}\n{{\n", tr.name));
1274        for prop in &tr.properties {
1275            out.push_str(&format!("{}{}\n", self.indent, self.emit_property(prop)));
1276        }
1277        for method in &tr.methods {
1278            out.push_str(&self.indent_block(&self.emit_function(method)));
1279        }
1280        out.push_str("}\n");
1281        out
1282    }
1283    /// Emit a PHP 8.1 enum declaration.
1284    pub fn emit_enum(&self, en: &PHPEnum) -> std::string::String {
1285        let mut out = std::string::String::new();
1286        out.push_str(&format!("enum {}", en.name));
1287        if let Some(bt) = &en.backing_type {
1288            out.push_str(&format!(": {}", bt));
1289        }
1290        if !en.implements.is_empty() {
1291            out.push_str(&format!(" implements {}", en.implements.join(", ")));
1292        }
1293        out.push_str("\n{\n");
1294        for case in &en.cases {
1295            if let Some(val) = &case.value {
1296                out.push_str(&format!("{}case {} = {};\n", self.indent, case.name, val));
1297            } else {
1298                out.push_str(&format!("{}case {};\n", self.indent, case.name));
1299            }
1300        }
1301        for method in &en.methods {
1302            out.push_str(&self.indent_block(&self.emit_function(method)));
1303        }
1304        out.push_str("}\n");
1305        out
1306    }
1307    /// Emit a PHP class declaration.
1308    pub fn emit_class(&self, class: &PHPClass) -> std::string::String {
1309        let mut out = std::string::String::new();
1310        if class.is_abstract {
1311            out.push_str("abstract ");
1312        }
1313        if class.is_final {
1314            out.push_str("final ");
1315        }
1316        if class.is_readonly {
1317            out.push_str("readonly ");
1318        }
1319        out.push_str(&format!("class {}", class.name));
1320        if let Some(parent) = &class.parent {
1321            out.push_str(&format!(" extends {}", parent));
1322        }
1323        if !class.interfaces.is_empty() {
1324            out.push_str(&format!(" implements {}", class.interfaces.join(", ")));
1325        }
1326        out.push_str("\n{\n");
1327        for tr in &class.traits {
1328            out.push_str(&format!("{}use {};\n", self.indent, tr));
1329        }
1330        if !class.traits.is_empty() {
1331            out.push('\n');
1332        }
1333        for (name, ty, val) in &class.constants {
1334            out.push_str(&format!(
1335                "{}const {}: {} = {};\n",
1336                self.indent, name, ty, val
1337            ));
1338        }
1339        for prop in &class.properties {
1340            out.push_str(&format!("{}{}\n", self.indent, self.emit_property(prop)));
1341        }
1342        if !class.properties.is_empty() {
1343            out.push('\n');
1344        }
1345        for method in &class.methods {
1346            out.push_str(&self.indent_block(&self.emit_function(method)));
1347            out.push('\n');
1348        }
1349        out.push_str("}\n");
1350        out
1351    }
1352    /// Emit a complete PHP script.
1353    pub fn emit_script(&self, script: &PHPScript) -> std::string::String {
1354        let mut out = std::string::String::from("<?php\n");
1355        if script.strict_types {
1356            out.push_str("declare(strict_types=1);\n\n");
1357        }
1358        if let Some(ns) = &script.namespace {
1359            out.push_str(&format!("namespace {};\n\n", ns));
1360        }
1361        for (path, alias) in &script.uses {
1362            match alias {
1363                Some(a) => out.push_str(&format!("use {} as {};\n", path, a)),
1364                None => out.push_str(&format!("use {};\n", path)),
1365            }
1366        }
1367        if !script.uses.is_empty() {
1368            out.push('\n');
1369        }
1370        for iface in &script.interfaces {
1371            out.push_str(&self.emit_interface(iface));
1372            out.push('\n');
1373        }
1374        for tr in &script.traits {
1375            out.push_str(&self.emit_trait(tr));
1376            out.push('\n');
1377        }
1378        for en in &script.enums {
1379            out.push_str(&self.emit_enum(en));
1380            out.push('\n');
1381        }
1382        for class in &script.classes {
1383            out.push_str(&self.emit_class(class));
1384            out.push('\n');
1385        }
1386        for func in &script.functions {
1387            out.push_str(&self.emit_function(func));
1388            out.push('\n');
1389        }
1390        for line in &script.main {
1391            out.push_str(line);
1392            out.push('\n');
1393        }
1394        out
1395    }
1396    /// Indent each line of a block by one level.
1397    pub(super) fn indent_block(&self, block: &str) -> std::string::String {
1398        block
1399            .lines()
1400            .map(|line| {
1401                if line.trim().is_empty() {
1402                    std::string::String::new()
1403                } else {
1404                    format!("{}{}", self.indent, line)
1405                }
1406            })
1407            .collect::<Vec<_>>()
1408            .join("\n")
1409            + "\n"
1410    }
1411    /// Emit a namespace block.
1412    pub fn emit_namespace(&self, ns: &PHPNamespace) -> std::string::String {
1413        let mut script = PHPScript::new();
1414        script.namespace = Some(ns.path.clone());
1415        script.uses = ns.uses.clone();
1416        script.functions = ns.functions.clone();
1417        script.classes = ns.classes.clone();
1418        script.interfaces = ns.interfaces.clone();
1419        script.traits = ns.traits.clone();
1420        script.enums = ns.enums.clone();
1421        self.emit_script(&script)
1422    }
1423}
1424/// Dependency graph for PHPExt.
1425#[allow(dead_code)]
1426#[derive(Debug, Clone)]
1427pub struct PHPExtDepGraph {
1428    pub(super) n: usize,
1429    pub(super) adj: Vec<Vec<usize>>,
1430    pub(super) rev: Vec<Vec<usize>>,
1431    pub(super) edge_count: usize,
1432}
1433impl PHPExtDepGraph {
1434    #[allow(dead_code)]
1435    pub fn new(n: usize) -> Self {
1436        Self {
1437            n,
1438            adj: vec![Vec::new(); n],
1439            rev: vec![Vec::new(); n],
1440            edge_count: 0,
1441        }
1442    }
1443    #[allow(dead_code)]
1444    pub fn add_edge(&mut self, from: usize, to: usize) {
1445        if from < self.n && to < self.n {
1446            if !self.adj[from].contains(&to) {
1447                self.adj[from].push(to);
1448                self.rev[to].push(from);
1449                self.edge_count += 1;
1450            }
1451        }
1452    }
1453    #[allow(dead_code)]
1454    pub fn succs(&self, n: usize) -> &[usize] {
1455        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1456    }
1457    #[allow(dead_code)]
1458    pub fn preds(&self, n: usize) -> &[usize] {
1459        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1460    }
1461    #[allow(dead_code)]
1462    pub fn topo_sort(&self) -> Option<Vec<usize>> {
1463        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
1464        let mut q: std::collections::VecDeque<usize> =
1465            (0..self.n).filter(|&i| deg[i] == 0).collect();
1466        let mut out = Vec::with_capacity(self.n);
1467        while let Some(u) = q.pop_front() {
1468            out.push(u);
1469            for &v in &self.adj[u] {
1470                deg[v] -= 1;
1471                if deg[v] == 0 {
1472                    q.push_back(v);
1473                }
1474            }
1475        }
1476        if out.len() == self.n {
1477            Some(out)
1478        } else {
1479            None
1480        }
1481    }
1482    #[allow(dead_code)]
1483    pub fn has_cycle(&self) -> bool {
1484        self.topo_sort().is_none()
1485    }
1486    #[allow(dead_code)]
1487    pub fn reachable(&self, start: usize) -> Vec<usize> {
1488        let mut vis = vec![false; self.n];
1489        let mut stk = vec![start];
1490        let mut out = Vec::new();
1491        while let Some(u) = stk.pop() {
1492            if u < self.n && !vis[u] {
1493                vis[u] = true;
1494                out.push(u);
1495                for &v in &self.adj[u] {
1496                    if !vis[v] {
1497                        stk.push(v);
1498                    }
1499                }
1500            }
1501        }
1502        out
1503    }
1504    #[allow(dead_code)]
1505    pub fn scc(&self) -> Vec<Vec<usize>> {
1506        let mut visited = vec![false; self.n];
1507        let mut order = Vec::new();
1508        for i in 0..self.n {
1509            if !visited[i] {
1510                let mut stk = vec![(i, 0usize)];
1511                while let Some((u, idx)) = stk.last_mut() {
1512                    if !visited[*u] {
1513                        visited[*u] = true;
1514                    }
1515                    if *idx < self.adj[*u].len() {
1516                        let v = self.adj[*u][*idx];
1517                        *idx += 1;
1518                        if !visited[v] {
1519                            stk.push((v, 0));
1520                        }
1521                    } else {
1522                        order.push(*u);
1523                        stk.pop();
1524                    }
1525                }
1526            }
1527        }
1528        let mut comp = vec![usize::MAX; self.n];
1529        let mut components: Vec<Vec<usize>> = Vec::new();
1530        for &start in order.iter().rev() {
1531            if comp[start] == usize::MAX {
1532                let cid = components.len();
1533                let mut component = Vec::new();
1534                let mut stk = vec![start];
1535                while let Some(u) = stk.pop() {
1536                    if comp[u] == usize::MAX {
1537                        comp[u] = cid;
1538                        component.push(u);
1539                        for &v in &self.rev[u] {
1540                            if comp[v] == usize::MAX {
1541                                stk.push(v);
1542                            }
1543                        }
1544                    }
1545                }
1546                components.push(component);
1547            }
1548        }
1549        components
1550    }
1551    #[allow(dead_code)]
1552    pub fn node_count(&self) -> usize {
1553        self.n
1554    }
1555    #[allow(dead_code)]
1556    pub fn edge_count(&self) -> usize {
1557        self.edge_count
1558    }
1559}
1560#[allow(dead_code)]
1561#[derive(Debug, Clone, Default)]
1562pub struct PHPPassStats {
1563    pub total_runs: u32,
1564    pub successful_runs: u32,
1565    pub total_changes: u64,
1566    pub time_ms: u64,
1567    pub iterations_used: u32,
1568}
1569impl PHPPassStats {
1570    #[allow(dead_code)]
1571    pub fn new() -> Self {
1572        Self::default()
1573    }
1574    #[allow(dead_code)]
1575    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1576        self.total_runs += 1;
1577        self.successful_runs += 1;
1578        self.total_changes += changes;
1579        self.time_ms += time_ms;
1580        self.iterations_used = iterations;
1581    }
1582    #[allow(dead_code)]
1583    pub fn average_changes_per_run(&self) -> f64 {
1584        if self.total_runs == 0 {
1585            return 0.0;
1586        }
1587        self.total_changes as f64 / self.total_runs as f64
1588    }
1589    #[allow(dead_code)]
1590    pub fn success_rate(&self) -> f64 {
1591        if self.total_runs == 0 {
1592            return 0.0;
1593        }
1594        self.successful_runs as f64 / self.total_runs as f64
1595    }
1596    #[allow(dead_code)]
1597    pub fn format_summary(&self) -> String {
1598        format!(
1599            "Runs: {}/{}, Changes: {}, Time: {}ms",
1600            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1601        )
1602    }
1603}
1604/// Dominator tree for PHPExt.
1605#[allow(dead_code)]
1606#[derive(Debug, Clone)]
1607pub struct PHPExtDomTree {
1608    pub(super) idom: Vec<Option<usize>>,
1609    pub(super) children: Vec<Vec<usize>>,
1610    pub(super) depth: Vec<usize>,
1611}
1612impl PHPExtDomTree {
1613    #[allow(dead_code)]
1614    pub fn new(n: usize) -> Self {
1615        Self {
1616            idom: vec![None; n],
1617            children: vec![Vec::new(); n],
1618            depth: vec![0; n],
1619        }
1620    }
1621    #[allow(dead_code)]
1622    pub fn set_idom(&mut self, node: usize, dom: usize) {
1623        if node < self.idom.len() {
1624            self.idom[node] = Some(dom);
1625            if dom < self.children.len() {
1626                self.children[dom].push(node);
1627            }
1628            self.depth[node] = if dom < self.depth.len() {
1629                self.depth[dom] + 1
1630            } else {
1631                1
1632            };
1633        }
1634    }
1635    #[allow(dead_code)]
1636    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1637        if a == b {
1638            return true;
1639        }
1640        let n = self.idom.len();
1641        for _ in 0..n {
1642            match self.idom.get(b).copied().flatten() {
1643                None => return false,
1644                Some(p) if p == a => return true,
1645                Some(p) if p == b => return false,
1646                Some(p) => b = p,
1647            }
1648        }
1649        false
1650    }
1651    #[allow(dead_code)]
1652    pub fn children_of(&self, n: usize) -> &[usize] {
1653        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1654    }
1655    #[allow(dead_code)]
1656    pub fn depth_of(&self, n: usize) -> usize {
1657        self.depth.get(n).copied().unwrap_or(0)
1658    }
1659    #[allow(dead_code)]
1660    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1661        let n = self.idom.len();
1662        for _ in 0..(2 * n) {
1663            if a == b {
1664                return a;
1665            }
1666            if self.depth_of(a) > self.depth_of(b) {
1667                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1668            } else {
1669                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1670            }
1671        }
1672        0
1673    }
1674}
1675/// PHP 8.1 enum declaration.
1676#[derive(Debug, Clone, PartialEq)]
1677pub struct PHPEnum {
1678    /// Enum name
1679    pub name: std::string::String,
1680    /// Backing type (int or string), `None` for pure enums
1681    pub backing_type: Option<PHPType>,
1682    /// Cases
1683    pub cases: Vec<PHPEnumCase>,
1684    /// Implemented interfaces
1685    pub implements: Vec<std::string::String>,
1686    /// Methods
1687    pub methods: Vec<PHPFunction>,
1688}
1689impl PHPEnum {
1690    /// Create a new pure enum.
1691    pub fn new(name: impl Into<std::string::String>) -> Self {
1692        PHPEnum {
1693            name: name.into(),
1694            backing_type: None,
1695            cases: vec![],
1696            implements: vec![],
1697            methods: vec![],
1698        }
1699    }
1700    /// Create a string-backed enum.
1701    pub fn string_backed(name: impl Into<std::string::String>) -> Self {
1702        PHPEnum {
1703            name: name.into(),
1704            backing_type: Some(PHPType::String),
1705            cases: vec![],
1706            implements: vec![],
1707            methods: vec![],
1708        }
1709    }
1710}
1711#[allow(dead_code)]
1712#[derive(Debug, Clone)]
1713pub struct PHPPassConfig {
1714    pub phase: PHPPassPhase,
1715    pub enabled: bool,
1716    pub max_iterations: u32,
1717    pub debug_output: bool,
1718    pub pass_name: String,
1719}
1720impl PHPPassConfig {
1721    #[allow(dead_code)]
1722    pub fn new(name: impl Into<String>, phase: PHPPassPhase) -> Self {
1723        PHPPassConfig {
1724            phase,
1725            enabled: true,
1726            max_iterations: 10,
1727            debug_output: false,
1728            pass_name: name.into(),
1729        }
1730    }
1731    #[allow(dead_code)]
1732    pub fn disabled(mut self) -> Self {
1733        self.enabled = false;
1734        self
1735    }
1736    #[allow(dead_code)]
1737    pub fn with_debug(mut self) -> Self {
1738        self.debug_output = true;
1739        self
1740    }
1741    #[allow(dead_code)]
1742    pub fn max_iter(mut self, n: u32) -> Self {
1743        self.max_iterations = n;
1744        self
1745    }
1746}
1747#[allow(dead_code)]
1748pub struct PHPConstantFoldingHelper;
1749impl PHPConstantFoldingHelper {
1750    #[allow(dead_code)]
1751    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1752        a.checked_add(b)
1753    }
1754    #[allow(dead_code)]
1755    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1756        a.checked_sub(b)
1757    }
1758    #[allow(dead_code)]
1759    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1760        a.checked_mul(b)
1761    }
1762    #[allow(dead_code)]
1763    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1764        if b == 0 {
1765            None
1766        } else {
1767            a.checked_div(b)
1768        }
1769    }
1770    #[allow(dead_code)]
1771    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1772        a + b
1773    }
1774    #[allow(dead_code)]
1775    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1776        a * b
1777    }
1778    #[allow(dead_code)]
1779    pub fn fold_neg_i64(a: i64) -> Option<i64> {
1780        a.checked_neg()
1781    }
1782    #[allow(dead_code)]
1783    pub fn fold_not_bool(a: bool) -> bool {
1784        !a
1785    }
1786    #[allow(dead_code)]
1787    pub fn fold_and_bool(a: bool, b: bool) -> bool {
1788        a && b
1789    }
1790    #[allow(dead_code)]
1791    pub fn fold_or_bool(a: bool, b: bool) -> bool {
1792        a || b
1793    }
1794    #[allow(dead_code)]
1795    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1796        a.checked_shl(b)
1797    }
1798    #[allow(dead_code)]
1799    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1800        a.checked_shr(b)
1801    }
1802    #[allow(dead_code)]
1803    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1804        if b == 0 {
1805            None
1806        } else {
1807            Some(a % b)
1808        }
1809    }
1810    #[allow(dead_code)]
1811    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1812        a & b
1813    }
1814    #[allow(dead_code)]
1815    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1816        a | b
1817    }
1818    #[allow(dead_code)]
1819    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1820        a ^ b
1821    }
1822    #[allow(dead_code)]
1823    pub fn fold_bitnot_i64(a: i64) -> i64 {
1824        !a
1825    }
1826}
1827/// PHP expression AST node.
1828#[derive(Debug, Clone, PartialEq)]
1829pub enum PHPExpr {
1830    /// Literal value (int, float, string, bool, null)
1831    Lit(std::string::String),
1832    /// Variable reference: `$name`
1833    Var(std::string::String),
1834    /// Binary operation: `lhs op rhs`
1835    BinOp(Box<PHPExpr>, std::string::String, Box<PHPExpr>),
1836    /// Unary operation: `op operand`
1837    UnaryOp(std::string::String, Box<PHPExpr>),
1838    /// Function/method call: `name(args...)`
1839    FuncCall(std::string::String, Vec<PHPExpr>),
1840    /// Array literal: `[expr, ...]`
1841    ArrayLit(Vec<PHPExpr>),
1842    /// Associative array literal: `[key => val, ...]`
1843    ArrayMap(Vec<(PHPExpr, PHPExpr)>),
1844    /// Object instantiation: `new ClassName(args...)`
1845    New(std::string::String, Vec<PHPExpr>),
1846    /// Property access: `$obj->prop`
1847    Arrow(Box<PHPExpr>, std::string::String),
1848    /// Null-safe property access: `$obj?->prop`
1849    NullSafe(Box<PHPExpr>, std::string::String),
1850    /// Static property/method access: `Class::member`
1851    StaticAccess(std::string::String, std::string::String),
1852    /// Array index: `$arr[idx]`
1853    Index(Box<PHPExpr>, Box<PHPExpr>),
1854    /// Ternary: `$cond ? $then : $else`
1855    Ternary(Box<PHPExpr>, Box<PHPExpr>, Box<PHPExpr>),
1856    /// Null coalescing: `$a ?? $b`
1857    NullCoalesce(Box<PHPExpr>, Box<PHPExpr>),
1858    /// Closure / anonymous function
1859    Closure {
1860        params: Vec<PHPParam>,
1861        use_vars: Vec<std::string::String>,
1862        return_type: Option<PHPType>,
1863        body: Vec<std::string::String>,
1864    },
1865    /// Arrow function (PHP 7.4+): `fn($x) => expr`
1866    ArrowFn {
1867        params: Vec<PHPParam>,
1868        return_type: Option<PHPType>,
1869        body: Box<PHPExpr>,
1870    },
1871    /// Match expression (PHP 8.0+)
1872    Match {
1873        subject: Box<PHPExpr>,
1874        arms: Vec<(PHPExpr, PHPExpr)>,
1875        default: Option<Box<PHPExpr>>,
1876    },
1877    /// Named argument: `name: value`
1878    NamedArg(std::string::String, Box<PHPExpr>),
1879    /// Spread operator: `...$arr`
1880    Spread(Box<PHPExpr>),
1881    /// Cast: `(type)$expr`
1882    Cast(std::string::String, Box<PHPExpr>),
1883    /// `isset($var)`
1884    Isset(Box<PHPExpr>),
1885    /// `empty($var)`
1886    Empty(Box<PHPExpr>),
1887}
1888/// A PHP class property.
1889#[derive(Debug, Clone, PartialEq)]
1890pub struct PHPProperty {
1891    /// Property name (without `$`)
1892    pub name: std::string::String,
1893    /// Optional type hint
1894    pub ty: Option<PHPType>,
1895    /// Visibility
1896    pub visibility: PHPVisibility,
1897    /// Whether this is static
1898    pub is_static: bool,
1899    /// Whether this is readonly (PHP 8.1+)
1900    pub readonly: bool,
1901    /// Optional default value expression (as string)
1902    pub default: Option<std::string::String>,
1903}
1904impl PHPProperty {
1905    /// Create a public property.
1906    pub fn public(name: impl Into<std::string::String>, ty: Option<PHPType>) -> Self {
1907        PHPProperty {
1908            name: name.into(),
1909            ty,
1910            visibility: PHPVisibility::Public,
1911            is_static: false,
1912            readonly: false,
1913            default: None,
1914        }
1915    }
1916    /// Create a private property.
1917    pub fn private(name: impl Into<std::string::String>, ty: Option<PHPType>) -> Self {
1918        PHPProperty {
1919            name: name.into(),
1920            ty,
1921            visibility: PHPVisibility::Private,
1922            is_static: false,
1923            readonly: false,
1924            default: None,
1925        }
1926    }
1927}
1928/// A PHP namespace block.
1929#[derive(Debug, Clone, PartialEq)]
1930pub struct PHPNamespace {
1931    /// Namespace path (e.g. `OxiLean\\Runtime`)
1932    pub path: std::string::String,
1933    /// `use` import statements
1934    pub uses: Vec<(std::string::String, Option<std::string::String>)>,
1935    /// Functions in this namespace
1936    pub functions: Vec<PHPFunction>,
1937    /// Classes in this namespace
1938    pub classes: Vec<PHPClass>,
1939    /// Interfaces in this namespace
1940    pub interfaces: Vec<PHPInterface>,
1941    /// Traits in this namespace
1942    pub traits: Vec<PHPTrait>,
1943    /// Enums in this namespace
1944    pub enums: Vec<PHPEnum>,
1945}
1946impl PHPNamespace {
1947    /// Create a new namespace.
1948    pub fn new(path: impl Into<std::string::String>) -> Self {
1949        PHPNamespace {
1950            path: path.into(),
1951            uses: vec![],
1952            functions: vec![],
1953            classes: vec![],
1954            interfaces: vec![],
1955            traits: vec![],
1956            enums: vec![],
1957        }
1958    }
1959}
1960#[allow(dead_code)]
1961#[derive(Debug, Clone)]
1962pub struct PHPDepGraph {
1963    pub(super) nodes: Vec<u32>,
1964    pub(super) edges: Vec<(u32, u32)>,
1965}
1966impl PHPDepGraph {
1967    #[allow(dead_code)]
1968    pub fn new() -> Self {
1969        PHPDepGraph {
1970            nodes: Vec::new(),
1971            edges: Vec::new(),
1972        }
1973    }
1974    #[allow(dead_code)]
1975    pub fn add_node(&mut self, id: u32) {
1976        if !self.nodes.contains(&id) {
1977            self.nodes.push(id);
1978        }
1979    }
1980    #[allow(dead_code)]
1981    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1982        self.add_node(dep);
1983        self.add_node(dependent);
1984        self.edges.push((dep, dependent));
1985    }
1986    #[allow(dead_code)]
1987    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1988        self.edges
1989            .iter()
1990            .filter(|(d, _)| *d == node)
1991            .map(|(_, dep)| *dep)
1992            .collect()
1993    }
1994    #[allow(dead_code)]
1995    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1996        self.edges
1997            .iter()
1998            .filter(|(_, dep)| *dep == node)
1999            .map(|(d, _)| *d)
2000            .collect()
2001    }
2002    #[allow(dead_code)]
2003    pub fn topological_sort(&self) -> Vec<u32> {
2004        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
2005        for &n in &self.nodes {
2006            in_degree.insert(n, 0);
2007        }
2008        for (_, dep) in &self.edges {
2009            *in_degree.entry(*dep).or_insert(0) += 1;
2010        }
2011        let mut queue: std::collections::VecDeque<u32> = self
2012            .nodes
2013            .iter()
2014            .filter(|&&n| in_degree[&n] == 0)
2015            .copied()
2016            .collect();
2017        let mut result = Vec::new();
2018        while let Some(node) = queue.pop_front() {
2019            result.push(node);
2020            for dep in self.dependents_of(node) {
2021                let cnt = in_degree.entry(dep).or_insert(0);
2022                *cnt = cnt.saturating_sub(1);
2023                if *cnt == 0 {
2024                    queue.push_back(dep);
2025                }
2026            }
2027        }
2028        result
2029    }
2030    #[allow(dead_code)]
2031    pub fn has_cycle(&self) -> bool {
2032        self.topological_sort().len() < self.nodes.len()
2033    }
2034}
2035/// PHP interface declaration.
2036#[derive(Debug, Clone, PartialEq)]
2037pub struct PHPInterface {
2038    /// Interface name
2039    pub name: std::string::String,
2040    /// Interfaces this extends
2041    pub extends: Vec<std::string::String>,
2042    /// Method signatures (abstract methods)
2043    pub methods: Vec<PHPFunction>,
2044    /// Constants
2045    pub constants: Vec<(std::string::String, std::string::String)>,
2046}
2047impl PHPInterface {
2048    /// Create a new interface.
2049    pub fn new(name: impl Into<std::string::String>) -> Self {
2050        PHPInterface {
2051            name: name.into(),
2052            extends: vec![],
2053            methods: vec![],
2054            constants: vec![],
2055        }
2056    }
2057}