Skip to main content

php_printer/
printer.rs

1use php_ast::ast::*;
2
3use crate::precedence::*;
4
5/// Configuration for the pretty printer.
6pub struct PrinterConfig {
7    pub indent: Indent,
8    pub newline: String,
9}
10
11/// Indentation style.
12pub enum Indent {
13    Spaces(usize),
14    Tabs,
15}
16
17impl Default for PrinterConfig {
18    fn default() -> Self {
19        Self {
20            indent: Indent::Spaces(4),
21            newline: "\n".to_string(),
22        }
23    }
24}
25
26const MAX_DEPTH: usize = 256;
27
28pub(crate) struct Printer {
29    output: String,
30    indent_level: usize,
31    indent_str: String,
32    nl: String,
33    depth: usize,
34}
35
36impl Printer {
37    pub fn new(config: &PrinterConfig) -> Self {
38        let indent_str = match &config.indent {
39            Indent::Spaces(n) => " ".repeat((*n).min(16)),
40            Indent::Tabs => "\t".to_string(),
41        };
42        Self {
43            output: String::with_capacity(4096),
44            indent_level: 0,
45            indent_str,
46            nl: config.newline.clone(),
47            depth: 0,
48        }
49    }
50
51    pub fn into_output(self) -> String {
52        self.output
53    }
54
55    fn w(&mut self, s: &str) {
56        self.output.push_str(s);
57    }
58
59    fn newline(&mut self) {
60        self.output.push_str(&self.nl);
61    }
62
63    fn write_indent(&mut self) {
64        for _ in 0..self.indent_level {
65            self.output.push_str(&self.indent_str);
66        }
67    }
68
69    fn indent(&mut self) {
70        self.indent_level += 1;
71    }
72
73    fn dedent(&mut self) {
74        self.indent_level = self.indent_level.saturating_sub(1);
75    }
76
77    // =========================================================================
78    // Top-level
79    // =========================================================================
80
81    pub fn print_program(&mut self, program: &Program) {
82        self.print_stmts(&program.stmts, false);
83    }
84
85    fn print_stmts(&mut self, stmts: &[Stmt], indent: bool) {
86        if indent {
87            self.indent();
88        }
89        for (i, stmt) in stmts.iter().enumerate() {
90            if i > 0 {
91                self.newline();
92            }
93            if i > 0 && is_declaration(&stmt.kind) {
94                self.newline();
95            }
96            self.write_indent();
97            self.print_stmt(stmt);
98        }
99        if indent {
100            self.dedent();
101        }
102    }
103
104    // =========================================================================
105    // Statements
106    // =========================================================================
107
108    fn print_stmt(&mut self, stmt: &Stmt) {
109        self.depth += 1;
110        if self.depth > MAX_DEPTH {
111            self.w("/* max depth */");
112            self.depth -= 1;
113            return;
114        }
115        self.print_stmt_inner(stmt);
116        self.depth -= 1;
117    }
118
119    fn print_stmt_inner(&mut self, stmt: &Stmt) {
120        match &stmt.kind {
121            StmtKind::Expression(expr) => {
122                self.print_expr(expr, PREC_LOWEST);
123                self.w(";");
124            }
125            StmtKind::Echo(exprs) => {
126                self.w("echo ");
127                self.print_comma_separated_exprs(exprs);
128                self.w(";");
129            }
130            StmtKind::Return(expr) => {
131                self.w("return");
132                if let Some(e) = expr {
133                    self.w(" ");
134                    self.print_expr(e, PREC_LOWEST);
135                }
136                self.w(";");
137            }
138            StmtKind::Block(stmts) => {
139                self.w("{");
140                if !stmts.is_empty() {
141                    self.newline();
142                    self.print_stmts(stmts, true);
143                    self.newline();
144                    self.write_indent();
145                }
146                self.w("}");
147            }
148            StmtKind::If(if_stmt) => self.print_if(if_stmt),
149            StmtKind::While(w) => {
150                self.w("while (");
151                self.print_expr(&w.condition, PREC_LOWEST);
152                self.w(") ");
153                self.print_block_or_stmt(w.body);
154            }
155            StmtKind::For(f) => {
156                self.w("for (");
157                self.print_comma_separated_exprs(&f.init);
158                self.w("; ");
159                self.print_comma_separated_exprs(&f.condition);
160                self.w("; ");
161                self.print_comma_separated_exprs(&f.update);
162                self.w(") ");
163                self.print_block_or_stmt(f.body);
164            }
165            StmtKind::Foreach(f) => {
166                self.w("foreach (");
167                self.print_expr(&f.expr, PREC_LOWEST);
168                self.w(" as ");
169                if let Some(key) = &f.key {
170                    self.print_expr(key, PREC_LOWEST);
171                    self.w(" => ");
172                }
173                self.print_expr(&f.value, PREC_LOWEST);
174                self.w(") ");
175                self.print_block_or_stmt(f.body);
176            }
177            StmtKind::DoWhile(dw) => {
178                self.w("do ");
179                self.print_block_or_stmt(dw.body);
180                self.w(" while (");
181                self.print_expr(&dw.condition, PREC_LOWEST);
182                self.w(");");
183            }
184            StmtKind::Function(func) => self.print_function(func),
185            StmtKind::Break(expr) => {
186                self.w("break");
187                if let Some(e) = expr {
188                    self.w(" ");
189                    self.print_expr(e, PREC_LOWEST);
190                }
191                self.w(";");
192            }
193            StmtKind::Continue(expr) => {
194                self.w("continue");
195                if let Some(e) = expr {
196                    self.w(" ");
197                    self.print_expr(e, PREC_LOWEST);
198                }
199                self.w(";");
200            }
201            StmtKind::Switch(sw) => {
202                self.w("switch (");
203                self.print_expr(&sw.expr, PREC_LOWEST);
204                self.w(") {");
205                self.newline();
206                for case in sw.cases.iter() {
207                    self.write_indent();
208                    if let Some(val) = &case.value {
209                        self.indent();
210                        self.w("case ");
211                        self.print_expr(val, PREC_LOWEST);
212                        self.w(":");
213                    } else {
214                        self.indent();
215                        self.w("default:");
216                    }
217                    if !case.body.is_empty() {
218                        self.newline();
219                        self.print_stmts(&case.body, false);
220                    }
221                    self.newline();
222                    self.dedent();
223                }
224                self.write_indent();
225                self.w("}");
226            }
227            StmtKind::Goto(label) => {
228                self.w("goto ");
229                self.w(label);
230                self.w(";");
231            }
232            StmtKind::Label(label) => {
233                self.w(label);
234                self.w(":");
235            }
236            StmtKind::Declare(decl) => {
237                self.w("declare(");
238                for (i, (name, val)) in decl.directives.iter().enumerate() {
239                    if i > 0 {
240                        self.w(", ");
241                    }
242                    self.w(name);
243                    self.w("=");
244                    self.print_expr(val, PREC_LOWEST);
245                }
246                self.w(")");
247                if let Some(body) = decl.body {
248                    self.w(" ");
249                    self.print_block_or_stmt(body);
250                } else {
251                    self.w(";");
252                }
253            }
254            StmtKind::Unset(exprs) => {
255                self.w("unset(");
256                self.print_comma_separated_exprs(exprs);
257                self.w(");");
258            }
259            StmtKind::Throw(expr) => {
260                self.w("throw ");
261                self.print_expr(expr, PREC_LOWEST);
262                self.w(";");
263            }
264            StmtKind::TryCatch(tc) => self.print_try_catch(tc),
265            StmtKind::Global(exprs) => {
266                self.w("global ");
267                self.print_comma_separated_exprs(exprs);
268                self.w(";");
269            }
270            StmtKind::Class(class) => self.print_class(class),
271            StmtKind::Interface(iface) => self.print_interface(iface),
272            StmtKind::Trait(trait_decl) => self.print_trait(trait_decl),
273            StmtKind::Enum(enum_decl) => self.print_enum(enum_decl),
274            StmtKind::Namespace(ns) => self.print_namespace(ns),
275            StmtKind::Use(use_decl) => self.print_use(use_decl),
276            StmtKind::Const(items) => {
277                self.w("const ");
278                for (i, item) in items.iter().enumerate() {
279                    if i > 0 {
280                        self.w(", ");
281                    }
282                    self.print_attributes(&item.attributes);
283                    self.w(item.name);
284                    self.w(" = ");
285                    self.print_expr(&item.value, PREC_LOWEST);
286                }
287                self.w(";");
288            }
289            StmtKind::StaticVar(vars) => {
290                self.w("static ");
291                for (i, var) in vars.iter().enumerate() {
292                    if i > 0 {
293                        self.w(", ");
294                    }
295                    self.w("$");
296                    self.w(var.name);
297                    if let Some(default) = &var.default {
298                        self.w(" = ");
299                        self.print_expr(default, PREC_LOWEST);
300                    }
301                }
302                self.w(";");
303            }
304            StmtKind::HaltCompiler(data) => {
305                self.w("__halt_compiler();");
306                self.w(data);
307            }
308            StmtKind::Nop => {
309                self.w(";");
310            }
311            StmtKind::InlineHtml(html) => {
312                self.w("?>");
313                self.w(html);
314                self.w("<?php");
315            }
316            StmtKind::Error => {
317                self.w("/* error */");
318            }
319        }
320    }
321
322    fn print_if(&mut self, if_stmt: &IfStmt) {
323        self.w("if (");
324        self.print_expr(&if_stmt.condition, PREC_LOWEST);
325        self.w(") ");
326        self.print_block_or_stmt(if_stmt.then_branch);
327        for elseif in if_stmt.elseif_branches.iter() {
328            self.w(" elseif (");
329            self.print_expr(&elseif.condition, PREC_LOWEST);
330            self.w(") ");
331            self.print_block_or_stmt(&elseif.body);
332        }
333        if let Some(else_branch) = &if_stmt.else_branch {
334            self.w(" else ");
335            self.print_block_or_stmt(else_branch);
336        }
337    }
338
339    fn print_try_catch(&mut self, tc: &TryCatchStmt) {
340        self.w("try {");
341        if !tc.body.is_empty() {
342            self.newline();
343            self.print_stmts(&tc.body, true);
344            self.newline();
345            self.write_indent();
346        }
347        self.w("}");
348        for catch in tc.catches.iter() {
349            self.w(" catch (");
350            for (i, ty) in catch.types.iter().enumerate() {
351                if i > 0 {
352                    self.w("|");
353                }
354                self.print_name(ty);
355            }
356            if let Some(var) = catch.var {
357                self.w(" $");
358                self.w(var);
359            }
360            self.w(") {");
361            if !catch.body.is_empty() {
362                self.newline();
363                self.print_stmts(&catch.body, true);
364                self.newline();
365                self.write_indent();
366            }
367            self.w("}");
368        }
369        if let Some(finally) = &tc.finally {
370            self.w(" finally {");
371            if !finally.is_empty() {
372                self.newline();
373                self.print_stmts(finally, true);
374                self.newline();
375                self.write_indent();
376            }
377            self.w("}");
378        }
379    }
380
381    fn print_block_or_stmt(&mut self, stmt: &Stmt) {
382        if let StmtKind::Block(stmts) = &stmt.kind {
383            self.w("{");
384            if !stmts.is_empty() {
385                self.newline();
386                self.print_stmts(stmts, true);
387                self.newline();
388                self.write_indent();
389            }
390            self.w("}");
391        } else {
392            self.w("{");
393            self.newline();
394            self.indent();
395            self.write_indent();
396            self.print_stmt(stmt);
397            self.newline();
398            self.dedent();
399            self.write_indent();
400            self.w("}");
401        }
402    }
403
404    // =========================================================================
405    // Expressions
406    // =========================================================================
407
408    fn print_expr(&mut self, expr: &Expr, parent_prec: i8) {
409        self.depth += 1;
410        if self.depth > MAX_DEPTH {
411            self.w("/* max depth */");
412            self.depth -= 1;
413            return;
414        }
415        let my_prec = expr_precedence(&expr.kind);
416        let needs_parens = my_prec < parent_prec && my_prec != PREC_PRIMARY;
417        if needs_parens {
418            self.w("(");
419        }
420        self.print_expr_inner(expr);
421        if needs_parens {
422            self.w(")");
423        }
424        self.depth -= 1;
425    }
426
427    fn print_expr_inner(&mut self, expr: &Expr) {
428        match &expr.kind {
429            ExprKind::Int(n) => self.w(&n.to_string()),
430            ExprKind::Float(f) => {
431                if f.is_nan() {
432                    self.w("\\NAN");
433                } else if f.is_infinite() {
434                    if f.is_sign_negative() {
435                        self.w("-\\INF");
436                    } else {
437                        self.w("\\INF");
438                    }
439                } else {
440                    let s = format!("{f}");
441                    self.w(&s);
442                    if !s.contains('.') && !s.contains('e') && !s.contains('E') {
443                        self.w(".0");
444                    }
445                }
446            }
447            ExprKind::String(s) => self.print_string_literal(s),
448            ExprKind::InterpolatedString(parts) => {
449                self.w("\"");
450                self.print_string_parts(parts);
451                self.w("\"");
452            }
453            ExprKind::Heredoc { label, parts } => {
454                self.w("<<<");
455                self.w(label);
456                self.newline();
457                self.print_string_parts(parts);
458                self.newline();
459                self.w(label);
460            }
461            ExprKind::Nowdoc { label, value } => {
462                self.w("<<<'");
463                self.w(label);
464                self.w("'");
465                self.newline();
466                self.w(value);
467                self.newline();
468                self.w(label);
469            }
470            ExprKind::ShellExec(parts) => {
471                self.w("`");
472                self.print_string_parts(parts);
473                self.w("`");
474            }
475            ExprKind::Bool(b) => self.w(if *b { "true" } else { "false" }),
476            ExprKind::Null => self.w("null"),
477            ExprKind::Variable(name) => {
478                self.w("$");
479                self.w(name.as_str());
480            }
481            ExprKind::VariableVariable(inner) => {
482                self.w("$");
483                if matches!(&inner.kind, ExprKind::Variable(_)) {
484                    self.print_expr(inner, PREC_PRIMARY);
485                } else {
486                    self.w("{");
487                    self.print_expr(inner, PREC_LOWEST);
488                    self.w("}");
489                }
490            }
491            ExprKind::Identifier(name) => self.w(name.as_str()),
492            ExprKind::Assign(assign) => {
493                let (_, lhs_prec, rhs_prec) = assign_op_precedence(assign.op);
494                self.print_expr(assign.target, lhs_prec);
495                self.w(" ");
496                if assign.by_ref {
497                    self.w("=& ");
498                } else {
499                    self.w(assign_op_str(assign.op));
500                    self.w(" ");
501                }
502                self.print_expr(assign.value, rhs_prec);
503            }
504            ExprKind::Binary(binary) => {
505                let (_, lhs_prec, rhs_prec) = binary_op_precedence(binary.op);
506                self.print_expr(binary.left, lhs_prec);
507                self.w(" ");
508                self.w(binary_op_str(binary.op));
509                self.w(" ");
510                self.print_expr(binary.right, rhs_prec);
511            }
512            ExprKind::UnaryPrefix(unary) => {
513                self.w(unary_prefix_op_str(unary.op));
514                self.print_expr(unary.operand, PREC_UNARY);
515            }
516            ExprKind::UnaryPostfix(unary) => {
517                self.print_expr(unary.operand, PREC_PRIMARY);
518                self.w(unary_postfix_op_str(unary.op));
519            }
520            ExprKind::Ternary(ternary) => {
521                self.print_expr(ternary.condition, PREC_TERNARY + 1);
522                if let Some(then_expr) = &ternary.then_expr {
523                    self.w(" ? ");
524                    self.print_expr(then_expr, PREC_LOWEST);
525                    self.w(" : ");
526                } else {
527                    self.w(" ?: ");
528                }
529                self.print_expr(ternary.else_expr, PREC_TERNARY + 1);
530            }
531            ExprKind::NullCoalesce(nc) => {
532                self.print_expr(nc.left, PREC_NULL_COALESCE + 1);
533                self.w(" ?? ");
534                self.print_expr(nc.right, PREC_NULL_COALESCE);
535            }
536            ExprKind::FunctionCall(call) => {
537                self.print_expr(call.name, PREC_PRIMARY);
538                self.w("(");
539                self.print_args(&call.args);
540                self.w(")");
541            }
542            ExprKind::Array(elements) => {
543                self.w("[");
544                self.print_array_elements(elements);
545                self.w("]");
546            }
547            ExprKind::ArrayAccess(access) => {
548                self.print_expr(access.array, PREC_PRIMARY);
549                self.w("[");
550                if let Some(index) = &access.index {
551                    self.print_expr(index, PREC_LOWEST);
552                }
553                self.w("]");
554            }
555            ExprKind::Print(e) => {
556                self.w("print ");
557                self.print_expr(e, PREC_PRINT);
558            }
559            ExprKind::Parenthesized(e) => {
560                self.w("(");
561                self.print_expr(e, PREC_LOWEST);
562                self.w(")");
563            }
564            ExprKind::Cast(kind, e) => {
565                self.w(cast_str(*kind));
566                self.print_expr(e, PREC_CAST);
567            }
568            ExprKind::ErrorSuppress(e) => {
569                self.w("@");
570                self.print_expr(e, PREC_UNARY);
571            }
572            ExprKind::Isset(exprs) => {
573                self.w("isset(");
574                self.print_comma_separated_exprs(exprs);
575                self.w(")");
576            }
577            ExprKind::Empty(e) => {
578                self.w("empty(");
579                self.print_expr(e, PREC_LOWEST);
580                self.w(")");
581            }
582            ExprKind::Include(kind, e) => {
583                self.w(include_kind_str(*kind));
584                self.w(" ");
585                self.print_expr(e, PREC_INCLUDE);
586            }
587            ExprKind::Eval(e) => {
588                self.w("eval(");
589                self.print_expr(e, PREC_LOWEST);
590                self.w(")");
591            }
592            ExprKind::Exit(e) => {
593                self.w("exit");
594                if let Some(e) = e {
595                    self.w("(");
596                    self.print_expr(e, PREC_LOWEST);
597                    self.w(")");
598                }
599            }
600            ExprKind::MagicConst(kind) => self.w(magic_const_str(*kind)),
601            ExprKind::Clone(e) => {
602                self.w("clone ");
603                self.print_expr(e, PREC_CLONE);
604            }
605            ExprKind::CloneWith(obj, overrides) => {
606                self.w("clone(");
607                self.print_expr(obj, PREC_LOWEST);
608                self.w(", ");
609                self.print_expr(overrides, PREC_LOWEST);
610                self.w(")");
611            }
612            ExprKind::New(new_expr) => {
613                self.w("new ");
614                self.print_expr(new_expr.class, PREC_PRIMARY);
615                if !new_expr.args.is_empty() {
616                    self.w("(");
617                    self.print_args(&new_expr.args);
618                    self.w(")");
619                }
620            }
621            ExprKind::PropertyAccess(access) => {
622                self.print_expr(access.object, PREC_PRIMARY);
623                self.w("->");
624                self.print_expr(access.property, PREC_PRIMARY);
625            }
626            ExprKind::NullsafePropertyAccess(access) => {
627                self.print_expr(access.object, PREC_PRIMARY);
628                self.w("?->");
629                self.print_expr(access.property, PREC_PRIMARY);
630            }
631            ExprKind::MethodCall(call) => {
632                self.print_expr(call.object, PREC_PRIMARY);
633                self.w("->");
634                self.print_expr(call.method, PREC_PRIMARY);
635                self.w("(");
636                self.print_args(&call.args);
637                self.w(")");
638            }
639            ExprKind::NullsafeMethodCall(call) => {
640                self.print_expr(call.object, PREC_PRIMARY);
641                self.w("?->");
642                self.print_expr(call.method, PREC_PRIMARY);
643                self.w("(");
644                self.print_args(&call.args);
645                self.w(")");
646            }
647            ExprKind::StaticPropertyAccess(access) => {
648                self.print_expr(access.class, PREC_PRIMARY);
649                self.w("::$");
650                self.w(&access.member);
651            }
652            ExprKind::ClassConstAccess(access) => {
653                self.print_expr(access.class, PREC_PRIMARY);
654                self.w("::");
655                self.w(&access.member);
656            }
657            ExprKind::ClassConstAccessDynamic { class, member } => {
658                self.print_expr(class, PREC_PRIMARY);
659                self.w("::{");
660                self.print_expr(member, PREC_LOWEST);
661                self.w("}");
662            }
663            ExprKind::StaticPropertyAccessDynamic { class, member } => {
664                self.print_expr(class, PREC_PRIMARY);
665                self.w("::");
666                self.print_expr(member, PREC_PRIMARY);
667            }
668            ExprKind::StaticMethodCall(call) => {
669                self.print_expr(call.class, PREC_PRIMARY);
670                self.w("::");
671                self.w(&call.method);
672                self.w("(");
673                self.print_args(&call.args);
674                self.w(")");
675            }
676            ExprKind::Closure(closure) => self.print_closure(closure),
677            ExprKind::ArrowFunction(af) => self.print_arrow_function(af),
678            ExprKind::Match(m) => self.print_match(m),
679            ExprKind::ThrowExpr(e) => {
680                self.w("throw ");
681                self.print_expr(e, PREC_LOWEST);
682            }
683            ExprKind::Yield(y) => {
684                if y.is_from {
685                    self.w("yield from ");
686                    if let Some(val) = &y.value {
687                        self.print_expr(val, PREC_YIELD_FROM);
688                    }
689                } else {
690                    self.w("yield");
691                    if let Some(key) = &y.key {
692                        self.w(" ");
693                        self.print_expr(key, PREC_YIELD);
694                        self.w(" => ");
695                    } else if y.value.is_some() {
696                        self.w(" ");
697                    }
698                    if let Some(val) = &y.value {
699                        self.print_expr(val, PREC_YIELD);
700                    }
701                }
702            }
703            ExprKind::AnonymousClass(class) => {
704                self.print_class_header(class);
705                self.print_class_body(&class.members);
706            }
707            ExprKind::CallableCreate(cc) => match &cc.kind {
708                CallableCreateKind::Function(name) => {
709                    self.print_expr(name, PREC_PRIMARY);
710                    self.w("(...)");
711                }
712                CallableCreateKind::Method { object, method } => {
713                    self.print_expr(object, PREC_PRIMARY);
714                    self.w("->");
715                    self.print_expr(method, PREC_PRIMARY);
716                    self.w("(...)");
717                }
718                CallableCreateKind::NullsafeMethod { object, method } => {
719                    self.print_expr(object, PREC_PRIMARY);
720                    self.w("?->");
721                    self.print_expr(method, PREC_PRIMARY);
722                    self.w("(...)");
723                }
724                CallableCreateKind::StaticMethod { class, method } => {
725                    self.print_expr(class, PREC_PRIMARY);
726                    self.w("::");
727                    self.w(method);
728                    self.w("(...)");
729                }
730            },
731            ExprKind::Omit => {}
732            ExprKind::Error => self.w("/* error */"),
733        }
734    }
735
736    // =========================================================================
737    // Declarations
738    // =========================================================================
739
740    fn print_function(&mut self, func: &FunctionDecl) {
741        self.print_doc_comment(&func.doc_comment);
742        self.print_attributes(&func.attributes);
743        self.w("function ");
744        if func.by_ref {
745            self.w("&");
746        }
747        self.w(func.name);
748        self.w("(");
749        self.print_params(&func.params);
750        self.w(")");
751        if let Some(ret) = &func.return_type {
752            self.w(": ");
753            self.print_type_hint(ret);
754        }
755        self.newline();
756        self.write_indent();
757        self.w("{");
758        if !func.body.is_empty() {
759            self.newline();
760            self.print_stmts(&func.body, true);
761            self.newline();
762            self.write_indent();
763        }
764        self.w("}");
765    }
766
767    fn print_class(&mut self, class: &ClassDecl) {
768        self.print_doc_comment(&class.doc_comment);
769        self.print_attributes(&class.attributes);
770        self.print_class_header(class);
771        self.print_class_body(&class.members);
772    }
773
774    fn print_class_header(&mut self, class: &ClassDecl) {
775        if class.modifiers.is_abstract {
776            self.w("abstract ");
777        }
778        if class.modifiers.is_final {
779            self.w("final ");
780        }
781        if class.modifiers.is_readonly {
782            self.w("readonly ");
783        }
784        self.w("class");
785        if let Some(name) = class.name {
786            self.w(" ");
787            self.w(name);
788        }
789        if let Some(extends) = &class.extends {
790            self.w(" extends ");
791            self.print_name(extends);
792        }
793        if !class.implements.is_empty() {
794            self.w(" implements ");
795            for (i, name) in class.implements.iter().enumerate() {
796                if i > 0 {
797                    self.w(", ");
798                }
799                self.print_name(name);
800            }
801        }
802    }
803
804    fn print_class_body(&mut self, members: &[ClassMember]) {
805        self.newline();
806        self.write_indent();
807        self.w("{");
808        if !members.is_empty() {
809            self.newline();
810            self.indent();
811            for (i, member) in members.iter().enumerate() {
812                if i > 0 {
813                    self.newline();
814                }
815                self.write_indent();
816                self.print_class_member(member);
817                self.newline();
818            }
819            self.dedent();
820            self.write_indent();
821        }
822        self.w("}");
823    }
824
825    fn print_class_member(&mut self, member: &ClassMember) {
826        match &member.kind {
827            ClassMemberKind::Property(prop) => self.print_property(prop),
828            ClassMemberKind::Method(method) => self.print_method(method),
829            ClassMemberKind::ClassConst(cc) => self.print_class_const(cc),
830            ClassMemberKind::TraitUse(tu) => self.print_trait_use(tu),
831        }
832    }
833
834    fn print_method(&mut self, method: &MethodDecl) {
835        self.print_doc_comment(&method.doc_comment);
836        self.print_attributes(&method.attributes);
837        if method.is_abstract {
838            self.w("abstract ");
839        }
840        if method.is_final {
841            self.w("final ");
842        }
843        if let Some(vis) = &method.visibility {
844            self.w(visibility_str(*vis));
845            self.w(" ");
846        }
847        if method.is_static {
848            self.w("static ");
849        }
850        self.w("function ");
851        if method.by_ref {
852            self.w("&");
853        }
854        self.w(method.name);
855        self.w("(");
856        self.print_params(&method.params);
857        self.w(")");
858        if let Some(ret) = &method.return_type {
859            self.w(": ");
860            self.print_type_hint(ret);
861        }
862        if let Some(body) = &method.body {
863            self.newline();
864            self.write_indent();
865            self.w("{");
866            if !body.is_empty() {
867                self.newline();
868                self.print_stmts(body, true);
869                self.newline();
870                self.write_indent();
871            }
872            self.w("}");
873        } else {
874            self.w(";");
875        }
876    }
877
878    fn print_property(&mut self, prop: &PropertyDecl) {
879        self.print_doc_comment(&prop.doc_comment);
880        self.print_attributes(&prop.attributes);
881        if let Some(vis) = &prop.visibility {
882            self.w(visibility_str(*vis));
883            self.w(" ");
884        }
885        if let Some(set_vis) = &prop.set_visibility {
886            self.w(visibility_str(*set_vis));
887            self.w("(set) ");
888        }
889        if prop.is_static {
890            self.w("static ");
891        }
892        if prop.is_readonly {
893            self.w("readonly ");
894        }
895        if let Some(th) = &prop.type_hint {
896            self.print_type_hint(th);
897            self.w(" ");
898        }
899        self.w("$");
900        self.w(prop.name);
901        if let Some(default) = &prop.default {
902            self.w(" = ");
903            self.print_expr(default, PREC_LOWEST);
904        }
905        if !prop.hooks.is_empty() {
906            self.w(" ");
907            self.print_property_hooks(&prop.hooks);
908        } else {
909            self.w(";");
910        }
911    }
912
913    fn print_property_hooks(&mut self, hooks: &[PropertyHook]) {
914        self.w("{");
915        self.newline();
916        self.indent();
917        for hook in hooks.iter() {
918            self.write_indent();
919            self.print_attributes(&hook.attributes);
920            if hook.is_final {
921                self.w("final ");
922            }
923            if hook.by_ref {
924                self.w("&");
925            }
926            match hook.kind {
927                PropertyHookKind::Get => self.w("get"),
928                PropertyHookKind::Set => self.w("set"),
929            }
930            if !hook.params.is_empty() {
931                self.w("(");
932                self.print_params(&hook.params);
933                self.w(")");
934            }
935            match &hook.body {
936                PropertyHookBody::Block(stmts) => {
937                    self.w(" {");
938                    if !stmts.is_empty() {
939                        self.newline();
940                        self.print_stmts(stmts, true);
941                        self.newline();
942                        self.write_indent();
943                    }
944                    self.w("}");
945                }
946                PropertyHookBody::Expression(e) => {
947                    self.w(" => ");
948                    self.print_expr(e, PREC_LOWEST);
949                    self.w(";");
950                }
951                PropertyHookBody::Abstract => self.w(";"),
952            }
953            self.newline();
954        }
955        self.dedent();
956        self.write_indent();
957        self.w("}");
958    }
959
960    fn print_class_const(&mut self, cc: &ClassConstDecl) {
961        self.print_doc_comment(&cc.doc_comment);
962        self.print_attributes(&cc.attributes);
963        if let Some(vis) = &cc.visibility {
964            self.w(visibility_str(*vis));
965            self.w(" ");
966        }
967        self.w("const ");
968        if let Some(th) = &cc.type_hint {
969            self.print_type_hint(th);
970            self.w(" ");
971        }
972        self.w(cc.name);
973        self.w(" = ");
974        self.print_expr(&cc.value, PREC_LOWEST);
975        self.w(";");
976    }
977
978    fn print_trait_use(&mut self, tu: &TraitUseDecl) {
979        self.w("use ");
980        for (i, name) in tu.traits.iter().enumerate() {
981            if i > 0 {
982                self.w(", ");
983            }
984            self.print_name(name);
985        }
986        if tu.adaptations.is_empty() {
987            self.w(";");
988        } else {
989            self.w(" {");
990            self.newline();
991            self.indent();
992            for adapt in tu.adaptations.iter() {
993                self.write_indent();
994                match &adapt.kind {
995                    TraitAdaptationKind::Precedence {
996                        trait_name,
997                        method,
998                        insteadof,
999                    } => {
1000                        self.print_name(trait_name);
1001                        self.w("::");
1002                        self.w(method);
1003                        self.w(" insteadof ");
1004                        for (i, name) in insteadof.iter().enumerate() {
1005                            if i > 0 {
1006                                self.w(", ");
1007                            }
1008                            self.print_name(name);
1009                        }
1010                    }
1011                    TraitAdaptationKind::Alias {
1012                        trait_name,
1013                        method,
1014                        new_modifier,
1015                        new_name,
1016                    } => {
1017                        if let Some(tn) = trait_name {
1018                            self.print_name(tn);
1019                            self.w("::");
1020                        }
1021                        self.w(method);
1022                        self.w(" as");
1023                        if let Some(vis) = new_modifier {
1024                            self.w(" ");
1025                            self.w(visibility_str(*vis));
1026                        }
1027                        if let Some(name) = new_name {
1028                            self.w(" ");
1029                            self.w(name);
1030                        }
1031                    }
1032                }
1033                self.w(";");
1034                self.newline();
1035            }
1036            self.dedent();
1037            self.write_indent();
1038            self.w("}");
1039        }
1040    }
1041
1042    fn print_interface(&mut self, iface: &InterfaceDecl) {
1043        self.print_doc_comment(&iface.doc_comment);
1044        self.print_attributes(&iface.attributes);
1045        self.w("interface ");
1046        self.w(iface.name);
1047        if !iface.extends.is_empty() {
1048            self.w(" extends ");
1049            for (i, name) in iface.extends.iter().enumerate() {
1050                if i > 0 {
1051                    self.w(", ");
1052                }
1053                self.print_name(name);
1054            }
1055        }
1056        self.print_class_body(&iface.members);
1057    }
1058
1059    fn print_trait(&mut self, trait_decl: &TraitDecl) {
1060        self.print_doc_comment(&trait_decl.doc_comment);
1061        self.print_attributes(&trait_decl.attributes);
1062        self.w("trait ");
1063        self.w(trait_decl.name);
1064        self.print_class_body(&trait_decl.members);
1065    }
1066
1067    fn print_enum(&mut self, enum_decl: &EnumDecl) {
1068        self.print_doc_comment(&enum_decl.doc_comment);
1069        self.print_attributes(&enum_decl.attributes);
1070        self.w("enum ");
1071        self.w(enum_decl.name);
1072        if let Some(scalar) = &enum_decl.scalar_type {
1073            self.w(": ");
1074            self.print_name(scalar);
1075        }
1076        if !enum_decl.implements.is_empty() {
1077            self.w(" implements ");
1078            for (i, name) in enum_decl.implements.iter().enumerate() {
1079                if i > 0 {
1080                    self.w(", ");
1081                }
1082                self.print_name(name);
1083            }
1084        }
1085        self.newline();
1086        self.write_indent();
1087        self.w("{");
1088        if !enum_decl.members.is_empty() {
1089            self.newline();
1090            self.indent();
1091            for (i, member) in enum_decl.members.iter().enumerate() {
1092                if i > 0 {
1093                    self.newline();
1094                }
1095                self.write_indent();
1096                self.print_enum_member(member);
1097                self.newline();
1098            }
1099            self.dedent();
1100            self.write_indent();
1101        }
1102        self.w("}");
1103    }
1104
1105    fn print_enum_member(&mut self, member: &EnumMember) {
1106        match &member.kind {
1107            EnumMemberKind::Case(case) => {
1108                self.print_doc_comment(&case.doc_comment);
1109                self.print_attributes(&case.attributes);
1110                self.w("case ");
1111                self.w(case.name);
1112                if let Some(val) = &case.value {
1113                    self.w(" = ");
1114                    self.print_expr(val, PREC_LOWEST);
1115                }
1116                self.w(";");
1117            }
1118            EnumMemberKind::Method(method) => self.print_method(method),
1119            EnumMemberKind::ClassConst(cc) => self.print_class_const(cc),
1120            EnumMemberKind::TraitUse(tu) => self.print_trait_use(tu),
1121        }
1122    }
1123
1124    fn print_namespace(&mut self, ns: &NamespaceDecl) {
1125        self.w("namespace");
1126        if let Some(name) = &ns.name {
1127            self.w(" ");
1128            self.print_name(name);
1129        }
1130        match &ns.body {
1131            NamespaceBody::Braced(stmts) => {
1132                self.w(" {");
1133                if !stmts.is_empty() {
1134                    self.newline();
1135                    self.print_stmts(stmts, true);
1136                    self.newline();
1137                    self.write_indent();
1138                }
1139                self.w("}");
1140            }
1141            NamespaceBody::Simple => self.w(";"),
1142        }
1143    }
1144
1145    fn print_use(&mut self, use_decl: &UseDecl) {
1146        self.w("use ");
1147        match use_decl.kind {
1148            UseKind::Function => self.w("function "),
1149            UseKind::Const => self.w("const "),
1150            UseKind::Normal => {}
1151        }
1152        for (i, item) in use_decl.uses.iter().enumerate() {
1153            if i > 0 {
1154                self.w(", ");
1155            }
1156            self.print_name(&item.name);
1157            if let Some(alias) = item.alias {
1158                self.w(" as ");
1159                self.w(alias);
1160            }
1161        }
1162        self.w(";");
1163    }
1164
1165    fn print_closure(&mut self, closure: &ClosureExpr) {
1166        self.print_attributes(&closure.attributes);
1167        if closure.is_static {
1168            self.w("static ");
1169        }
1170        self.w("function");
1171        if closure.by_ref {
1172            self.w(" &");
1173        }
1174        self.w("(");
1175        self.print_params(&closure.params);
1176        self.w(")");
1177        if !closure.use_vars.is_empty() {
1178            self.w(" use (");
1179            for (i, var) in closure.use_vars.iter().enumerate() {
1180                if i > 0 {
1181                    self.w(", ");
1182                }
1183                if var.by_ref {
1184                    self.w("&");
1185                }
1186                self.w("$");
1187                self.w(var.name);
1188            }
1189            self.w(")");
1190        }
1191        if let Some(ret) = &closure.return_type {
1192            self.w(": ");
1193            self.print_type_hint(ret);
1194        }
1195        self.w(" {");
1196        if !closure.body.is_empty() {
1197            self.newline();
1198            self.print_stmts(&closure.body, true);
1199            self.newline();
1200            self.write_indent();
1201        }
1202        self.w("}");
1203    }
1204
1205    fn print_arrow_function(&mut self, af: &ArrowFunctionExpr) {
1206        self.print_attributes(&af.attributes);
1207        if af.is_static {
1208            self.w("static ");
1209        }
1210        self.w("fn");
1211        if af.by_ref {
1212            self.w("&");
1213        }
1214        self.w("(");
1215        self.print_params(&af.params);
1216        self.w(")");
1217        if let Some(ret) = &af.return_type {
1218            self.w(": ");
1219            self.print_type_hint(ret);
1220        }
1221        self.w(" => ");
1222        self.print_expr(af.body, PREC_LOWEST);
1223    }
1224
1225    fn print_match(&mut self, m: &MatchExpr) {
1226        self.w("match (");
1227        self.print_expr(m.subject, PREC_LOWEST);
1228        self.w(") {");
1229        self.newline();
1230        self.indent();
1231        for arm in m.arms.iter() {
1232            self.write_indent();
1233            if let Some(conds) = &arm.conditions {
1234                for (i, cond) in conds.iter().enumerate() {
1235                    if i > 0 {
1236                        self.w(", ");
1237                    }
1238                    self.print_expr(cond, PREC_LOWEST);
1239                }
1240            } else {
1241                self.w("default");
1242            }
1243            self.w(" => ");
1244            self.print_expr(&arm.body, PREC_LOWEST);
1245            self.w(",");
1246            self.newline();
1247        }
1248        self.dedent();
1249        self.write_indent();
1250        self.w("}");
1251    }
1252
1253    // =========================================================================
1254    // Helpers
1255    // =========================================================================
1256
1257    fn print_name(&mut self, name: &Name) {
1258        match name {
1259            Name::Simple { value, .. } => self.w(value),
1260            Name::Complex { parts, kind, .. } => {
1261                match kind {
1262                    NameKind::FullyQualified => self.w("\\"),
1263                    NameKind::Relative => self.w("namespace\\"),
1264                    _ => {}
1265                }
1266                for (i, part) in parts.iter().enumerate() {
1267                    if i > 0 {
1268                        self.w("\\");
1269                    }
1270                    self.w(part);
1271                }
1272            }
1273        }
1274    }
1275
1276    fn print_type_hint(&mut self, hint: &TypeHint) {
1277        match &hint.kind {
1278            TypeHintKind::Named(name) => self.print_name(name),
1279            TypeHintKind::Keyword(builtin, _) => self.w(builtin.as_str()),
1280            TypeHintKind::Nullable(inner) => {
1281                self.w("?");
1282                self.print_type_hint(inner);
1283            }
1284            TypeHintKind::Union(types) => {
1285                for (i, ty) in types.iter().enumerate() {
1286                    if i > 0 {
1287                        self.w("|");
1288                    }
1289                    self.print_type_hint(ty);
1290                }
1291            }
1292            TypeHintKind::Intersection(types) => {
1293                for (i, ty) in types.iter().enumerate() {
1294                    if i > 0 {
1295                        self.w("&");
1296                    }
1297                    self.print_type_hint(ty);
1298                }
1299            }
1300        }
1301    }
1302
1303    fn print_params(&mut self, params: &[Param]) {
1304        for (i, param) in params.iter().enumerate() {
1305            if i > 0 {
1306                self.w(", ");
1307            }
1308            self.print_attributes_inline(&param.attributes);
1309            if let Some(vis) = &param.visibility {
1310                self.w(visibility_str(*vis));
1311                self.w(" ");
1312            }
1313            if let Some(set_vis) = &param.set_visibility {
1314                self.w(visibility_str(*set_vis));
1315                self.w("(set) ");
1316            }
1317            if param.is_readonly {
1318                self.w("readonly ");
1319            }
1320            if param.is_final {
1321                self.w("final ");
1322            }
1323            if let Some(th) = &param.type_hint {
1324                self.print_type_hint(th);
1325                self.w(" ");
1326            }
1327            if param.variadic {
1328                self.w("...");
1329            }
1330            if param.by_ref {
1331                self.w("&");
1332            }
1333            self.w("$");
1334            self.w(param.name);
1335            if let Some(default) = &param.default {
1336                self.w(" = ");
1337                self.print_expr(default, PREC_LOWEST);
1338            }
1339        }
1340    }
1341
1342    fn print_args(&mut self, args: &[Arg]) {
1343        for (i, arg) in args.iter().enumerate() {
1344            if i > 0 {
1345                self.w(", ");
1346            }
1347            if let Some(name) = &arg.name {
1348                self.w(name);
1349                self.w(": ");
1350            }
1351            if arg.unpack {
1352                self.w("...");
1353            }
1354            if arg.by_ref {
1355                self.w("&");
1356            }
1357            self.print_expr(&arg.value, PREC_LOWEST);
1358        }
1359    }
1360
1361    fn print_attributes(&mut self, attrs: &[Attribute]) {
1362        for attr in attrs.iter() {
1363            self.w("#[");
1364            self.print_name(&attr.name);
1365            if !attr.args.is_empty() {
1366                self.w("(");
1367                self.print_args(&attr.args);
1368                self.w(")");
1369            }
1370            self.w("]");
1371            self.newline();
1372            self.write_indent();
1373        }
1374    }
1375
1376    fn print_attributes_inline(&mut self, attrs: &[Attribute]) {
1377        for attr in attrs.iter() {
1378            self.w("#[");
1379            self.print_name(&attr.name);
1380            if !attr.args.is_empty() {
1381                self.w("(");
1382                self.print_args(&attr.args);
1383                self.w(")");
1384            }
1385            self.w("] ");
1386        }
1387    }
1388
1389    fn print_doc_comment(&mut self, doc: &Option<Comment>) {
1390        if let Some(comment) = doc {
1391            for (i, line) in comment.text.lines().enumerate() {
1392                if i > 0 {
1393                    self.newline();
1394                    self.write_indent();
1395                }
1396                self.w(line.trim_end());
1397            }
1398            self.newline();
1399            self.write_indent();
1400        }
1401    }
1402
1403    fn print_comma_separated_exprs(&mut self, exprs: &[Expr]) {
1404        for (i, expr) in exprs.iter().enumerate() {
1405            if i > 0 {
1406                self.w(", ");
1407            }
1408            self.print_expr(expr, PREC_LOWEST);
1409        }
1410    }
1411
1412    fn print_array_elements(&mut self, elements: &[ArrayElement]) {
1413        for (i, elem) in elements.iter().enumerate() {
1414            if i > 0 {
1415                self.w(", ");
1416            }
1417            if elem.unpack {
1418                self.w("...");
1419            }
1420            if let Some(key) = &elem.key {
1421                self.print_expr(key, PREC_LOWEST);
1422                self.w(" => ");
1423            }
1424            if elem.by_ref {
1425                self.w("&");
1426            }
1427            self.print_expr(&elem.value, PREC_LOWEST);
1428        }
1429    }
1430
1431    fn print_string_parts(&mut self, parts: &[StringPart]) {
1432        for part in parts.iter() {
1433            match part {
1434                StringPart::Literal(s) => self.w(&escape_double_quoted(s)),
1435                StringPart::Expr(expr) => match &expr.kind {
1436                    ExprKind::Variable(name) => {
1437                        self.w("$");
1438                        self.w(name.as_str());
1439                    }
1440                    _ => {
1441                        self.w("{");
1442                        self.print_expr(expr, PREC_LOWEST);
1443                        self.w("}");
1444                    }
1445                },
1446            }
1447        }
1448    }
1449
1450    fn print_string_literal(&mut self, s: &str) {
1451        if needs_double_quotes(s) {
1452            self.w("\"");
1453            self.w(&escape_double_quoted(s));
1454            self.w("\"");
1455        } else {
1456            self.w("'");
1457            self.w(&escape_single_quoted(s));
1458            self.w("'");
1459        }
1460    }
1461}
1462
1463// =============================================================================
1464// Pure helper functions
1465// =============================================================================
1466
1467fn is_declaration(kind: &StmtKind) -> bool {
1468    matches!(
1469        kind,
1470        StmtKind::Function(_)
1471            | StmtKind::Class(_)
1472            | StmtKind::Interface(_)
1473            | StmtKind::Trait(_)
1474            | StmtKind::Enum(_)
1475            | StmtKind::Namespace(_)
1476    )
1477}
1478
1479fn needs_double_quotes(s: &str) -> bool {
1480    s.bytes().any(|b| {
1481        matches!(
1482            b,
1483            b'\n' | b'\r' | b'\t' | b'\x1b' | b'\x0c' | b'\x0b' | b'$'
1484        )
1485    })
1486}
1487
1488fn escape_single_quoted(s: &str) -> String {
1489    let mut out = String::with_capacity(s.len());
1490    for ch in s.chars() {
1491        match ch {
1492            '\'' => out.push_str("\\'"),
1493            '\\' => out.push_str("\\\\"),
1494            _ => out.push(ch),
1495        }
1496    }
1497    out
1498}
1499
1500fn escape_double_quoted(s: &str) -> String {
1501    let mut out = String::with_capacity(s.len());
1502    for ch in s.chars() {
1503        match ch {
1504            '"' => out.push_str("\\\""),
1505            '\\' => out.push_str("\\\\"),
1506            '$' => out.push_str("\\$"),
1507            '\n' => out.push_str("\\n"),
1508            '\r' => out.push_str("\\r"),
1509            '\t' => out.push_str("\\t"),
1510            '\x1b' => out.push_str("\\e"),
1511            '\x0c' => out.push_str("\\f"),
1512            '\x0b' => out.push_str("\\v"),
1513            _ => out.push(ch),
1514        }
1515    }
1516    out
1517}
1518
1519fn binary_op_str(op: BinaryOp) -> &'static str {
1520    match op {
1521        BinaryOp::Add => "+",
1522        BinaryOp::Sub => "-",
1523        BinaryOp::Mul => "*",
1524        BinaryOp::Div => "/",
1525        BinaryOp::Mod => "%",
1526        BinaryOp::Pow => "**",
1527        BinaryOp::Concat => ".",
1528        BinaryOp::Equal => "==",
1529        BinaryOp::NotEqual => "!=",
1530        BinaryOp::Identical => "===",
1531        BinaryOp::NotIdentical => "!==",
1532        BinaryOp::Less => "<",
1533        BinaryOp::Greater => ">",
1534        BinaryOp::LessOrEqual => "<=",
1535        BinaryOp::GreaterOrEqual => ">=",
1536        BinaryOp::Spaceship => "<=>",
1537        BinaryOp::BooleanAnd => "&&",
1538        BinaryOp::BooleanOr => "||",
1539        BinaryOp::BitwiseAnd => "&",
1540        BinaryOp::BitwiseOr => "|",
1541        BinaryOp::BitwiseXor => "^",
1542        BinaryOp::ShiftLeft => "<<",
1543        BinaryOp::ShiftRight => ">>",
1544        BinaryOp::LogicalAnd => "and",
1545        BinaryOp::LogicalOr => "or",
1546        BinaryOp::LogicalXor => "xor",
1547        BinaryOp::Instanceof => "instanceof",
1548        BinaryOp::Pipe => "|>",
1549    }
1550}
1551
1552fn assign_op_str(op: AssignOp) -> &'static str {
1553    match op {
1554        AssignOp::Assign => "=",
1555        AssignOp::Plus => "+=",
1556        AssignOp::Minus => "-=",
1557        AssignOp::Mul => "*=",
1558        AssignOp::Div => "/=",
1559        AssignOp::Mod => "%=",
1560        AssignOp::Pow => "**=",
1561        AssignOp::Concat => ".=",
1562        AssignOp::BitwiseAnd => "&=",
1563        AssignOp::BitwiseOr => "|=",
1564        AssignOp::BitwiseXor => "^=",
1565        AssignOp::ShiftLeft => "<<=",
1566        AssignOp::ShiftRight => ">>=",
1567        AssignOp::Coalesce => "??=",
1568    }
1569}
1570
1571fn unary_prefix_op_str(op: UnaryPrefixOp) -> &'static str {
1572    match op {
1573        UnaryPrefixOp::Negate => "-",
1574        UnaryPrefixOp::Plus => "+",
1575        UnaryPrefixOp::BooleanNot => "!",
1576        UnaryPrefixOp::BitwiseNot => "~",
1577        UnaryPrefixOp::PreIncrement => "++",
1578        UnaryPrefixOp::PreDecrement => "--",
1579    }
1580}
1581
1582fn unary_postfix_op_str(op: UnaryPostfixOp) -> &'static str {
1583    match op {
1584        UnaryPostfixOp::PostIncrement => "++",
1585        UnaryPostfixOp::PostDecrement => "--",
1586    }
1587}
1588
1589fn cast_str(kind: CastKind) -> &'static str {
1590    match kind {
1591        CastKind::Int => "(int)",
1592        CastKind::Float => "(float)",
1593        CastKind::String => "(string)",
1594        CastKind::Bool => "(bool)",
1595        CastKind::Array => "(array)",
1596        CastKind::Object => "(object)",
1597        CastKind::Unset => "(unset)",
1598        CastKind::Void => "(void)",
1599    }
1600}
1601
1602fn include_kind_str(kind: IncludeKind) -> &'static str {
1603    match kind {
1604        IncludeKind::Include => "include",
1605        IncludeKind::IncludeOnce => "include_once",
1606        IncludeKind::Require => "require",
1607        IncludeKind::RequireOnce => "require_once",
1608    }
1609}
1610
1611fn magic_const_str(kind: MagicConstKind) -> &'static str {
1612    match kind {
1613        MagicConstKind::Class => "__CLASS__",
1614        MagicConstKind::Dir => "__DIR__",
1615        MagicConstKind::File => "__FILE__",
1616        MagicConstKind::Function => "__FUNCTION__",
1617        MagicConstKind::Line => "__LINE__",
1618        MagicConstKind::Method => "__METHOD__",
1619        MagicConstKind::Namespace => "__NAMESPACE__",
1620        MagicConstKind::Trait => "__TRAIT__",
1621        MagicConstKind::Property => "__PROPERTY__",
1622    }
1623}
1624
1625fn visibility_str(vis: Visibility) -> &'static str {
1626    match vis {
1627        Visibility::Public => "public",
1628        Visibility::Protected => "protected",
1629        Visibility::Private => "private",
1630    }
1631}