1use php_ast::ast::*;
2
3use crate::precedence::*;
4
5pub struct PrinterConfig {
7 pub indent: Indent,
8 pub newline: String,
9}
10
11pub 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 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 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 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 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 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(¶m.attributes);
1309 if let Some(vis) = ¶m.visibility {
1310 self.w(visibility_str(*vis));
1311 self.w(" ");
1312 }
1313 if let Some(set_vis) = ¶m.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) = ¶m.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) = ¶m.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
1463fn 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}