PageRenderTime 56ms CodeModel.GetById 11ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php

https://bitbucket.org/alan_cordova/api-sb-map
PHP | 969 lines | 769 code | 174 blank | 26 comment | 60 complexity | 87d79da695ca64900f31dffe52ac3adb MD5 | raw file
  1<?php
  2
  3namespace PhpParser\PrettyPrinter;
  4
  5use PhpParser\Node;
  6use PhpParser\Node\Expr;
  7use PhpParser\Node\Expr\AssignOp;
  8use PhpParser\Node\Expr\BinaryOp;
  9use PhpParser\Node\Expr\Cast;
 10use PhpParser\Node\Name;
 11use PhpParser\Node\Scalar;
 12use PhpParser\Node\Scalar\MagicConst;
 13use PhpParser\Node\Stmt;
 14use PhpParser\PrettyPrinterAbstract;
 15
 16class 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        return $this->pPrefixOp('Expr_UnaryMinus', '-', $node->expr);
378    }
379
380    protected function pExpr_UnaryPlus(Expr\UnaryPlus $node) {
381        return $this->pPrefixOp('Expr_UnaryPlus', '+', $node->expr);
382    }
383
384    protected function pExpr_PreInc(Expr\PreInc $node) {
385        return $this->pPrefixOp('Expr_PreInc', '++', $node->var);
386    }
387
388    protected function pExpr_PreDec(Expr\PreDec $node) {
389        return $this->pPrefixOp('Expr_PreDec', '--', $node->var);
390    }
391
392    protected function pExpr_PostInc(Expr\PostInc $node) {
393        return $this->pPostfixOp('Expr_PostInc', $node->var, '++');
394    }
395
396    protected function pExpr_PostDec(Expr\PostDec $node) {
397        return $this->pPostfixOp('Expr_PostDec', $node->var, '--');
398    }
399
400    protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) {
401        return $this->pPrefixOp('Expr_ErrorSuppress', '@', $node->expr);
402    }
403
404    protected function pExpr_YieldFrom(Expr\YieldFrom $node) {
405        return $this->pPrefixOp('Expr_YieldFrom', 'yield from ', $node->expr);
406    }
407
408    protected function pExpr_Print(Expr\Print_ $node) {
409        return $this->pPrefixOp('Expr_Print', 'print ', $node->expr);
410    }
411
412    // Casts
413
414    protected function pExpr_Cast_Int(Cast\Int_ $node) {
415        return $this->pPrefixOp('Expr_Cast_Int', '(int) ', $node->expr);
416    }
417
418    protected function pExpr_Cast_Double(Cast\Double $node) {
419        return $this->pPrefixOp('Expr_Cast_Double', '(double) ', $node->expr);
420    }
421
422    protected function pExpr_Cast_String(Cast\String_ $node) {
423        return $this->pPrefixOp('Expr_Cast_String', '(string) ', $node->expr);
424    }
425
426    protected function pExpr_Cast_Array(Cast\Array_ $node) {
427        return $this->pPrefixOp('Expr_Cast_Array', '(array) ', $node->expr);
428    }
429
430    protected function pExpr_Cast_Object(Cast\Object_ $node) {
431        return $this->pPrefixOp('Expr_Cast_Object', '(object) ', $node->expr);
432    }
433
434    protected function pExpr_Cast_Bool(Cast\Bool_ $node) {
435        return $this->pPrefixOp('Expr_Cast_Bool', '(bool) ', $node->expr);
436    }
437
438    protected function pExpr_Cast_Unset(Cast\Unset_ $node) {
439        return $this->pPrefixOp('Expr_Cast_Unset', '(unset) ', $node->expr);
440    }
441
442    // Function calls and similar constructs
443
444    protected function pExpr_FuncCall(Expr\FuncCall $node) {
445        return $this->pCallLhs($node->name)
446             . '(' . $this->pMaybeMultiline($node->args) . ')';
447    }
448
449    protected function pExpr_MethodCall(Expr\MethodCall $node) {
450        return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name)
451             . '(' . $this->pMaybeMultiline($node->args) . ')';
452    }
453
454    protected function pExpr_StaticCall(Expr\StaticCall $node) {
455        return $this->pDereferenceLhs($node->class) . '::'
456             . ($node->name instanceof Expr
457                ? ($node->name instanceof Expr\Variable
458                   ? $this->p($node->name)
459                   : '{' . $this->p($node->name) . '}')
460                : $node->name)
461             . '(' . $this->pMaybeMultiline($node->args) . ')';
462    }
463
464    protected function pExpr_Empty(Expr\Empty_ $node) {
465        return 'empty(' . $this->p($node->expr) . ')';
466    }
467
468    protected function pExpr_Isset(Expr\Isset_ $node) {
469        return 'isset(' . $this->pCommaSeparated($node->vars) . ')';
470    }
471
472    protected function pExpr_Eval(Expr\Eval_ $node) {
473        return 'eval(' . $this->p($node->expr) . ')';
474    }
475
476    protected function pExpr_Include(Expr\Include_ $node) {
477        static $map = array(
478            Expr\Include_::TYPE_INCLUDE      => 'include',
479            Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once',
480            Expr\Include_::TYPE_REQUIRE      => 'require',
481            Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once',
482        );
483
484        return $map[$node->type] . ' ' . $this->p($node->expr);
485    }
486
487    protected function pExpr_List(Expr\List_ $node) {
488        return 'list(' . $this->pCommaSeparated($node->items) . ')';
489    }
490
491    // Other
492
493    protected function pExpr_Error(Expr\Error $node) {
494        throw new \LogicException('Cannot pretty-print AST with Error nodes');
495    }
496
497    protected function pExpr_Variable(Expr\Variable $node) {
498        if ($node->name instanceof Expr) {
499            return '${' . $this->p($node->name) . '}';
500        } else {
501            return '$' . $node->name;
502        }
503    }
504
505    protected function pExpr_Array(Expr\Array_ $node) {
506        $syntax = $node->getAttribute('kind',
507            $this->options['shortArraySyntax'] ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG);
508        if ($syntax === Expr\Array_::KIND_SHORT) {
509            return '[' . $this->pMaybeMultiline($node->items, true) . ']';
510        } else {
511            return 'array(' . $this->pMaybeMultiline($node->items, true) . ')';
512        }
513    }
514
515    protected function pExpr_ArrayItem(Expr\ArrayItem $node) {
516        return (null !== $node->key ? $this->p($node->key) . ' => ' : '')
517             . ($node->byRef ? '&' : '') . $this->p($node->value);
518    }
519
520    protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node) {
521        return $this->pDereferenceLhs($node->var)
522             . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']';
523    }
524
525    protected function pExpr_ConstFetch(Expr\ConstFetch $node) {
526        return $this->p($node->name);
527    }
528
529    protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
530        return $this->p($node->class) . '::'
531             . (is_string($node->name) ? $node->name : $this->p($node->name));
532    }
533
534    protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
535        return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name);
536    }
537
538    protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) {
539        return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
540    }
541
542    protected function pExpr_ShellExec(Expr\ShellExec $node) {
543        return '`' . $this->pEncapsList($node->parts, '`') . '`';
544    }
545
546    protected function pExpr_Closure(Expr\Closure $node) {
547        return ($node->static ? 'static ' : '')
548             . 'function ' . ($node->byRef ? '&' : '')
549             . '(' . $this->pCommaSeparated($node->params) . ')'
550             . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')': '')
551             . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
552             . ' {' . $this->pStmts($node->stmts) . "\n" . '}';
553    }
554
555    protected function pExpr_ClosureUse(Expr\ClosureUse $node) {
556        return ($node->byRef ? '&' : '') . '$' . $node->var;
557    }
558
559    protected function pExpr_New(Expr\New_ $node) {
560        if ($node->class instanceof Stmt\Class_) {
561            $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : '';
562            return 'new ' . $this->pClassCommon($node->class, $args);
563        }
564        return 'new ' . $this->p($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')';
565    }
566
567    protected function pExpr_Clone(Expr\Clone_ $node) {
568        return 'clone ' . $this->p($node->expr);
569    }
570
571    protected function pExpr_Ternary(Expr\Ternary $node) {
572        // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator.
573        // this is okay because the part between ? and : never needs parentheses.
574        return $this->pInfixOp('Expr_Ternary',
575            $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else
576        );
577    }
578
579    protected function pExpr_Exit(Expr\Exit_ $node) {
580        $kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE);
581        return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die')
582             . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : '');
583    }
584
585    protected function pExpr_Yield(Expr\Yield_ $node) {
586        if ($node->value === null) {
587            return 'yield';
588        } else {
589            // this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary
590            return '(yield '
591                 . ($node->key !== null ? $this->p($node->key) . ' => ' : '')
592                 . $this->p($node->value)
593                 . ')';
594        }
595    }
596
597    // Declarations
598
599    protected function pStmt_Namespace(Stmt\Namespace_ $node) {
600        if ($this->canUseSemicolonNamespaces) {
601            return 'namespace ' . $this->p($node->name) . ';' . "\n" . $this->pStmts($node->stmts, false);
602        } else {
603            return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '')
604                 . ' {' . $this->pStmts($node->stmts) . "\n" . '}';
605        }
606    }
607
608    protected function pStmt_Use(Stmt\Use_ $node) {
609        return 'use ' . $this->pUseType($node->type)
610             . $this->pCommaSeparated($node->uses) . ';';
611    }
612
613    protected function pStmt_GroupUse(Stmt\GroupUse $node) {
614        return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix)
615             . '\{' . $this->pCommaSeparated($node->uses) . '};';
616    }
617
618    protected function pStmt_UseUse(Stmt\UseUse $node) {
619        return $this->pUseType($node->type) . $this->p($node->name)
620             . ($node->name->getLast() !== $node->alias ? ' as ' . $node->alias : '');
621    }
622
623    protected function pUseType($type) {
624        return $type === Stmt\Use_::TYPE_FUNCTION ? 'function '
625            : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : '');
626    }
627
628    protected function pStmt_Interface(Stmt\Interface_ $node) {
629        return 'interface ' . $node->name
630             . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
631             . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
632    }
633
634    protected function pStmt_Class(Stmt\Class_ $node) {
635        return $this->pClassCommon($node, ' ' . $node->name);
636    }
637
638    protected function pStmt_Trait(Stmt\Trait_ $node) {
639        return 'trait ' . $node->name
640             . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
641    }
642
643    protected function pStmt_TraitUse(Stmt\TraitUse $node) {
644        return 'use ' . $this->pCommaSeparated($node->traits)
645             . (empty($node->adaptations)
646                ? ';'
647                : ' {' . $this->pStmts($node->adaptations) . "\n" . '}');
648    }
649
650    protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node) {
651        return $this->p($node->trait) . '::' . $node->method
652             . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';';
653    }
654
655    protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node) {
656        return (null !== $node->trait ? $this->p($node->trait) . '::' : '')
657             . $node->method . ' as'
658             . (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '')
659             . (null !== $node->newName     ? ' ' . $node->newName                        : '')
660             . ';';
661    }
662
663    protected function pStmt_Property(Stmt\Property $node) {
664        return (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) . $this->pCommaSeparated($node->props) . ';';
665    }
666
667    protected function pStmt_PropertyProperty(Stmt\PropertyProperty $node) {
668        return '$' . $node->name
669             . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
670    }
671
672    protected function pStmt_ClassMethod(Stmt\ClassMethod $node) {
673        return $this->pModifiers($node->flags)
674             . 'function ' . ($node->byRef ? '&' : '') . $node->name
675             . '(' . $this->pCommaSeparated($node->params) . ')'
676             . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
677             . (null !== $node->stmts
678                ? "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}'
679                : ';');
680    }
681
682    protected function pStmt_ClassConst(Stmt\ClassConst $node) {
683        return $this->pModifiers($node->flags)
684             . 'const ' . $this->pCommaSeparated($node->consts) . ';';
685    }
686
687    protected function pStmt_Function(Stmt\Function_ $node) {
688        return 'function ' . ($node->byRef ? '&' : '') . $node->name
689             . '(' . $this->pCommaSeparated($node->params) . ')'
690             . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
691             . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
692    }
693
694    protected function pStmt_Const(Stmt\Const_ $node) {
695        return 'const ' . $this->pCommaSeparated($node->consts) . ';';
696    }
697
698    protected function pStmt_Declare(Stmt\Declare_ $node) {
699        return 'declare (' . $this->pCommaSeparated($node->declares) . ')'
700             . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . "\n" . '}' : ';');
701    }
702
703    protected function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node) {
704        return $node->key . '=' . $this->p($node->value);
705    }
706
707    // Control flow
708
709    protected function pStmt_If(Stmt\If_ $node) {
710        return 'if (' . $this->p($node->cond) . ') {'
711             . $this->pStmts($node->stmts) . "\n" . '}'
712             . $this->pImplode($node->elseifs)
713             . (null !== $node->else ? $this->p($node->else) : '');
714    }
715
716    protected function pStmt_ElseIf(Stmt\ElseIf_ $node) {
717        return ' elseif (' . $this->p($node->cond) . ') {'
718             . $this->pStmts($node->stmts) . "\n" . '}';
719    }
720
721    protected function pStmt_Else(Stmt\Else_ $node) {
722        return ' else {' . $this->pStmts($node->stmts) . "\n" . '}';
723    }
724
725    protected function pStmt_For(Stmt\For_ $node) {
726        return 'for ('
727             . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '')
728             . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '')
729             . $this->pCommaSeparated($node->loop)
730             . ') {' . $this->pStmts($node->stmts) . "\n" . '}';
731    }
732
733    protected function pStmt_Foreach(Stmt\Foreach_ $node) {
734        return 'foreach (' . $this->p($node->expr) . ' as '
735             . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
736             . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
737             . $this->pStmts($node->stmts) . "\n" . '}';
738    }
739
740    protected function pStmt_While(Stmt\While_ $node) {
741        return 'while (' . $this->p($node->cond) . ') {'
742             . $this->pStmts($node->stmts) . "\n" . '}';
743    }
744
745    protected function pStmt_Do(Stmt\Do_ $node) {
746        return 'do {' . $this->pStmts($node->stmts) . "\n"
747             . '} while (' . $this->p($node->cond) . ');';
748    }
749
750    protected function pStmt_Switch(Stmt\Switch_ $node) {
751        return 'switch (' . $this->p($node->cond) . ') {'
752             . $this->pStmts($node->cases) . "\n" . '}';
753    }
754
755    protected function pStmt_Case(Stmt\Case_ $node) {
756        return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
757             . $this->pStmts($node->stmts);
758    }
759
760    protected function pStmt_TryCatch(Stmt\TryCatch $node) {
761        return 'try {' . $this->pStmts($node->stmts) . "\n" . '}'
762             . $this->pImplode($node->catches)
763             . ($node->finally !== null ? $this->p($node->finally) : '');
764    }
765
766    protected function pStmt_Catch(Stmt\Catch_ $node) {
767        return ' catch (' . $this->pImplode($node->types, '|') . ' $' . $node->var . ') {'
768             . $this->pStmts($node->stmts) . "\n" . '}';
769    }
770
771    protected function pStmt_Finally(Stmt\Finally_ $node) {
772        return ' finally {' . $this->pStmts($node->stmts) . "\n" . '}';
773    }
774
775    protected function pStmt_Break(Stmt\Break_ $node) {
776        return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
777    }
778
779    protected function pStmt_Continue(Stmt\Continue_ $node) {
780        return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
781    }
782
783    protected function pStmt_Return(Stmt\Return_ $node) {
784        return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';';
785    }
786
787    protected function pStmt_Throw(Stmt\Throw_ $node) {
788        return 'throw ' . $this->p($node->expr) . ';';
789    }
790
791    protected function pStmt_Label(Stmt\Label $node) {
792        return $node->name . ':';
793    }
794
795    protected function pStmt_Goto(Stmt\Goto_ $node) {
796        return 'goto ' . $node->name . ';';
797    }
798
799    // Other
800
801    protected function pStmt_Echo(Stmt\Echo_ $node) {
802        return 'echo ' . $this->pCommaSeparated($node->exprs) . ';';
803    }
804
805    protected function pStmt_Static(Stmt\Static_ $node) {
806        return 'static ' . $this->pCommaSeparated($node->vars) . ';';
807    }
808
809    protected function pStmt_Global(Stmt\Global_ $node) {
810        return 'global ' . $this->pCommaSeparated($node->vars) . ';';
811    }
812
813    protected function pStmt_StaticVar(Stmt\StaticVar $node) {
814        return '$' . $node->name
815             . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
816    }
817
818    protected function pStmt_Unset(Stmt\Unset_ $node) {
819        return 'unset(' . $this->pCommaSeparated($node->vars) . ');';
820    }
821
822    protected function pStmt_InlineHTML(Stmt\InlineHTML $node) {
823        $newline = $node->getAttribute('hasLeadingNewline', true) ? "\n" : '';
824        return '?>' . $this->pNoIndent($newline . $node->value) . '<?php ';
825    }
826
827    protected function pStmt_HaltCompiler(Stmt\HaltCompiler $node) {
828        return '__halt_compiler();' . $node->remaining;
829    }
830
831    protected function pStmt_Nop(Stmt\Nop $node) {
832        return '';
833    }
834
835    // Helpers
836
837    protected function pType($node) {
838        return is_string($node) ? $node : $this->p($node);
839    }
840
841    protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) {
842        return $this->pModifiers($node->flags)
843        . 'class' . $afterClassToken
844        . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
845        . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
846        . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
847    }
848
849    protected function pObjectProperty($node) {
850        if ($node instanceof Expr) {
851            return '{' . $this->p($node) . '}';
852        } else {
853            return $node;
854        }
855    }
856
857    protected function pModifiers($modifiers) {
858        return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC    ? 'public '    : '')
859             . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '')
860             . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE   ? 'private '   : '')
861             . ($modifiers & Stmt\Class_::MODIFIER_STATIC    ? 'static '    : '')
862             . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT  ? 'abstract '  : '')
863             . ($modifiers & Stmt\Class_::MODIFIER_FINAL     ? 'final '     : '');
864    }
865
866    protected function pEncapsList(array $encapsList, $quote) {
867        $return = '';
868        foreach ($encapsList as $element) {
869            if ($element instanceof Scalar\EncapsedStringPart) {
870                $return .= $this->escapeString($element->value, $quote);
871            } else {
872                $return .= '{' . $this->p($element) . '}';
873            }
874        }
875
876        return $return;
877    }
878
879    protected function escapeString($string, $quote) {
880        if (null === $quote) {
881            // For doc strings, don't escape newlines
882            $escaped = addcslashes($string, "\t\f\v$\\");
883        } else {
884            $escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
885        }
886
887        // Escape other control characters
888        return preg_replace_callback('/([\0-\10\16-\37])(?=([0-7]?))/', function ($matches) {
889            $oct = decoct(ord($matches[1]));
890            if ($matches[2] !== '') {
891                // If there is a trailing digit, use the full three character form
892                return '\\' . str_pad($oct, 3, '0', STR_PAD_LEFT);
893            }
894            return '\\' . $oct;
895        }, $escaped);
896    }
897
898    protected function containsEndLabel($string, $label, $atStart = true, $atEnd = true) {
899        $start = $atStart ? '(?:^|[\r\n])' : '[\r\n]';
900        $end = $atEnd ? '(?:$|[;\r\n])' : '[;\r\n]';
901        return false !== strpos($string, $label)
902            && preg_match('/' . $start . $label . $end . '/', $string);
903    }
904
905    protected function encapsedContainsEndLabel(array $parts, $label) {
906        foreach ($parts as $i => $part) {
907            $atStart = $i === 0;
908            $atEnd = $i === count($parts) - 1;
909            if ($part instanceof Scalar\EncapsedStringPart
910                && $this->containsEndLabel($part->value, $label, $atStart, $atEnd)
911            ) {
912                return true;
913            }
914        }
915        return false;
916    }
917
918    protected function pDereferenceLhs(Node $node) {
919        if ($node instanceof Expr\Variable
920            || $node instanceof Name
921            || $node instanceof Expr\ArrayDimFetch
922            || $node instanceof Expr\PropertyFetch
923            || $node instanceof Expr\StaticPropertyFetch
924            || $node instanceof Expr\FuncCall
925            || $node instanceof Expr\MethodCall
926            || $node instanceof Expr\StaticCall
927            || $node instanceof Expr\Array_
928            || $node instanceof Scalar\String_
929            || $node instanceof Expr\ConstFetch
930            || $node instanceof Expr\ClassConstFetch
931        ) {
932            return $this->p($node);
933        } else  {
934            return '(' . $this->p($node) . ')';
935        }
936    }
937
938    protected function pCallLhs(Node $node) {
939        if ($node instanceof Name
940            || $node instanceof Expr\Variable
941            || $node instanceof Expr\ArrayDimFetch
942            || $node instanceof Expr\FuncCall
943            || $node instanceof Expr\MethodCall
944            || $node instanceof Expr\StaticCall
945            || $node instanceof Expr\Array_
946        ) {
947            return $this->p($node);
948        } else  {
949            return '(' . $this->p($node) . ')';
950        }
951    }
952
953    private function hasNodeWithComments(array $nodes) {
954        foreach ($nodes as $node) {
955            if ($node && $node->getAttribute('comments')) {
956                return true;
957            }
958        }
959        return false;
960    }
961
962    private function pMaybeMultiline(array $nodes, $trailingComma = false) {
963        if (!$this->hasNodeWithComments($nodes)) {
964            return $this->pCommaSeparated($nodes);
965        } else {
966            return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . "\n";
967        }
968    }
969}