PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.2
4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 All 200 releases
betterdocs / includes / Dependencies / PhpParser / PrettyPrinter / Standard.php

Standard.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.2, at includes/Dependencies/PhpParser/PrettyPrinter/Standard.php

978 lines 37.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Dependencies\PhpParser\PrettyPrinter;
4
5 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node;
6 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Expr;
7 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Expr\AssignOp;
8 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Expr\BinaryOp;
9 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Expr\Cast;
10 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Name;
11 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Scalar;
12 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Scalar\MagicConst;
13 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt;
14 use WPDeveloper\BetterDocs\Dependencies\PhpParser\PrettyPrinterAbstract;
15
16 class Standard extends PrettyPrinterAbstract
17 {
18 // Special nodes
19
20 protected function pParam(Node\Param $node) {
21 return ($node->type ? $this->pType($node->type) . ' ' : '')
22 . ($node->byRef ? '&' : '')
23 . ($node->variadic ? '...' : '')
24 . '$' . $node->name
25 . ($node->default ? ' = ' . $this->p($node->default) : '');
26 }
27
28 protected function pArg(Node\Arg $node) {
29 return ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value);
30 }
31
32 protected function pConst(Node\Const_ $node) {
33 return $node->name . ' = ' . $this->p($node->value);
34 }
35
36 protected function pNullableType(Node\NullableType $node) {
37 return '?' . $this->pType($node->type);
38 }
39
40 // Names
41
42 protected function pName(Name $node) {
43 return implode('\\', $node->parts);
44 }
45
46 protected function pName_FullyQualified(Name\FullyQualified $node) {
47 return '\\' . implode('\\', $node->parts);
48 }
49
50 protected function pName_Relative(Name\Relative $node) {
51 return 'namespace\\' . implode('\\', $node->parts);
52 }
53
54 // Magic Constants
55
56 protected function pScalar_MagicConst_Class(MagicConst\Class_ $node) {
57 return '__CLASS__';
58 }
59
60 protected function pScalar_MagicConst_Dir(MagicConst\Dir $node) {
61 return '__DIR__';
62 }
63
64 protected function pScalar_MagicConst_File(MagicConst\File $node) {
65 return '__FILE__';
66 }
67
68 protected function pScalar_MagicConst_Function(MagicConst\Function_ $node) {
69 return '__FUNCTION__';
70 }
71
72 protected function pScalar_MagicConst_Line(MagicConst\Line $node) {
73 return '__LINE__';
74 }
75
76 protected function pScalar_MagicConst_Method(MagicConst\Method $node) {
77 return '__METHOD__';
78 }
79
80 protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node) {
81 return '__NAMESPACE__';
82 }
83
84 protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node) {
85 return '__TRAIT__';
86 }
87
88 // Scalars
89
90 protected function pScalar_String(Scalar\String_ $node) {
91 $kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED);
92 switch ($kind) {
93 case Scalar\String_::KIND_NOWDOC:
94 $label = $node->getAttribute('docLabel');
95 if ($label && !$this->containsEndLabel($node->value, $label)) {
96 if ($node->value === '') {
97 return $this->pNoIndent("<<<'$label'\n$label") . $this->docStringEndToken;
98 }
99
100 return $this->pNoIndent("<<<'$label'\n$node->value\n$label")
101 . $this->docStringEndToken;
102 }
103 /* break missing intentionally */
104 case Scalar\String_::KIND_SINGLE_QUOTED:
105 return '\'' . $this->pNoIndent(addcslashes($node->value, '\'\\')) . '\'';
106 case Scalar\String_::KIND_HEREDOC:
107 $label = $node->getAttribute('docLabel');
108 if ($label && !$this->containsEndLabel($node->value, $label)) {
109 if ($node->value === '') {
110 return $this->pNoIndent("<<<$label\n$label") . $this->docStringEndToken;
111 }
112
113 $escaped = $this->escapeString($node->value, null);
114 return $this->pNoIndent("<<<$label\n" . $escaped ."\n$label")
115 . $this->docStringEndToken;
116 }
117 /* break missing intentionally */
118 case Scalar\String_::KIND_DOUBLE_QUOTED:
119 return '"' . $this->escapeString($node->value, '"') . '"';
120 }
121 throw new \Exception('Invalid string kind');
122 }
123
124 protected function pScalar_Encapsed(Scalar\Encapsed $node) {
125 if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) {
126 $label = $node->getAttribute('docLabel');
127 if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) {
128 if (count($node->parts) === 1
129 && $node->parts[0] instanceof Scalar\EncapsedStringPart
130 && $node->parts[0]->value === ''
131 ) {
132 return $this->pNoIndent("<<<$label\n$label") . $this->docStringEndToken;
133 }
134
135 return $this->pNoIndent(
136 "<<<$label\n" . $this->pEncapsList($node->parts, null) . "\n$label"
137 ) . $this->docStringEndToken;
138 }
139 }
140 return '"' . $this->pEncapsList($node->parts, '"') . '"';
141 }
142
143 protected function pScalar_LNumber(Scalar\LNumber $node) {
144 if ($node->value === -\PHP_INT_MAX-1) {
145 // PHP_INT_MIN cannot be represented as a literal,
146 // because the sign is not part of the literal
147 return '(-' . \PHP_INT_MAX . '-1)';
148 }
149
150 $kind = $node->getAttribute('kind', Scalar\LNumber::KIND_DEC);
151 if (Scalar\LNumber::KIND_DEC === $kind) {
152 return (string) $node->value;
153 }
154
155 $sign = $node->value < 0 ? '-' : '';
156 $str = (string) $node->value;
157 switch ($kind) {
158 case Scalar\LNumber::KIND_BIN:
159 return $sign . '0b' . base_convert($str, 10, 2);
160 case Scalar\LNumber::KIND_OCT:
161 return $sign . '0' . base_convert($str, 10, 8);
162 case Scalar\LNumber::KIND_HEX:
163 return $sign . '0x' . base_convert($str, 10, 16);
164 }
165 throw new \Exception('Invalid number kind');
166 }
167
168 protected function pScalar_DNumber(Scalar\DNumber $node) {
169 if (!is_finite($node->value)) {
170 if ($node->value === \INF) {
171 return '\INF';
172 } elseif ($node->value === -\INF) {
173 return '-\INF';
174 } else {
175 return '\NAN';
176 }
177 }
178
179 // Try to find a short full-precision representation
180 $stringValue = sprintf('%.16G', $node->value);
181 if ($node->value !== (double) $stringValue) {
182 $stringValue = sprintf('%.17G', $node->value);
183 }
184
185 // %G is locale dependent and there exists no locale-independent alternative. We don't want
186 // mess with switching locales here, so let's assume that a comma is the only non-standard
187 // decimal separator we may encounter...
188 $stringValue = str_replace(',', '.', $stringValue);
189
190 // ensure that number is really printed as float
191 return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue;
192 }
193
194 // Assignments
195
196 protected function pExpr_Assign(Expr\Assign $node) {
197 return $this->pInfixOp('Expr_Assign', $node->var, ' = ', $node->expr);
198 }
199
200 protected function pExpr_AssignRef(Expr\AssignRef $node) {
201 return $this->pInfixOp('Expr_AssignRef', $node->var, ' =& ', $node->expr);
202 }
203
204 protected function pExpr_AssignOp_Plus(AssignOp\Plus $node) {
205 return $this->pInfixOp('Expr_AssignOp_Plus', $node->var, ' += ', $node->expr);
206 }
207
208 protected function pExpr_AssignOp_Minus(AssignOp\Minus $node) {
209 return $this->pInfixOp('Expr_AssignOp_Minus', $node->var, ' -= ', $node->expr);
210 }
211
212 protected function pExpr_AssignOp_Mul(AssignOp\Mul $node) {
213 return $this->pInfixOp('Expr_AssignOp_Mul', $node->var, ' *= ', $node->expr);
214 }
215
216 protected function pExpr_AssignOp_Div(AssignOp\Div $node) {
217 return $this->pInfixOp('Expr_AssignOp_Div', $node->var, ' /= ', $node->expr);
218 }
219
220 protected function pExpr_AssignOp_Concat(AssignOp\Concat $node) {
221 return $this->pInfixOp('Expr_AssignOp_Concat', $node->var, ' .= ', $node->expr);
222 }
223
224 protected function pExpr_AssignOp_Mod(AssignOp\Mod $node) {
225 return $this->pInfixOp('Expr_AssignOp_Mod', $node->var, ' %= ', $node->expr);
226 }
227
228 protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node) {
229 return $this->pInfixOp('Expr_AssignOp_BitwiseAnd', $node->var, ' &= ', $node->expr);
230 }
231
232 protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node) {
233 return $this->pInfixOp('Expr_AssignOp_BitwiseOr', $node->var, ' |= ', $node->expr);
234 }
235
236 protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node) {
237 return $this->pInfixOp('Expr_AssignOp_BitwiseXor', $node->var, ' ^= ', $node->expr);
238 }
239
240 protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node) {
241 return $this->pInfixOp('Expr_AssignOp_ShiftLeft', $node->var, ' <<= ', $node->expr);
242 }
243
244 protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node) {
245 return $this->pInfixOp('Expr_AssignOp_ShiftRight', $node->var, ' >>= ', $node->expr);
246 }
247
248 protected function pExpr_AssignOp_Pow(AssignOp\Pow $node) {
249 return $this->pInfixOp('Expr_AssignOp_Pow', $node->var, ' **= ', $node->expr);
250 }
251
252 // Binary expressions
253
254 protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) {
255 return $this->pInfixOp('Expr_BinaryOp_Plus', $node->left, ' + ', $node->right);
256 }
257
258 protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node) {
259 return $this->pInfixOp('Expr_BinaryOp_Minus', $node->left, ' - ', $node->right);
260 }
261
262 protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node) {
263 return $this->pInfixOp('Expr_BinaryOp_Mul', $node->left, ' * ', $node->right);
264 }
265
266 protected function pExpr_BinaryOp_Div(BinaryOp\Div $node) {
267 return $this->pInfixOp('Expr_BinaryOp_Div', $node->left, ' / ', $node->right);
268 }
269
270 protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node) {
271 return $this->pInfixOp('Expr_BinaryOp_Concat', $node->left, ' . ', $node->right);
272 }
273
274 protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node) {
275 return $this->pInfixOp('Expr_BinaryOp_Mod', $node->left, ' % ', $node->right);
276 }
277
278 protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node) {
279 return $this->pInfixOp('Expr_BinaryOp_BooleanAnd', $node->left, ' && ', $node->right);
280 }
281
282 protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node) {
283 return $this->pInfixOp('Expr_BinaryOp_BooleanOr', $node->left, ' || ', $node->right);
284 }
285
286 protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node) {
287 return $this->pInfixOp('Expr_BinaryOp_BitwiseAnd', $node->left, ' & ', $node->right);
288 }
289
290 protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node) {
291 return $this->pInfixOp('Expr_BinaryOp_BitwiseOr', $node->left, ' | ', $node->right);
292 }
293
294 protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node) {
295 return $this->pInfixOp('Expr_BinaryOp_BitwiseXor', $node->left, ' ^ ', $node->right);
296 }
297
298 protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node) {
299 return $this->pInfixOp('Expr_BinaryOp_ShiftLeft', $node->left, ' << ', $node->right);
300 }
301
302 protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node) {
303 return $this->pInfixOp('Expr_BinaryOp_ShiftRight', $node->left, ' >> ', $node->right);
304 }
305
306 protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node) {
307 return $this->pInfixOp('Expr_BinaryOp_Pow', $node->left, ' ** ', $node->right);
308 }
309
310 protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node) {
311 return $this->pInfixOp('Expr_BinaryOp_LogicalAnd', $node->left, ' and ', $node->right);
312 }
313
314 protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node) {
315 return $this->pInfixOp('Expr_BinaryOp_LogicalOr', $node->left, ' or ', $node->right);
316 }
317
318 protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node) {
319 return $this->pInfixOp('Expr_BinaryOp_LogicalXor', $node->left, ' xor ', $node->right);
320 }
321
322 protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node) {
323 return $this->pInfixOp('Expr_BinaryOp_Equal', $node->left, ' == ', $node->right);
324 }
325
326 protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node) {
327 return $this->pInfixOp('Expr_BinaryOp_NotEqual', $node->left, ' != ', $node->right);
328 }
329
330 protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node) {
331 return $this->pInfixOp('Expr_BinaryOp_Identical', $node->left, ' === ', $node->right);
332 }
333
334 protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node) {
335 return $this->pInfixOp('Expr_BinaryOp_NotIdentical', $node->left, ' !== ', $node->right);
336 }
337
338 protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node) {
339 return $this->pInfixOp('Expr_BinaryOp_Spaceship', $node->left, ' <=> ', $node->right);
340 }
341
342 protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node) {
343 return $this->pInfixOp('Expr_BinaryOp_Greater', $node->left, ' > ', $node->right);
344 }
345
346 protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node) {
347 return $this->pInfixOp('Expr_BinaryOp_GreaterOrEqual', $node->left, ' >= ', $node->right);
348 }
349
350 protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node) {
351 return $this->pInfixOp('Expr_BinaryOp_Smaller', $node->left, ' < ', $node->right);
352 }
353
354 protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node) {
355 return $this->pInfixOp('Expr_BinaryOp_SmallerOrEqual', $node->left, ' <= ', $node->right);
356 }
357
358 protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) {
359 return $this->pInfixOp('Expr_BinaryOp_Coalesce', $node->left, ' ?? ', $node->right);
360 }
361
362 protected function pExpr_Instanceof(Expr\Instanceof_ $node) {
363 return $this->pInfixOp('Expr_Instanceof', $node->expr, ' instanceof ', $node->class);
364 }
365
366 // Unary expressions
367
368 protected function pExpr_BooleanNot(Expr\BooleanNot $node) {
369 return $this->pPrefixOp('Expr_BooleanNot', '!', $node->expr);
370 }
371
372 protected function pExpr_BitwiseNot(Expr\BitwiseNot $node) {
373 return $this->pPrefixOp('Expr_BitwiseNot', '~', $node->expr);
374 }
375
376 protected function pExpr_UnaryMinus(Expr\UnaryMinus $node) {
377 if ($node->expr instanceof Expr\UnaryMinus || $node->expr instanceof Expr\PreDec) {
378 // Enforce -(-$expr) instead of --$expr
379 return '-(' . $this->p($node->expr) . ')';
380 }
381 return $this->pPrefixOp('Expr_UnaryMinus', '-', $node->expr);
382 }
383
384 protected function pExpr_UnaryPlus(Expr\UnaryPlus $node) {
385 if ($node->expr instanceof Expr\UnaryPlus || $node->expr instanceof Expr\PreInc) {
386 // Enforce +(+$expr) instead of ++$expr
387 return '+(' . $this->p($node->expr) . ')';
388 }
389 return $this->pPrefixOp('Expr_UnaryPlus', '+', $node->expr);
390 }
391
392 protected function pExpr_PreInc(Expr\PreInc $node) {
393 return $this->pPrefixOp('Expr_PreInc', '++', $node->var);
394 }
395
396 protected function pExpr_PreDec(Expr\PreDec $node) {
397 return $this->pPrefixOp('Expr_PreDec', '--', $node->var);
398 }
399
400 protected function pExpr_PostInc(Expr\PostInc $node) {
401 return $this->pPostfixOp('Expr_PostInc', $node->var, '++');
402 }
403
404 protected function pExpr_PostDec(Expr\PostDec $node) {
405 return $this->pPostfixOp('Expr_PostDec', $node->var, '--');
406 }
407
408 protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) {
409 return $this->pPrefixOp('Expr_ErrorSuppress', '@', $node->expr);
410 }
411
412 protected function pExpr_YieldFrom(Expr\YieldFrom $node) {
413 return $this->pPrefixOp('Expr_YieldFrom', 'yield from ', $node->expr);
414 }
415
416 protected function pExpr_Print(Expr\Print_ $node) {
417 return $this->pPrefixOp('Expr_Print', 'print ', $node->expr);
418 }
419
420 // Casts
421
422 protected function pExpr_Cast_Int(Cast\Int_ $node) {
423 return $this->pPrefixOp('Expr_Cast_Int', '(int) ', $node->expr);
424 }
425
426 protected function pExpr_Cast_Double(Cast\Double $node) {
427 return $this->pPrefixOp('Expr_Cast_Double', '(double) ', $node->expr);
428 }
429
430 protected function pExpr_Cast_String(Cast\String_ $node) {
431 return $this->pPrefixOp('Expr_Cast_String', '(string) ', $node->expr);
432 }
433
434 protected function pExpr_Cast_Array(Cast\Array_ $node) {
435 return $this->pPrefixOp('Expr_Cast_Array', '(array) ', $node->expr);
436 }
437
438 protected function pExpr_Cast_Object(Cast\Object_ $node) {
439 return $this->pPrefixOp('Expr_Cast_Object', '(object) ', $node->expr);
440 }
441
442 protected function pExpr_Cast_Bool(Cast\Bool_ $node) {
443 return $this->pPrefixOp('Expr_Cast_Bool', '(bool) ', $node->expr);
444 }
445
446 protected function pExpr_Cast_Unset(Cast\Unset_ $node) {
447 return $this->pPrefixOp('Expr_Cast_Unset', '(unset) ', $node->expr);
448 }
449
450 // Function calls and similar constructs
451
452 protected function pExpr_FuncCall(Expr\FuncCall $node) {
453 return $this->pCallLhs($node->name)
454 . '(' . $this->pMaybeMultiline($node->args) . ')';
455 }
456
457 protected function pExpr_MethodCall(Expr\MethodCall $node) {
458 return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name)
459 . '(' . $this->pMaybeMultiline($node->args) . ')';
460 }
461
462 protected function pExpr_StaticCall(Expr\StaticCall $node) {
463 return $this->pDereferenceLhs($node->class) . '::'
464 . ($node->name instanceof Expr
465 ? ($node->name instanceof Expr\Variable
466 ? $this->p($node->name)
467 : '{' . $this->p($node->name) . '}')
468 : $node->name)
469 . '(' . $this->pMaybeMultiline($node->args) . ')';
470 }
471
472 protected function pExpr_Empty(Expr\Empty_ $node) {
473 return 'empty(' . $this->p($node->expr) . ')';
474 }
475
476 protected function pExpr_Isset(Expr\Isset_ $node) {
477 return 'isset(' . $this->pCommaSeparated($node->vars) . ')';
478 }
479
480 protected function pExpr_Eval(Expr\Eval_ $node) {
481 return 'eval(' . $this->p($node->expr) . ')';
482 }
483
484 protected function pExpr_Include(Expr\Include_ $node) {
485 static $map = array(
486 Expr\Include_::TYPE_INCLUDE => 'include',
487 Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once',
488 Expr\Include_::TYPE_REQUIRE => 'require',
489 Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once',
490 );
491
492 return $map[$node->type] . ' ' . $this->p($node->expr);
493 }
494
495 protected function pExpr_List(Expr\List_ $node) {
496 return 'list(' . $this->pCommaSeparated($node->items) . ')';
497 }
498
499 // Other
500
501 protected function pExpr_Error(Expr\Error $node) {
502 throw new \LogicException('Cannot pretty-print AST with Error nodes');
503 }
504
505 protected function pExpr_Variable(Expr\Variable $node) {
506 if ($node->name instanceof Expr) {
507 return '${' . $this->p($node->name) . '}';
508 } else {
509 return '$' . $node->name;
510 }
511 }
512
513 protected function pExpr_Array(Expr\Array_ $node) {
514 $syntax = $node->getAttribute('kind',
515 $this->options['shortArraySyntax'] ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG);
516 if ($syntax === Expr\Array_::KIND_SHORT) {
517 return '[' . $this->pMaybeMultiline($node->items, true) . ']';
518 } else {
519 return 'array(' . $this->pMaybeMultiline($node->items, true) . ')';
520 }
521 }
522
523 protected function pExpr_ArrayItem(Expr\ArrayItem $node) {
524 return (null !== $node->key ? $this->p($node->key) . ' => ' : '')
525 . ($node->byRef ? '&' : '') . $this->p($node->value);
526 }
527
528 protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node) {
529 return $this->pDereferenceLhs($node->var)
530 . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']';
531 }
532
533 protected function pExpr_ConstFetch(Expr\ConstFetch $node) {
534 return $this->p($node->name);
535 }
536
537 protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
538 return $this->p($node->class) . '::'
539 . (is_string($node->name) ? $node->name : $this->p($node->name));
540 }
541
542 protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
543 return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name);
544 }
545
546 protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) {
547 return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
548 }
549
550 protected function pExpr_ShellExec(Expr\ShellExec $node) {
551 return '`' . $this->pEncapsList($node->parts, '`') . '`';
552 }
553
554 protected function pExpr_Closure(Expr\Closure $node) {
555 return ($node->static ? 'static ' : '')
556 . 'function ' . ($node->byRef ? '&' : '')
557 . '(' . $this->pCommaSeparated($node->params) . ')'
558 . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')': '')
559 . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
560 . ' {' . $this->pStmts($node->stmts) . "\n" . '}';
561 }
562
563 protected function pExpr_ClosureUse(Expr\ClosureUse $node) {
564 return ($node->byRef ? '&' : '') . '$' . $node->var;
565 }
566
567 protected function pExpr_New(Expr\New_ $node) {
568 if ($node->class instanceof Stmt\Class_) {
569 $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : '';
570 return 'new ' . $this->pClassCommon($node->class, $args);
571 }
572 return 'new ' . $this->p($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')';
573 }
574
575 protected function pExpr_Clone(Expr\Clone_ $node) {
576 return 'clone ' . $this->p($node->expr);
577 }
578
579 protected function pExpr_Ternary(Expr\Ternary $node) {
580 // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator.
581 // this is okay because the part between ? and : never needs parentheses.
582 return $this->pInfixOp('Expr_Ternary',
583 $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else
584 );
585 }
586
587 protected function pExpr_Exit(Expr\Exit_ $node) {
588 $kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE);
589 return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die')
590 . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : '');
591 }
592
593 protected function pExpr_Yield(Expr\Yield_ $node) {
594 if ($node->value === null) {
595 return 'yield';
596 } else {
597 // this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary
598 return '(yield '
599 . ($node->key !== null ? $this->p($node->key) . ' => ' : '')
600 . $this->p($node->value)
601 . ')';
602 }
603 }
604
605 // Declarations
606
607 protected function pStmt_Namespace(Stmt\Namespace_ $node) {
608 if ($this->canUseSemicolonNamespaces) {
609 return 'namespace ' . $this->p($node->name) . ';' . "\n" . $this->pStmts($node->stmts, false);
610 } else {
611 return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '')
612 . ' {' . $this->pStmts($node->stmts) . "\n" . '}';
613 }
614 }
615
616 protected function pStmt_Use(Stmt\Use_ $node) {
617 return 'use ' . $this->pUseType($node->type)
618 . $this->pCommaSeparated($node->uses) . ';';
619 }
620
621 protected function pStmt_GroupUse(Stmt\GroupUse $node) {
622 return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix)
623 . '\{' . $this->pCommaSeparated($node->uses) . '};';
624 }
625
626 protected function pStmt_UseUse(Stmt\UseUse $node) {
627 return $this->pUseType($node->type) . $this->p($node->name)
628 . ($node->name->getLast() !== $node->alias ? ' as ' . $node->alias : '');
629 }
630
631 protected function pUseType($type) {
632 return $type === Stmt\Use_::TYPE_FUNCTION ? 'function '
633 : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : '');
634 }
635
636 protected function pStmt_Interface(Stmt\Interface_ $node) {
637 return 'interface ' . $node->name
638 . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
639 . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
640 }
641
642 protected function pStmt_Class(Stmt\Class_ $node) {
643 return $this->pClassCommon($node, ' ' . $node->name);
644 }
645
646 protected function pStmt_Trait(Stmt\Trait_ $node) {
647 return 'trait ' . $node->name
648 . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
649 }
650
651 protected function pStmt_TraitUse(Stmt\TraitUse $node) {
652 return 'use ' . $this->pCommaSeparated($node->traits)
653 . (empty($node->adaptations)
654 ? ';'
655 : ' {' . $this->pStmts($node->adaptations) . "\n" . '}');
656 }
657
658 protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node) {
659 return $this->p($node->trait) . '::' . $node->method
660 . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';';
661 }
662
663 protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node) {
664 return (null !== $node->trait ? $this->p($node->trait) . '::' : '')
665 . $node->method . ' as'
666 . (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '')
667 . (null !== $node->newName ? ' ' . $node->newName : '')
668 . ';';
669 }
670
671 protected function pStmt_Property(Stmt\Property $node) {
672 return (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) . $this->pCommaSeparated($node->props) . ';';
673 }
674
675 protected function pStmt_PropertyProperty(Stmt\PropertyProperty $node) {
676 return '$' . $node->name
677 . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
678 }
679
680 protected function pStmt_ClassMethod(Stmt\ClassMethod $node) {
681 return $this->pModifiers($node->flags)
682 . 'function ' . ($node->byRef ? '&' : '') . $node->name
683 . '(' . $this->pCommaSeparated($node->params) . ')'
684 . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
685 . (null !== $node->stmts
686 ? "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}'
687 : ';');
688 }
689
690 protected function pStmt_ClassConst(Stmt\ClassConst $node) {
691 return $this->pModifiers($node->flags)
692 . 'const ' . $this->pCommaSeparated($node->consts) . ';';
693 }
694
695 protected function pStmt_Function(Stmt\Function_ $node) {
696 return 'function ' . ($node->byRef ? '&' : '') . $node->name
697 . '(' . $this->pCommaSeparated($node->params) . ')'
698 . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
699 . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
700 }
701
702 protected function pStmt_Const(Stmt\Const_ $node) {
703 return 'const ' . $this->pCommaSeparated($node->consts) . ';';
704 }
705
706 protected function pStmt_Declare(Stmt\Declare_ $node) {
707 return 'declare (' . $this->pCommaSeparated($node->declares) . ')'
708 . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . "\n" . '}' : ';');
709 }
710
711 protected function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node) {
712 return $node->key . '=' . $this->p($node->value);
713 }
714
715 // Control flow
716
717 protected function pStmt_If(Stmt\If_ $node) {
718 return 'if (' . $this->p($node->cond) . ') {'
719 . $this->pStmts($node->stmts) . "\n" . '}'
720 . $this->pImplode($node->elseifs)
721 . (null !== $node->else ? $this->p($node->else) : '');
722 }
723
724 protected function pStmt_ElseIf(Stmt\ElseIf_ $node) {
725 return ' elseif (' . $this->p($node->cond) . ') {'
726 . $this->pStmts($node->stmts) . "\n" . '}';
727 }
728
729 protected function pStmt_Else(Stmt\Else_ $node) {
730 return ' else {' . $this->pStmts($node->stmts) . "\n" . '}';
731 }
732
733 protected function pStmt_For(Stmt\For_ $node) {
734 return 'for ('
735 . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '')
736 . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '')
737 . $this->pCommaSeparated($node->loop)
738 . ') {' . $this->pStmts($node->stmts) . "\n" . '}';
739 }
740
741 protected function pStmt_Foreach(Stmt\Foreach_ $node) {
742 return 'foreach (' . $this->p($node->expr) . ' as '
743 . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
744 . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
745 . $this->pStmts($node->stmts) . "\n" . '}';
746 }
747
748 protected function pStmt_While(Stmt\While_ $node) {
749 return 'while (' . $this->p($node->cond) . ') {'
750 . $this->pStmts($node->stmts) . "\n" . '}';
751 }
752
753 protected function pStmt_Do(Stmt\Do_ $node) {
754 return 'do {' . $this->pStmts($node->stmts) . "\n"
755 . '} while (' . $this->p($node->cond) . ');';
756 }
757
758 protected function pStmt_Switch(Stmt\Switch_ $node) {
759 return 'switch (' . $this->p($node->cond) . ') {'
760 . $this->pStmts($node->cases) . "\n" . '}';
761 }
762
763 protected function pStmt_Case(Stmt\Case_ $node) {
764 return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
765 . $this->pStmts($node->stmts);
766 }
767
768 protected function pStmt_TryCatch(Stmt\TryCatch $node) {
769 return 'try {' . $this->pStmts($node->stmts) . "\n" . '}'
770 . $this->pImplode($node->catches)
771 . ($node->finally !== null ? $this->p($node->finally) : '');
772 }
773
774 protected function pStmt_Catch(Stmt\Catch_ $node) {
775 return ' catch (' . $this->pImplode($node->types, '|') . ' $' . $node->var . ') {'
776 . $this->pStmts($node->stmts) . "\n" . '}';
777 }
778
779 protected function pStmt_Finally(Stmt\Finally_ $node) {
780 return ' finally {' . $this->pStmts($node->stmts) . "\n" . '}';
781 }
782
783 protected function pStmt_Break(Stmt\Break_ $node) {
784 return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
785 }
786
787 protected function pStmt_Continue(Stmt\Continue_ $node) {
788 return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
789 }
790
791 protected function pStmt_Return(Stmt\Return_ $node) {
792 return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';';
793 }
794
795 protected function pStmt_Throw(Stmt\Throw_ $node) {
796 return 'throw ' . $this->p($node->expr) . ';';
797 }
798
799 protected function pStmt_Label(Stmt\Label $node) {
800 return $node->name . ':';
801 }
802
803 protected function pStmt_Goto(Stmt\Goto_ $node) {
804 return 'goto ' . $node->name . ';';
805 }
806
807 // Other
808
809 protected function pStmt_Echo(Stmt\Echo_ $node) {
810 return 'echo ' . $this->pCommaSeparated($node->exprs) . ';';
811 }
812
813 protected function pStmt_Static(Stmt\Static_ $node) {
814 return 'static ' . $this->pCommaSeparated($node->vars) . ';';
815 }
816
817 protected function pStmt_Global(Stmt\Global_ $node) {
818 return 'global ' . $this->pCommaSeparated($node->vars) . ';';
819 }
820
821 protected function pStmt_StaticVar(Stmt\StaticVar $node) {
822 return '$' . $node->name
823 . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
824 }
825
826 protected function pStmt_Unset(Stmt\Unset_ $node) {
827 return 'unset(' . $this->pCommaSeparated($node->vars) . ');';
828 }
829
830 protected function pStmt_InlineHTML(Stmt\InlineHTML $node) {
831 $newline = $node->getAttribute('hasLeadingNewline', true) ? "\n" : '';
832 return '?>' . $this->pNoIndent($newline . $node->value) . '<?php ';
833 }
834
835 protected function pStmt_HaltCompiler(Stmt\HaltCompiler $node) {
836 return '__halt_compiler();' . $node->remaining;
837 }
838
839 protected function pStmt_Nop(Stmt\Nop $node) {
840 return '';
841 }
842
843 // Helpers
844
845 protected function pType($node) {
846 return is_string($node) ? $node : $this->p($node);
847 }
848
849 protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) {
850 return $this->pModifiers($node->flags)
851 . 'class' . $afterClassToken
852 . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
853 . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
854 . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
855 }
856
857 protected function pObjectProperty($node) {
858 if ($node instanceof Expr) {
859 return '{' . $this->p($node) . '}';
860 } else {
861 return $node;
862 }
863 }
864
865 protected function pModifiers($modifiers) {
866 return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC ? 'public ' : '')
867 . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '')
868 . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '')
869 . ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '')
870 . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '')
871 . ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '');
872 }
873
874 protected function pEncapsList(array $encapsList, $quote) {
875 $return = '';
876 foreach ($encapsList as $element) {
877 if ($element instanceof Scalar\EncapsedStringPart) {
878 $return .= $this->escapeString($element->value, $quote);
879 } else {
880 $return .= '{' . $this->p($element) . '}';
881 }
882 }
883
884 return $return;
885 }
886
887 protected function escapeString($string, $quote) {
888 if (null === $quote) {
889 // For doc strings, don't escape newlines
890 $escaped = addcslashes($string, "\t\f\v$\\");
891 } else {
892 $escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
893 }
894
895 // Escape other control characters
896 return preg_replace_callback('/([\0-\10\16-\37])(?=([0-7]?))/', function ($matches) {
897 $oct = decoct(ord($matches[1]));
898 if ($matches[2] !== '') {
899 // If there is a trailing digit, use the full three character form
900 return '\\' . str_pad($oct, 3, '0', STR_PAD_LEFT);
901 }
902 return '\\' . $oct;
903 }, $escaped);
904 }
905
906 protected function containsEndLabel($string, $label, $atStart = true, $atEnd = true) {
907 $start = $atStart ? '(?:^|[\r\n])' : '[\r\n]';
908 $end = $atEnd ? '(?:$|[;\r\n])' : '[;\r\n]';
909 return false !== strpos($string, $label)
910 && preg_match('/' . $start . $label . $end . '/', $string);
911 }
912
913 protected function encapsedContainsEndLabel(array $parts, $label) {
914 foreach ($parts as $i => $part) {
915 $atStart = $i === 0;
916 $atEnd = $i === count($parts) - 1;
917 if ($part instanceof Scalar\EncapsedStringPart
918 && $this->containsEndLabel($part->value, $label, $atStart, $atEnd)
919 ) {
920 return true;
921 }
922 }
923 return false;
924 }
925
926 protected function pDereferenceLhs(Node $node) {
927 if ($node instanceof Expr\Variable
928 || $node instanceof Name
929 || $node instanceof Expr\ArrayDimFetch
930 || $node instanceof Expr\PropertyFetch
931 || $node instanceof Expr\StaticPropertyFetch
932 || $node instanceof Expr\FuncCall
933 || $node instanceof Expr\MethodCall
934 || $node instanceof Expr\StaticCall
935 || $node instanceof Expr\Array_
936 || $node instanceof Scalar\String_
937 || $node instanceof Expr\ConstFetch
938 || $node instanceof Expr\ClassConstFetch
939 ) {
940 return $this->p($node);
941 } else {
942 return '(' . $this->p($node) . ')';
943 }
944 }
945
946 protected function pCallLhs(Node $node) {
947 if ($node instanceof Name
948 || $node instanceof Expr\Variable
949 || $node instanceof Expr\ArrayDimFetch
950 || $node instanceof Expr\FuncCall
951 || $node instanceof Expr\MethodCall
952 || $node instanceof Expr\StaticCall
953 || $node instanceof Expr\Array_
954 ) {
955 return $this->p($node);
956 } else {
957 return '(' . $this->p($node) . ')';
958 }
959 }
960
961 private function hasNodeWithComments(array $nodes) {
962 foreach ($nodes as $node) {
963 if ($node && $node->getAttribute('comments')) {
964 return true;
965 }
966 }
967 return false;
968 }
969
970 private function pMaybeMultiline(array $nodes, $trailingComma = false) {
971 if (!$this->hasNodeWithComments($nodes)) {
972 return $this->pCommaSeparated($nodes);
973 } else {
974 return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . "\n";
975 }
976 }
977 }
978