Вход Регистрация
Файл: vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php
Строк: 2072
<?php declare(strict_types=1);

namespace 
PhpParserPrettyPrinter;

use 
PhpParserNode;
use 
PhpParserNodeExpr;
use 
PhpParserNodeExprAssignOp;
use 
PhpParserNodeExprBinaryOp;
use 
PhpParserNodeExprCast;
use 
PhpParserNodeName;
use 
PhpParserNodeScalar;
use 
PhpParserNodeScalarMagicConst;
use 
PhpParserNodeStmt;
use 
PhpParserPrettyPrinterAbstract;

class 
Standard extends PrettyPrinterAbstract
{
    
// Special nodes

    
protected function pParam(NodeParam $node) {
        return 
$this->pAttrGroups($node->attrGroups, true)
             . 
$this->pModifiers($node->flags)
             . (
$node->type ? $this->p($node->type) . ' ' : '')
             . (
$node->byRef ? '&' : '')
             . (
$node->variadic ? '...' : '')
             . 
$this->p($node->var)
             . (
$node->default ? ' = ' . $this->p($node->default) : '');
    }

    protected function 
pArg(NodeArg $node) {
        return (
$node->name ? $node->name->toString() . ': ' : '')
             . (
$node->byRef ? '&' : '') . ($node->unpack ? '...' : '')
             . 
$this->p($node->value);
    }

    protected function 
pVariadicPlaceholder(NodeVariadicPlaceholder $node) {
        return 
'...';
    }

    protected function 
pConst(NodeConst_ $node) {
        return 
$node->name . ' = ' . $this->p($node->value);
    }

    protected function 
pNullableType(NodeNullableType $node) {
        return 
'?' . $this->p($node->type);
    }

    protected function 
pUnionType(NodeUnionType $node) {
        
$types = [];
        foreach (
$node->types as $typeNode) {
            if (
$typeNode instanceof NodeIntersectionType) {
                
$types[] = '('. $this->p($typeNode) . ')';
                continue;
            }
            
$types[] = $this->p($typeNode);
        }
        return 
implode('|', $types);
    }

    protected function 
pIntersectionType(NodeIntersectionType $node) {
        return 
$this->pImplode($node->types, '&');
    }

    protected function 
pIdentifier(NodeIdentifier $node) {
        return 
$node->name;
    }

    protected function 
pVarLikeIdentifier(NodeVarLikeIdentifier $node) {
        return 
'$' . $node->name;
    }

    protected function 
pAttribute(NodeAttribute $node) {
        return 
$this->p($node->name)
             . (
$node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : '');
    }

    protected function 
pAttributeGroup(NodeAttributeGroup $node) {
        return 
'#[' . $this->pCommaSeparated($node->attrs) . ']';
    }

    
// Names

    
protected function pName(Name $node) {
        return 
implode('\', $node->parts);
    }

    protected function pName_FullyQualified(NameFullyQualified $node) {
        return '
\' . implode('\', $node->parts);
    }

    protected function pName_Relative(NameRelative $node) {
        return '
namespace\' . implode('\', $node->parts);
    }

    // Magic Constants

    protected function pScalar_MagicConst_Class(MagicConstClass_ $node) {
        return '
__CLASS__';
    }

    protected function pScalar_MagicConst_Dir(MagicConstDir $node) {
        return '
__DIR__';
    }

    protected function pScalar_MagicConst_File(MagicConstFile $node) {
        return '
__FILE__';
    }

    protected function pScalar_MagicConst_Function(MagicConstFunction_ $node) {
        return '
__FUNCTION__';
    }

    protected function pScalar_MagicConst_Line(MagicConstLine $node) {
        return '
__LINE__';
    }

    protected function pScalar_MagicConst_Method(MagicConstMethod $node) {
        return '
__METHOD__';
    }

    protected function pScalar_MagicConst_Namespace(MagicConstNamespace_ $node) {
        return '
__NAMESPACE__';
    }

    protected function pScalar_MagicConst_Trait(MagicConstTrait_ $node) {
        return '
__TRAIT__';
    }

    // Scalars

    protected function pScalar_String(ScalarString_ $node) {
        $kind = $node->getAttribute('
kind', ScalarString_::KIND_SINGLE_QUOTED);
        switch ($kind) {
            case ScalarString_::KIND_NOWDOC:
                $label = $node->getAttribute('
docLabel');
                if ($label && !$this->containsEndLabel($node->value, $label)) {
                    if ($node->value === '') {
                        return "<<<'
$label'n$label" . $this->docStringEndToken;
                    }

                    return "<<<'
$label'n$node->valuen$label"
                         . $this->docStringEndToken;
                }
                /* break missing intentionally */
            case ScalarString_::KIND_SINGLE_QUOTED:
                return $this->pSingleQuotedString($node->value);
            case ScalarString_::KIND_HEREDOC:
                $label = $node->getAttribute('
docLabel');
                if ($label && !$this->containsEndLabel($node->value, $label)) {
                    if ($node->value === '') {
                        return "<<<$labeln$label" . $this->docStringEndToken;
                    }

                    $escaped = $this->escapeString($node->value, null);
                    return "<<<$labeln" . $escaped . "n$label"
                         . $this->docStringEndToken;
                }
            /* break missing intentionally */
            case ScalarString_::KIND_DOUBLE_QUOTED:
                return '"' . 
$this->escapeString($node->value, '"') . '"';
        }
        throw new Exception('Invalid string kind');
    }

    protected function pScalar_Encapsed(ScalarEncapsed 
$node) {
        if (
$node->getAttribute('kind') === ScalarString_::KIND_HEREDOC) {
            
$label = $node->getAttribute('docLabel');
            if (
$label && !$this->encapsedContainsEndLabel($node->parts, $label)) {
                if (count(
$node->parts) === 1
                    && 
$node->parts[0] instanceof ScalarEncapsedStringPart
                    && 
$node->parts[0]->value === ''
                ) {
                    return "
<<<$labeln$label" . $this->docStringEndToken;
                }

                return "
<<<$labeln" . $this->pEncapsList($node->parts, null) . "n$label"
                     . 
$this->docStringEndToken;
            }
        }
        return '"' . $this->pEncapsList($node->parts, '"') . '"';
    }

    protected function pScalar_LNumber(ScalarLNumber $node) {
        if ($node->value === -PHP_INT_MAX-1) {
            // PHP_INT_MIN cannot be represented as a literal,
            // because the sign is not part of the literal
            return '
(-' . PHP_INT_MAX . '-1)';
        }

        $kind = $node->getAttribute('
kind', ScalarLNumber::KIND_DEC);
        if (ScalarLNumber::KIND_DEC === $kind) {
            return (string) $node->value;
        }

        if ($node->value < 0) {
            $sign = '
-';
            $str = (string) -$node->value;
        } else {
            $sign = '';
            $str = (string) $node->value;
        }
        switch ($kind) {
            case ScalarLNumber::KIND_BIN:
                return $sign . '
0b' . base_convert($str, 10, 2);
            case ScalarLNumber::KIND_OCT:
                return $sign . '
0' . base_convert($str, 10, 8);
            case ScalarLNumber::KIND_HEX:
                return $sign . '
0x' . base_convert($str, 10, 16);
        }
        throw new Exception('
Invalid number kind');
    }

    protected function pScalar_DNumber(ScalarDNumber $node) {
        if (!is_finite($node->value)) {
            if ($node->value === INF) {
                return '
INF';
            } elseif ($node->value === -INF) {
                return '
-INF';
            } else {
                return '
NAN';
            }
        }

        // Try to find a short full-precision representation
        $stringValue = sprintf('
%.16G', $node->value);
        if ($node->value !== (double) $stringValue) {
            $stringValue = sprintf('
%.17G', $node->value);
        }

        // %G is locale dependent and there exists no locale-independent alternative. We don'
t want
        
// mess with switching locales here, so let's assume that a comma is the only non-standard
        // decimal separator we may encounter...
        
$stringValue = str_replace(',', '.', $stringValue);

        
// ensure that number is really printed as float
        
return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue;
    }

    protected function 
pScalar_EncapsedStringPart(ScalarEncapsedStringPart $node) {
        throw new 
LogicException('Cannot directly print EncapsedStringPart');
    }

    
// Assignments

    
protected function pExpr_Assign(ExprAssign $node) {
        return 
$this->pInfixOp(ExprAssign::class, $node->var, ' = ', $node->expr);
    }

    protected function 
pExpr_AssignRef(ExprAssignRef $node) {
        return 
$this->pInfixOp(ExprAssignRef::class, $node->var, ' =& ', $node->expr);
    }

    protected function 
pExpr_AssignOp_Plus(AssignOpPlus $node) {
        return 
$this->pInfixOp(AssignOpPlus::class, $node->var, ' += ', $node->expr);
    }

    protected function 
pExpr_AssignOp_Minus(AssignOpMinus $node) {
        return 
$this->pInfixOp(AssignOpMinus::class, $node->var, ' -= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_Mul(AssignOpMul $node) {
        return 
$this->pInfixOp(AssignOpMul::class, $node->var, ' *= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_Div(AssignOpDiv $node) {
        return 
$this->pInfixOp(AssignOpDiv::class, $node->var, ' /= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_Concat(AssignOpConcat $node) {
        return 
$this->pInfixOp(AssignOpConcat::class, $node->var, ' .= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_Mod(AssignOpMod $node) {
        return 
$this->pInfixOp(AssignOpMod::class, $node->var, ' %= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_BitwiseAnd(AssignOpBitwiseAnd $node) {
        return 
$this->pInfixOp(AssignOpBitwiseAnd::class, $node->var, ' &= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_BitwiseOr(AssignOpBitwiseOr $node) {
        return 
$this->pInfixOp(AssignOpBitwiseOr::class, $node->var, ' |= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_BitwiseXor(AssignOpBitwiseXor $node) {
        return 
$this->pInfixOp(AssignOpBitwiseXor::class, $node->var, ' ^= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_ShiftLeft(AssignOpShiftLeft $node) {
        return 
$this->pInfixOp(AssignOpShiftLeft::class, $node->var, ' <<= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_ShiftRight(AssignOpShiftRight $node) {
        return 
$this->pInfixOp(AssignOpShiftRight::class, $node->var, ' >>= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_Pow(AssignOpPow $node) {
        return 
$this->pInfixOp(AssignOpPow::class, $node->var, ' **= ', $node->expr);
    }

    protected function 
pExpr_AssignOp_Coalesce(AssignOpCoalesce $node) {
        return 
$this->pInfixOp(AssignOpCoalesce::class, $node->var, ' ??= ', $node->expr);
    }

    
// Binary expressions

    
protected function pExpr_BinaryOp_Plus(BinaryOpPlus $node) {
        return 
$this->pInfixOp(BinaryOpPlus::class, $node->left, ' + ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Minus(BinaryOpMinus $node) {
        return 
$this->pInfixOp(BinaryOpMinus::class, $node->left, ' - ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Mul(BinaryOpMul $node) {
        return 
$this->pInfixOp(BinaryOpMul::class, $node->left, ' * ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Div(BinaryOpDiv $node) {
        return 
$this->pInfixOp(BinaryOpDiv::class, $node->left, ' / ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Concat(BinaryOpConcat $node) {
        return 
$this->pInfixOp(BinaryOpConcat::class, $node->left, ' . ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Mod(BinaryOpMod $node) {
        return 
$this->pInfixOp(BinaryOpMod::class, $node->left, ' % ', $node->right);
    }

    protected function 
pExpr_BinaryOp_BooleanAnd(BinaryOpBooleanAnd $node) {
        return 
$this->pInfixOp(BinaryOpBooleanAnd::class, $node->left, ' && ', $node->right);
    }

    protected function 
pExpr_BinaryOp_BooleanOr(BinaryOpBooleanOr $node) {
        return 
$this->pInfixOp(BinaryOpBooleanOr::class, $node->left, ' || ', $node->right);
    }

    protected function 
pExpr_BinaryOp_BitwiseAnd(BinaryOpBitwiseAnd $node) {
        return 
$this->pInfixOp(BinaryOpBitwiseAnd::class, $node->left, ' & ', $node->right);
    }

    protected function 
pExpr_BinaryOp_BitwiseOr(BinaryOpBitwiseOr $node) {
        return 
$this->pInfixOp(BinaryOpBitwiseOr::class, $node->left, ' | ', $node->right);
    }

    protected function 
pExpr_BinaryOp_BitwiseXor(BinaryOpBitwiseXor $node) {
        return 
$this->pInfixOp(BinaryOpBitwiseXor::class, $node->left, ' ^ ', $node->right);
    }

    protected function 
pExpr_BinaryOp_ShiftLeft(BinaryOpShiftLeft $node) {
        return 
$this->pInfixOp(BinaryOpShiftLeft::class, $node->left, ' << ', $node->right);
    }

    protected function 
pExpr_BinaryOp_ShiftRight(BinaryOpShiftRight $node) {
        return 
$this->pInfixOp(BinaryOpShiftRight::class, $node->left, ' >> ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Pow(BinaryOpPow $node) {
        return 
$this->pInfixOp(BinaryOpPow::class, $node->left, ' ** ', $node->right);
    }

    protected function 
pExpr_BinaryOp_LogicalAnd(BinaryOpLogicalAnd $node) {
        return 
$this->pInfixOp(BinaryOpLogicalAnd::class, $node->left, ' and ', $node->right);
    }

    protected function 
pExpr_BinaryOp_LogicalOr(BinaryOpLogicalOr $node) {
        return 
$this->pInfixOp(BinaryOpLogicalOr::class, $node->left, ' or ', $node->right);
    }

    protected function 
pExpr_BinaryOp_LogicalXor(BinaryOpLogicalXor $node) {
        return 
$this->pInfixOp(BinaryOpLogicalXor::class, $node->left, ' xor ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Equal(BinaryOpEqual $node) {
        return 
$this->pInfixOp(BinaryOpEqual::class, $node->left, ' == ', $node->right);
    }

    protected function 
pExpr_BinaryOp_NotEqual(BinaryOpNotEqual $node) {
        return 
$this->pInfixOp(BinaryOpNotEqual::class, $node->left, ' != ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Identical(BinaryOpIdentical $node) {
        return 
$this->pInfixOp(BinaryOpIdentical::class, $node->left, ' === ', $node->right);
    }

    protected function 
pExpr_BinaryOp_NotIdentical(BinaryOpNotIdentical $node) {
        return 
$this->pInfixOp(BinaryOpNotIdentical::class, $node->left, ' !== ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Spaceship(BinaryOpSpaceship $node) {
        return 
$this->pInfixOp(BinaryOpSpaceship::class, $node->left, ' <=> ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Greater(BinaryOpGreater $node) {
        return 
$this->pInfixOp(BinaryOpGreater::class, $node->left, ' > ', $node->right);
    }

    protected function 
pExpr_BinaryOp_GreaterOrEqual(BinaryOpGreaterOrEqual $node) {
        return 
$this->pInfixOp(BinaryOpGreaterOrEqual::class, $node->left, ' >= ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Smaller(BinaryOpSmaller $node) {
        return 
$this->pInfixOp(BinaryOpSmaller::class, $node->left, ' < ', $node->right);
    }

    protected function 
pExpr_BinaryOp_SmallerOrEqual(BinaryOpSmallerOrEqual $node) {
        return 
$this->pInfixOp(BinaryOpSmallerOrEqual::class, $node->left, ' <= ', $node->right);
    }

    protected function 
pExpr_BinaryOp_Coalesce(BinaryOpCoalesce $node) {
        return 
$this->pInfixOp(BinaryOpCoalesce::class, $node->left, ' ?? ', $node->right);
    }

    protected function 
pExpr_Instanceof(ExprInstanceof_ $node) {
        list(
$precedence, $associativity) = $this->precedenceMap[ExprInstanceof_::class];
        return 
$this->pPrec($node->expr, $precedence, $associativity, -1)
             . 
' instanceof '
             
. $this->pNewVariable($node->class);
    }

    
// Unary expressions

    
protected function pExpr_BooleanNot(ExprBooleanNot $node) {
        return 
$this->pPrefixOp(ExprBooleanNot::class, '!', $node->expr);
    }

    protected function 
pExpr_BitwiseNot(ExprBitwiseNot $node) {
        return 
$this->pPrefixOp(ExprBitwiseNot::class, '~', $node->expr);
    }

    protected function 
pExpr_UnaryMinus(ExprUnaryMinus $node) {
        if (
$node->expr instanceof ExprUnaryMinus || $node->expr instanceof ExprPreDec) {
            
// Enforce -(-$expr) instead of --$expr
            
return '-(' . $this->p($node->expr) . ')';
        }
        return 
$this->pPrefixOp(ExprUnaryMinus::class, '-', $node->expr);
    }

    protected function 
pExpr_UnaryPlus(ExprUnaryPlus $node) {
        if (
$node->expr instanceof ExprUnaryPlus || $node->expr instanceof ExprPreInc) {
            
// Enforce +(+$expr) instead of ++$expr
            
return '+(' . $this->p($node->expr) . ')';
        }
        return 
$this->pPrefixOp(ExprUnaryPlus::class, '+', $node->expr);
    }

    protected function 
pExpr_PreInc(ExprPreInc $node) {
        return 
$this->pPrefixOp(ExprPreInc::class, '++', $node->var);
    }

    protected function 
pExpr_PreDec(ExprPreDec $node) {
        return 
$this->pPrefixOp(ExprPreDec::class, '--', $node->var);
    }

    protected function 
pExpr_PostInc(ExprPostInc $node) {
        return 
$this->pPostfixOp(ExprPostInc::class, $node->var, '++');
    }

    protected function 
pExpr_PostDec(ExprPostDec $node) {
        return 
$this->pPostfixOp(ExprPostDec::class, $node->var, '--');
    }

    protected function 
pExpr_ErrorSuppress(ExprErrorSuppress $node) {
        return 
$this->pPrefixOp(ExprErrorSuppress::class, '@', $node->expr);
    }

    protected function 
pExpr_YieldFrom(ExprYieldFrom $node) {
        return 
$this->pPrefixOp(ExprYieldFrom::class, 'yield from ', $node->expr);
    }

    protected function 
pExpr_Print(ExprPrint_ $node) {
        return 
$this->pPrefixOp(ExprPrint_::class, 'print ', $node->expr);
    }

    
// Casts

    
protected function pExpr_Cast_Int(CastInt_ $node) {
        return 
$this->pPrefixOp(CastInt_::class, '(int) ', $node->expr);
    }

    protected function 
pExpr_Cast_Double(CastDouble $node) {
        
$kind = $node->getAttribute('kind', CastDouble::KIND_DOUBLE);
        if (
$kind === CastDouble::KIND_DOUBLE) {
            
$cast = '(double)';
        } elseif (
$kind === CastDouble::KIND_FLOAT) {
            
$cast = '(float)';
        } elseif (
$kind === CastDouble::KIND_REAL) {
            
$cast = '(real)';
        }
        return 
$this->pPrefixOp(CastDouble::class, $cast . ' ', $node->expr);
    }

    protected function 
pExpr_Cast_String(CastString_ $node) {
        return 
$this->pPrefixOp(CastString_::class, '(string) ', $node->expr);
    }

    protected function 
pExpr_Cast_Array(CastArray_ $node) {
        return 
$this->pPrefixOp(CastArray_::class, '(array) ', $node->expr);
    }

    protected function 
pExpr_Cast_Object(CastObject_ $node) {
        return 
$this->pPrefixOp(CastObject_::class, '(object) ', $node->expr);
    }

    protected function 
pExpr_Cast_Bool(CastBool_ $node) {
        return 
$this->pPrefixOp(CastBool_::class, '(bool) ', $node->expr);
    }

    protected function 
pExpr_Cast_Unset(CastUnset_ $node) {
        return 
$this->pPrefixOp(CastUnset_::class, '(unset) ', $node->expr);
    }

    
// Function calls and similar constructs

    
protected function pExpr_FuncCall(ExprFuncCall $node) {
        return 
$this->pCallLhs($node->name)
             . 
'(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function 
pExpr_MethodCall(ExprMethodCall $node) {
        return 
$this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name)
             . 
'(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function 
pExpr_NullsafeMethodCall(ExprNullsafeMethodCall $node) {
        return 
$this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name)
            . 
'(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function 
pExpr_StaticCall(ExprStaticCall $node) {
        return 
$this->pDereferenceLhs($node->class) . '::'
             
. ($node->name instanceof Expr
                
? ($node->name instanceof ExprVariable
                   
? $this->p($node->name)
                   : 
'{' . $this->p($node->name) . '}')
                : 
$node->name)
             . 
'(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function 
pExpr_Empty(ExprEmpty_ $node) {
        return 
'empty(' . $this->p($node->expr) . ')';
    }

    protected function 
pExpr_Isset(ExprIsset_ $node) {
        return 
'isset(' . $this->pCommaSeparated($node->vars) . ')';
    }

    protected function 
pExpr_Eval(ExprEval_ $node) {
        return 
'eval(' . $this->p($node->expr) . ')';
    }

    protected function 
pExpr_Include(ExprInclude_ $node) {
        static 
$map = [
            
ExprInclude_::TYPE_INCLUDE      => 'include',
            
ExprInclude_::TYPE_INCLUDE_ONCE => 'include_once',
            
ExprInclude_::TYPE_REQUIRE      => 'require',
            
ExprInclude_::TYPE_REQUIRE_ONCE => 'require_once',
        ];

        return 
$map[$node->type] . ' ' . $this->p($node->expr);
    }

    protected function 
pExpr_List(ExprList_ $node) {
        return 
'list(' . $this->pCommaSeparated($node->items) . ')';
    }

    
// Other

    
protected function pExpr_Error(ExprError $node) {
        throw new 
LogicException('Cannot pretty-print AST with Error nodes');
    }

    protected function 
pExpr_Variable(ExprVariable $node) {
        if (
$node->name instanceof Expr) {
            return 
'${' . $this->p($node->name) . '}';
        } else {
            return 
'$' . $node->name;
        }
    }

    protected function 
pExpr_Array(ExprArray_ $node) {
        
$syntax = $node->getAttribute('kind',
            
$this->options['shortArraySyntax'] ? ExprArray_::KIND_SHORT : ExprArray_::KIND_LONG);
        if (
$syntax === ExprArray_::KIND_SHORT) {
            return 
'[' . $this->pMaybeMultiline($node->items, true) . ']';
        } else {
            return 
'array(' . $this->pMaybeMultiline($node->items, true) . ')';
        }
    }

    protected function 
pExpr_ArrayItem(ExprArrayItem $node) {
        return (
null !== $node->key ? $this->p($node->key) . ' => ' : '')
             . (
$node->byRef ? '&' : '')
             . (
$node->unpack ? '...' : '')
             . 
$this->p($node->value);
    }

    protected function 
pExpr_ArrayDimFetch(ExprArrayDimFetch $node) {
        return 
$this->pDereferenceLhs($node->var)
             . 
'[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']';
    }

    protected function 
pExpr_ConstFetch(ExprConstFetch $node) {
        return 
$this->p($node->name);
    }

    protected function 
pExpr_ClassConstFetch(ExprClassConstFetch $node) {
        return 
$this->pDereferenceLhs($node->class) . '::' . $this->p($node->name);
    }

    protected function 
pExpr_PropertyFetch(ExprPropertyFetch $node) {
        return 
$this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name);
    }

    protected function 
pExpr_NullsafePropertyFetch(ExprNullsafePropertyFetch $node) {
        return 
$this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name);
    }

    protected function 
pExpr_StaticPropertyFetch(ExprStaticPropertyFetch $node) {
        return 
$this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
    }

    protected function 
pExpr_ShellExec(ExprShellExec $node) {
        return 
'`' . $this->pEncapsList($node->parts, '`') . '`';
    }

    protected function 
pExpr_Closure(ExprClosure $node) {
        return 
$this->pAttrGroups($node->attrGroups, true)
             . (
$node->static ? 'static ' : '')
             . 
'function ' . ($node->byRef ? '&' : '')
             . 
'(' . $this->pCommaSeparated($node->params) . ')'
             
. (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')' : '')
             . (
null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
             . 
' {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pExpr_Match(ExprMatch_ $node) {
        return 
'match (' . $this->p($node->cond) . ') {'
            
. $this->pCommaSeparatedMultiline($node->arms, true)
            . 
$this->nl
            
. '}';
    }

    protected function 
pMatchArm(NodeMatchArm $node) {
        return (
$node->conds ? $this->pCommaSeparated($node->conds) : 'default')
            . 
' => ' . $this->p($node->body);
    }

    protected function 
pExpr_ArrowFunction(ExprArrowFunction $node) {
        return 
$this->pAttrGroups($node->attrGroups, true)
            . (
$node->static ? 'static ' : '')
            . 
'fn' . ($node->byRef ? '&' : '')
            . 
'(' . $this->pCommaSeparated($node->params) . ')'
            
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
            . 
' => '
            
. $this->p($node->expr);
    }

    protected function 
pExpr_ClosureUse(ExprClosureUse $node) {
        return (
$node->byRef ? '&' : '') . $this->p($node->var);
    }

    protected function 
pExpr_New(ExprNew_ $node) {
        if (
$node->class instanceof StmtClass_) {
            
$args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : '';
            return 
'new ' . $this->pClassCommon($node->class, $args);
        }
        return 
'new ' . $this->pNewVariable($node->class)
            . 
'(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function 
pExpr_Clone(ExprClone_ $node) {
        return 
'clone ' . $this->p($node->expr);
    }

    protected function 
pExpr_Ternary(ExprTernary $node) {
        
// a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator.
        // this is okay because the part between ? and : never needs parentheses.
        
return $this->pInfixOp(ExprTernary::class,
            
$node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else
        
);
    }

    protected function 
pExpr_Exit(ExprExit_ $node) {
        
$kind = $node->getAttribute('kind', ExprExit_::KIND_DIE);
        return (
$kind === ExprExit_::KIND_EXIT ? 'exit' : 'die')
             . (
null !== $node->expr ? '(' . $this->p($node->expr) . ')' : '');
    }

    protected function 
pExpr_Throw(ExprThrow_ $node) {
        return 
'throw ' . $this->p($node->expr);
    }

    protected function 
pExpr_Yield(ExprYield_ $node) {
        if (
$node->value === null) {
            return 
'yield';
        } else {
            
// this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary
            
return '(yield '
                 
. ($node->key !== null ? $this->p($node->key) . ' => ' : '')
                 . 
$this->p($node->value)
                 . 
')';
        }
    }

    
// Declarations

    
protected function pStmt_Namespace(StmtNamespace_ $node) {
        if (
$this->canUseSemicolonNamespaces) {
            return 
'namespace ' . $this->p($node->name) . ';'
                 
. $this->nl . $this->pStmts($node->stmts, false);
        } else {
            return 
'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '')
                 . 
' {' . $this->pStmts($node->stmts) . $this->nl . '}';
        }
    }

    protected function 
pStmt_Use(StmtUse_ $node) {
        return 
'use ' . $this->pUseType($node->type)
             . 
$this->pCommaSeparated($node->uses) . ';';
    }

    protected function 
pStmt_GroupUse(StmtGroupUse $node) {
        return 
'use ' . $this->pUseType($node->type) . $this->pName($node->prefix)
             . 
'{' . $this->pCommaSeparated($node->uses) . '};';
    }

    protected function 
pStmt_UseUse(StmtUseUse $node) {
        return 
$this->pUseType($node->type) . $this->p($node->name)
             . (
null !== $node->alias ? ' as ' . $node->alias : '');
    }

    protected function 
pUseType($type) {
        return 
$type === StmtUse_::TYPE_FUNCTION ? 'function '
            
: ($type === StmtUse_::TYPE_CONSTANT ? 'const ' : '');
    }

    protected function 
pStmt_Interface(StmtInterface_ $node) {
        return 
$this->pAttrGroups($node->attrGroups)
             . 
'interface ' . $node->name
             
. (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
             . 
$this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_Enum(StmtEnum_ $node) {
        return 
$this->pAttrGroups($node->attrGroups)
             . 
'enum ' . $node->name
             
. ($node->scalarType ? " : $node->scalarType" : '')
             . (!empty(
$node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
             . 
$this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_Class(StmtClass_ $node) {
        return 
$this->pClassCommon($node, ' ' . $node->name);
    }

    protected function 
pStmt_Trait(StmtTrait_ $node) {
        return 
$this->pAttrGroups($node->attrGroups)
             . 
'trait ' . $node->name
             
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_EnumCase(StmtEnumCase $node) {
        return 
$this->pAttrGroups($node->attrGroups)
             . 
'case ' . $node->name
             
. ($node->expr ? ' = ' . $this->p($node->expr) : '')
             . 
';';
    }

    protected function 
pStmt_TraitUse(StmtTraitUse $node) {
        return 
'use ' . $this->pCommaSeparated($node->traits)
             . (empty(
$node->adaptations)
                ? 
';'
                
: ' {' . $this->pStmts($node->adaptations) . $this->nl . '}');
    }

    protected function 
pStmt_TraitUseAdaptation_Precedence(StmtTraitUseAdaptationPrecedence $node) {
        return 
$this->p($node->trait) . '::' . $node->method
             
. ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';';
    }

    protected function 
pStmt_TraitUseAdaptation_Alias(StmtTraitUseAdaptationAlias $node) {
        return (
null !== $node->trait ? $this->p($node->trait) . '::' : '')
             . 
$node->method . ' as'
             
. (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '')
             . (
null !== $node->newName     ? ' ' . $node->newName                        : '')
             . 
';';
    }

    protected function 
pStmt_Property(StmtProperty $node) {
        return 
$this->pAttrGroups($node->attrGroups)
            . (
0 === $node->flags ? 'var ' : $this->pModifiers($node->flags))
            . (
$node->type ? $this->p($node->type) . ' ' : '')
            . 
$this->pCommaSeparated($node->props) . ';';
    }

    protected function 
pStmt_PropertyProperty(StmtPropertyProperty $node) {
        return 
'$' . $node->name
             
. (null !== $node->default ? ' = ' . $this->p($node->default) : '');
    }

    protected function 
pStmt_ClassMethod(StmtClassMethod $node) {
        return 
$this->pAttrGroups($node->attrGroups)
             . 
$this->pModifiers($node->flags)
             . 
'function ' . ($node->byRef ? '&' : '') . $node->name
             
. '(' . $this->pMaybeMultiline($node->params) . ')'
             
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
             . (
null !== $node->stmts
                
? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'
                
: ';');
    }

    protected function 
pStmt_ClassConst(StmtClassConst $node) {
        return 
$this->pAttrGroups($node->attrGroups)
             . 
$this->pModifiers($node->flags)
             . 
'const ' . $this->pCommaSeparated($node->consts) . ';';
    }

    protected function 
pStmt_Function(StmtFunction_ $node) {
        return 
$this->pAttrGroups($node->attrGroups)
             . 
'function ' . ($node->byRef ? '&' : '') . $node->name
             
. '(' . $this->pCommaSeparated($node->params) . ')'
             
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
             . 
$this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_Const(StmtConst_ $node) {
        return 
'const ' . $this->pCommaSeparated($node->consts) . ';';
    }

    protected function 
pStmt_Declare(StmtDeclare_ $node) {
        return 
'declare (' . $this->pCommaSeparated($node->declares) . ')'
             
. (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';');
    }

    protected function 
pStmt_DeclareDeclare(StmtDeclareDeclare $node) {
        return 
$node->key . '=' . $this->p($node->value);
    }

    
// Control flow

    
protected function pStmt_If(StmtIf_ $node) {
        return 
'if (' . $this->p($node->cond) . ') {'
             
. $this->pStmts($node->stmts) . $this->nl . '}'
             
. ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '')
             . (
null !== $node->else ? ' ' . $this->p($node->else) : '');
    }

    protected function 
pStmt_ElseIf(StmtElseIf_ $node) {
        return 
'elseif (' . $this->p($node->cond) . ') {'
             
. $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_Else(StmtElse_ $node) {
        return 
'else {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_For(StmtFor_ $node) {
        return 
'for ('
             
. $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '')
             . 
$this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '')
             . 
$this->pCommaSeparated($node->loop)
             . 
') {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_Foreach(StmtForeach_ $node) {
        return 
'foreach (' . $this->p($node->expr) . ' as '
             
. (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
             . (
$node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
             
. $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_While(StmtWhile_ $node) {
        return 
'while (' . $this->p($node->cond) . ') {'
             
. $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_Do(StmtDo_ $node) {
        return 
'do {' . $this->pStmts($node->stmts) . $this->nl
             
. '} while (' . $this->p($node->cond) . ');';
    }

    protected function 
pStmt_Switch(StmtSwitch_ $node) {
        return 
'switch (' . $this->p($node->cond) . ') {'
             
. $this->pStmts($node->cases) . $this->nl . '}';
    }

    protected function 
pStmt_Case(StmtCase_ $node) {
        return (
null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
             
. $this->pStmts($node->stmts);
    }

    protected function 
pStmt_TryCatch(StmtTryCatch $node) {
        return 
'try {' . $this->pStmts($node->stmts) . $this->nl . '}'
             
. ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '')
             . (
$node->finally !== null ? ' ' . $this->p($node->finally) : '');
    }

    protected function 
pStmt_Catch(StmtCatch_ $node) {
        return 
'catch (' . $this->pImplode($node->types, '|')
             . (
$node->var !== null ? ' ' . $this->p($node->var) : '')
             . 
') {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_Finally(StmtFinally_ $node) {
        return 
'finally {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pStmt_Break(StmtBreak_ $node) {
        return 
'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
    }

    protected function 
pStmt_Continue(StmtContinue_ $node) {
        return 
'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
    }

    protected function 
pStmt_Return(StmtReturn_ $node) {
        return 
'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';';
    }

    protected function 
pStmt_Throw(StmtThrow_ $node) {
        return 
'throw ' . $this->p($node->expr) . ';';
    }

    protected function 
pStmt_Label(StmtLabel $node) {
        return 
$node->name . ':';
    }

    protected function 
pStmt_Goto(StmtGoto_ $node) {
        return 
'goto ' . $node->name . ';';
    }

    
// Other

    
protected function pStmt_Expression(StmtExpression $node) {
        return 
$this->p($node->expr) . ';';
    }

    protected function 
pStmt_Echo(StmtEcho_ $node) {
        return 
'echo ' . $this->pCommaSeparated($node->exprs) . ';';
    }

    protected function 
pStmt_Static(StmtStatic_ $node) {
        return 
'static ' . $this->pCommaSeparated($node->vars) . ';';
    }

    protected function 
pStmt_Global(StmtGlobal_ $node) {
        return 
'global ' . $this->pCommaSeparated($node->vars) . ';';
    }

    protected function 
pStmt_StaticVar(StmtStaticVar $node) {
        return 
$this->p($node->var)
             . (
null !== $node->default ? ' = ' . $this->p($node->default) : '');
    }

    protected function 
pStmt_Unset(StmtUnset_ $node) {
        return 
'unset(' . $this->pCommaSeparated($node->vars) . ');';
    }

    protected function 
pStmt_InlineHTML(StmtInlineHTML $node) {
        
$newline = $node->getAttribute('hasLeadingNewline', true) ? "n" : '';
        return 
'?>' . $newline . $node->value . '<?php ';
    }

    protected function 
pStmt_HaltCompiler(StmtHaltCompiler $node) {
        return 
'__halt_compiler();' . $node->remaining;
    }

    protected function 
pStmt_Nop(StmtNop $node) {
        return 
'';
    }

    
// Helpers

    
protected function pClassCommon(StmtClass_ $node, $afterClassToken) {
        return 
$this->pAttrGroups($node->attrGroups, $node->name === null)
            . 
$this->pModifiers($node->flags)
            . 
'class' . $afterClassToken
            
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
            . (!empty(
$node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
            . 
$this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function 
pObjectProperty($node) {
        if (
$node instanceof Expr) {
            return 
'{' . $this->p($node) . '}';
        } else {
            return 
$node;
        }
    }

    protected function 
pEncapsList(array $encapsList, $quote) {
        
$return = '';
        foreach (
$encapsList as $element) {
            if (
$element instanceof ScalarEncapsedStringPart) {
                
$return .= $this->escapeString($element->value, $quote);
            } else {
                
$return .= '{' . $this->p($element) . '}';
            }
        }

        return 
$return;
    }

    protected function 
pSingleQuotedString(string $string) {
        return 
''' . addcslashes($string, ''\') . ''';
    }

    protected function 
escapeString($string, $quote) {
        if (
null === $quote) {
            
// For doc strings, don't escape newlines
            
$escaped = addcslashes($string, "tfv$\");
        } else {
            
$escaped = addcslashes($string, "nrtfv$" . $quote . "\");
        }

        // Escape control characters and non-UTF-8 characters.
        // Regex based on https://stackoverflow.com/a/11709412/385378.
        
$regex = '/(
              [x00-x08x0E-x1F] # Control characters
            | [xC0-xC1] # Invalid UTF-8 Bytes
            | [xF5-xFF] # Invalid UTF-8 Bytes
            | xE0(?=[x80-x9F]) # Overlong encoding of prior code point
            | xF0(?=[x80-x8F]) # Overlong encoding of prior code point
            | [xC2-xDF](?![x80-xBF]) # Invalid UTF-8 Sequence Start
            | [xE0-xEF](?![x80-xBF]{2}) # Invalid UTF-8 Sequence Start
            | [xF0-xF4](?![x80-xBF]{3}) # Invalid UTF-8 Sequence Start
            | (?<=[x00-x7FxF5-xFF])[x80-xBF] # Invalid UTF-8 Sequence Middle
            | (?<![xC2-xDF]|[xE0-xEF]|[xE0-xEF][x80-xBF]|[xF0-xF4]|[xF0-xF4][x80-xBF]|[xF0-xF4][x80-xBF]{2})[x80-xBF] # Overlong Sequence
            | (?<=[xE0-xEF])[x80-xBF](?![x80-xBF]) # Short 3 byte sequence
            | (?<=[xF0-xF4])[x80-xBF](?![x80-xBF]{2}) # Short 4 byte sequence
            | (?<=[xF0-xF4][x80-xBF])[x80-xBF](?![x80-xBF]) # Short 4 byte sequence (2)
        )/x';
        return preg_replace_callback(
$regex, function ($matches) {
            assert(strlen(
$matches[0]) === 1);
            
$hex = dechex(ord($matches[0]));;
            return '\x' . str_pad(
$hex, 2, '0', STR_PAD_LEFT);
        }, 
$escaped);
    }

    protected function containsEndLabel(
$string, $label, $atStart = true, $atEnd = true) {
        
$start = $atStart ? '(?:^|[rn])' : '[rn]';
        
$end = $atEnd ? '(?:$|[;rn])' : '[;rn]';
        return false !== strpos(
$string, $label)
            && preg_match('/' . 
$start . $label . $end . '/', $string);
    }

    protected function encapsedContainsEndLabel(array 
$parts, $label) {
        foreach (
$parts as $i => $part) {
            
$atStart = $i === 0;
            
$atEnd = $i === count($parts) - 1;
            if (
$part instanceof ScalarEncapsedStringPart
                && 
$this->containsEndLabel($part->value, $label, $atStart, $atEnd)
            ) {
                return true;
            }
        }
        return false;
    }

    protected function pDereferenceLhs(Node 
$node) {
        if (!
$this->dereferenceLhsRequiresParens($node)) {
            return 
$this->p($node);
        } else  {
            return '(' . 
$this->p($node) . ')';
        }
    }

    protected function pCallLhs(Node 
$node) {
        if (!
$this->callLhsRequiresParens($node)) {
            return 
$this->p($node);
        } else  {
            return '(' . 
$this->p($node) . ')';
        }
    }

    protected function pNewVariable(Node 
$node) {
        // TODO: This is not fully accurate.
        return 
$this->pDereferenceLhs($node);
    }

    /**
     * @param Node[] 
$nodes
     * @return bool
     */
    protected function hasNodeWithComments(array 
$nodes) {
        foreach (
$nodes as $node) {
            if (
$node && $node->getComments()) {
                return true;
            }
        }
        return false;
    }

    protected function pMaybeMultiline(array 
$nodes, bool $trailingComma = false) {
        if (!
$this->hasNodeWithComments($nodes)) {
            return 
$this->pCommaSeparated($nodes);
        } else {
            return 
$this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl;
        }
    }

    protected function pAttrGroups(array 
$nodes, bool $inline = false): string {
        
$result = '';
        
$sep = $inline ? ' ' : $this->nl;
        foreach (
$nodes as $node) {
            
$result .= $this->p($node) . $sep;
        }

        return 
$result;
    }
}
Онлайн: 2
Реклама