N°5809 Update nikic/php-parser from 4.14.0 to 4.18.0

This commit is contained in:
Pierre Goiffon
2024-01-25 17:33:51 +01:00
parent 38cdcf4f61
commit e1296105f9
41 changed files with 3296 additions and 5696 deletions

View File

@@ -19,6 +19,8 @@ class ClassConst implements PhpParser\Builder
/** @var Node\AttributeGroup[] */
protected $attributeGroups = [];
/** @var Identifier|Node\Name|Node\ComplexType */
protected $type;
/**
* Creates a class constant builder
@@ -116,6 +118,19 @@ class ClassConst implements PhpParser\Builder
return $this;
}
/**
* Sets the constant type.
*
* @param string|Node\Name|Identifier|Node\ComplexType $type
*
* @return $this
*/
public function setType($type) {
$this->type = BuilderHelpers::normalizeType($type);
return $this;
}
/**
* Returns the built class node.
*
@@ -126,7 +141,8 @@ class ClassConst implements PhpParser\Builder
$this->constants,
$this->flags,
$this->attributes,
$this->attributeGroups
$this->attributeGroups,
$this->type
);
}
}

View File

@@ -78,8 +78,8 @@ class EnumCase implements PhpParser\Builder
return new Stmt\EnumCase(
$this->name,
$this->value,
$this->attributes,
$this->attributeGroups
$this->attributeGroups,
$this->attributes
);
}
}

View File

@@ -19,6 +19,8 @@ class Param implements PhpParser\Builder
protected $variadic = false;
protected $flags = 0;
/** @var Node\AttributeGroup[] */
protected $attributeGroups = [];
@@ -95,6 +97,50 @@ class Param implements PhpParser\Builder
return $this;
}
/**
* Makes the (promoted) parameter public.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC);
return $this;
}
/**
* Makes the (promoted) parameter protected.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED);
return $this;
}
/**
* Makes the (promoted) parameter private.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE);
return $this;
}
/**
* Makes the (promoted) parameter readonly.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeReadonly() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY);
return $this;
}
/**
* Adds an attribute group.
*
@@ -116,7 +162,7 @@ class Param implements PhpParser\Builder
public function getNode() : Node {
return new Node\Param(
new Node\Expr\Variable($this->name),
$this->default, $this->type, $this->byRef, $this->variadic, [], 0, $this->attributeGroups
$this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups
);
}
}

View File

@@ -349,15 +349,15 @@ class BuilderFactory
/**
* Creates a class constant fetch node.
*
* @param string|Name|Expr $class Class name
* @param string|Identifier $name Constant name
* @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Constant name
*
* @return Expr\ClassConstFetch
*/
public function classConstFetch($class, $name): Expr\ClassConstFetch {
return new Expr\ClassConstFetch(
BuilderHelpers::normalizeNameOrExpr($class),
BuilderHelpers::normalizeIdentifier($name)
BuilderHelpers::normalizeIdentifierOrExpr($name)
);
}

View File

@@ -178,7 +178,20 @@ final class BuilderHelpers
}
$builtinTypes = [
'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object', 'mixed', 'never',
'array',
'callable',
'bool',
'int',
'float',
'string',
'iterable',
'void',
'object',
'null',
'false',
'mixed',
'never',
'true',
];
$lowerType = strtolower($type);

View File

@@ -19,6 +19,8 @@ class PrintableNewAnonClassNode extends Expr
{
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** @var int Modifiers */
public $flags;
/** @var Node\Arg[] Arguments */
public $args;
/** @var null|Node\Name Name of extended class */
@@ -29,11 +31,12 @@ class PrintableNewAnonClassNode extends Expr
public $stmts;
public function __construct(
array $attrGroups, array $args, Node\Name $extends = null, array $implements,
array $attrGroups, int $flags, array $args, Node\Name $extends = null, array $implements,
array $stmts, array $attributes
) {
parent::__construct($attributes);
$this->attrGroups = $attrGroups;
$this->flags = $flags;
$this->args = $args;
$this->extends = $extends;
$this->implements = $implements;
@@ -46,7 +49,7 @@ class PrintableNewAnonClassNode extends Expr
// We don't assert that $class->name is null here, to allow consumers to assign unique names
// to anonymous classes for their own purposes. We simplify ignore the name here.
return new self(
$class->attrGroups, $newNode->args, $class->extends, $class->implements,
$class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements,
$class->stmts, $newNode->getAttributes()
);
}
@@ -56,6 +59,6 @@ class PrintableNewAnonClassNode extends Expr
}
public function getSubNodeNames() : array {
return ['attrGroups', 'args', 'extends', 'implements', 'stmts'];
return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts'];
}
}

View File

@@ -206,6 +206,11 @@ class TokenStream
|| $this->haveTokenInRange($startPos, $endPos, '}');
}
public function haveTagInRange(int $startPos, int $endPos): bool {
return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
|| $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
}
/**
* Get indentation before token position.
*

View File

@@ -14,6 +14,7 @@ use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
@@ -24,6 +25,7 @@ class Emulative extends Lexer
const PHP_7_4 = '7.4dev';
const PHP_8_0 = '8.0dev';
const PHP_8_1 = '8.1dev';
const PHP_8_2 = '8.2dev';
/** @var mixed[] Patches used to reverse changes introduced in the code */
private $patches = [];
@@ -41,7 +43,7 @@ class Emulative extends Lexer
*/
public function __construct(array $options = [])
{
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_1;
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_2;
unset($options['phpVersion']);
parent::__construct($options);
@@ -57,6 +59,7 @@ class Emulative extends Lexer
new EnumTokenEmulator(),
new ReadonlyTokenEmulator(),
new ExplicitOctalEmulator(),
new ReadonlyFunctionTokenEmulator(),
];
// Collect emulators that are relevant for the PHP version we're running

View File

@@ -33,7 +33,7 @@ abstract class KeywordEmulator extends TokenEmulator
/**
* @param mixed[] $tokens
* @return mixed[]|null
* @return array|string|null
*/
private function getPreviousNonSpaceToken(array $tokens, int $start)
{

View File

@@ -0,0 +1,31 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
/*
* In PHP 8.1, "readonly(" was special cased in the lexer in order to support functions with
* name readonly. In PHP 8.2, this may conflict with readonly properties having a DNF type. For
* this reason, PHP 8.2 instead treats this as T_READONLY and then handles it specially in the
* parser. This emulator only exists to handle this special case, which is skipped by the
* PHP 8.1 ReadonlyTokenEmulator.
*/
class ReadonlyFunctionTokenEmulator extends KeywordEmulator {
public function getKeywordString(): string {
return 'readonly';
}
public function getKeywordToken(): int {
return \T_READONLY;
}
public function getPhpVersion(): string {
return Emulative::PHP_8_2;
}
public function reverseEmulate(string $code, array $tokens): array {
// Don't bother
return $tokens;
}
}

View File

@@ -20,4 +20,17 @@ final class ReadonlyTokenEmulator extends KeywordEmulator
{
return \T_READONLY;
}
}
protected function isKeywordContext(array $tokens, int $pos): bool
{
if (!parent::isKeywordContext($tokens, $pos)) {
return false;
}
// Support "function readonly("
return !(isset($tokens[$pos + 1]) &&
($tokens[$pos + 1][0] === '(' ||
($tokens[$pos + 1][0] === \T_WHITESPACE &&
isset($tokens[$pos + 2]) &&
$tokens[$pos + 2][0] === '(')));
}
}

View File

@@ -10,15 +10,15 @@ class ClassConstFetch extends Expr
{
/** @var Name|Expr Class name */
public $class;
/** @var Identifier|Error Constant name */
/** @var Identifier|Expr|Error Constant name */
public $name;
/**
* Constructs a class const fetch node.
*
* @param Name|Expr $class Class name
* @param string|Identifier|Error $name Constant name
* @param array $attributes Additional attributes
* @param Name|Expr $class Class name
* @param string|Identifier|Expr|Error $name Constant name
* @param array $attributes Additional attributes
*/
public function __construct($class, $name, array $attributes = []) {
$this->attributes = $attributes;
@@ -29,7 +29,7 @@ class ClassConstFetch extends Expr
public function getSubNodeNames() : array {
return ['class', 'name'];
}
public function getType() : string {
return 'Expr_ClassConstFetch';
}

View File

@@ -6,7 +6,10 @@ use PhpParser\NodeAbstract;
class Name extends NodeAbstract
{
/** @var string[] Parts of the name */
/**
* @var string[] Parts of the name
* @deprecated Use getParts() instead
*/
public $parts;
private static $specialClassNames = [
@@ -30,6 +33,15 @@ class Name extends NodeAbstract
return ['parts'];
}
/**
* Get parts of name (split by the namespace separator).
*
* @return string[] Parts of name
*/
public function getParts(): array {
return $this->parts;
}
/**
* Gets the first part of the name, i.e. everything before the first namespace separator.
*
@@ -162,7 +174,7 @@ class Name extends NodeAbstract
$realLength = $numParts - $realOffset;
} else {
$realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
if ($realLength < 0 || $realLength > $numParts) {
if ($realLength < 0 || $realLength > $numParts - $realOffset) {
throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
}
}

View File

@@ -47,13 +47,7 @@ class DNumber extends Scalar
public static function parse(string $str) : float {
$str = str_replace('_', '', $str);
// if string contains any of .eE just cast it to float
if (false !== strpbrk($str, '.eE')) {
return (float) $str;
}
// otherwise it's an integer notation that overflowed into a float
// if it starts with 0 it's one of the special integer notations
// Check whether this is one of the special integer notations.
if ('0' === $str[0]) {
// hex
if ('x' === $str[1] || 'X' === $str[1]) {
@@ -65,10 +59,12 @@ class DNumber extends Scalar
return bindec($str);
}
// oct
// substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit (8 or 9)
// so that only the digits before that are used
return octdec(substr($str, 0, strcspn($str, '89')));
// oct, but only if the string does not contain any of '.eE'.
if (false === strpbrk($str, '.eE')) {
// substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit
// (8 or 9) so that only the digits before that are used.
return octdec(substr($str, 0, strcspn($str, '89')));
}
}
// dec

View File

@@ -10,31 +10,36 @@ class ClassConst extends Node\Stmt
public $flags;
/** @var Node\Const_[] Constant declarations */
public $consts;
/** @var Node\AttributeGroup[] */
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** @var Node\Identifier|Node\Name|Node\ComplexType|null Type declaration */
public $type;
/**
* Constructs a class const list node.
*
* @param Node\Const_[] $consts Constant declarations
* @param int $flags Modifiers
* @param array $attributes Additional attributes
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
* @param Node\Const_[] $consts Constant declarations
* @param int $flags Modifiers
* @param array $attributes Additional attributes
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
* @param null|string|Node\Identifier|Node\Name|Node\ComplexType $type Type declaration
*/
public function __construct(
array $consts,
int $flags = 0,
array $attributes = [],
array $attrGroups = []
array $attrGroups = [],
$type = null
) {
$this->attributes = $attributes;
$this->flags = $flags;
$this->consts = $consts;
$this->attrGroups = $attrGroups;
$this->type = \is_string($type) ? new Node\Identifier($type) : $type;
}
public function getSubNodeNames() : array {
return ['attrGroups', 'flags', 'consts'];
return ['attrGroups', 'flags', 'type', 'consts'];
}
/**

View File

@@ -23,21 +23,23 @@ class ClassMethod extends Node\Stmt implements FunctionLike
public $attrGroups;
private static $magicNames = [
'__construct' => true,
'__destruct' => true,
'__call' => true,
'__callstatic' => true,
'__get' => true,
'__set' => true,
'__isset' => true,
'__unset' => true,
'__sleep' => true,
'__wakeup' => true,
'__tostring' => true,
'__set_state' => true,
'__clone' => true,
'__invoke' => true,
'__debuginfo' => true,
'__construct' => true,
'__destruct' => true,
'__call' => true,
'__callstatic' => true,
'__get' => true,
'__set' => true,
'__isset' => true,
'__unset' => true,
'__sleep' => true,
'__wakeup' => true,
'__tostring' => true,
'__set_state' => true,
'__clone' => true,
'__invoke' => true,
'__debuginfo' => true,
'__serialize' => true,
'__unserialize' => true,
];
/**

View File

@@ -4,13 +4,13 @@ namespace PhpParser\Node;
class UnionType extends ComplexType
{
/** @var (Identifier|Name)[] Types */
/** @var (Identifier|Name|IntersectionType)[] Types */
public $types;
/**
* Constructs a union type.
*
* @param (Identifier|Name)[] $types Types
* @param (Identifier|Name|IntersectionType)[] $types Types
* @param array $attributes Additional attributes
*/
public function __construct(array $types, array $attributes = []) {

View File

@@ -118,6 +118,9 @@ class NameResolver extends NodeVisitorAbstract
$this->addNamespacedName($const);
}
} else if ($node instanceof Stmt\ClassConst) {
if (null !== $node->type) {
$node->type = $this->resolveType($node->type);
}
$this->resolveAttrGroups($node);
} else if ($node instanceof Stmt\EnumCase) {
$this->resolveAttrGroups($node);
@@ -161,7 +164,7 @@ class NameResolver extends NodeVisitorAbstract
return null;
}
private function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) {
private function addAlias(Stmt\UseUse $use, int $type, Name $prefix = null) {
// Add prefix for group uses
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
// Type is determined either by individual element or whole use declaration

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -16,9 +16,12 @@ use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\ElseIf_;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Nop;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\TryCatch;
use PhpParser\Node\Stmt\UseUse;
@@ -664,6 +667,7 @@ abstract class ParserAbstract implements Parser
'false' => true,
'mixed' => true,
'never' => true,
'true' => true,
];
if (!$name->isUnqualified()) {
@@ -875,6 +879,24 @@ abstract class ParserAbstract implements Parser
return $attributes;
}
/** @param ElseIf_|Else_ $node */
protected function fixupAlternativeElse($node) {
// Make sure a trailing nop statement carrying comments is part of the node.
$numStmts = \count($node->stmts);
if ($numStmts !== 0 && $node->stmts[$numStmts - 1] instanceof Nop) {
$nopAttrs = $node->stmts[$numStmts - 1]->getAttributes();
if (isset($nopAttrs['endLine'])) {
$node->setAttribute('endLine', $nopAttrs['endLine']);
}
if (isset($nopAttrs['endFilePos'])) {
$node->setAttribute('endFilePos', $nopAttrs['endFilePos']);
}
if (isset($nopAttrs['endTokenPos'])) {
$node->setAttribute('endTokenPos', $nopAttrs['endTokenPos']);
}
}
}
protected function checkClassModifier($a, $b, $modifierPos) {
try {
Class_::verifyClassModifier($a, $b);

View File

@@ -2,6 +2,9 @@
namespace PhpParser;
use PhpParser\Lexer\Emulative;
use PhpParser\Parser\Php7;
class ParserFactory
{
const PREFER_PHP7 = 1;
@@ -41,4 +44,33 @@ class ParserFactory
);
}
}
/**
* Create a parser targeting the newest version supported by this library. Code for older
* versions will be accepted if there have been no relevant backwards-compatibility breaks in
* PHP.
*
* All supported lexer attributes (comments, startLine, endLine, startTokenPos, endTokenPos,
* startFilePos, endFilePos) will be enabled.
*/
public function createForNewestSupportedVersion(): Parser {
return new Php7(new Emulative($this->getLexerOptions()));
}
/**
* Create a parser targeting the host PHP version, that is the PHP version we're currently
* running on. This parser will not use any token emulation.
*
* All supported lexer attributes (comments, startLine, endLine, startTokenPos, endTokenPos,
* startFilePos, endFilePos) will be enabled.
*/
public function createForHostVersion(): Parser {
return new Php7(new Lexer($this->getLexerOptions()));
}
private function getLexerOptions(): array {
return ['usedAttributes' => [
'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos', 'startFilePos', 'endFilePos',
]];
}
}

View File

@@ -46,7 +46,15 @@ class Standard extends PrettyPrinterAbstract
}
protected function pUnionType(Node\UnionType $node) {
return $this->pImplode($node->types, '|');
$types = [];
foreach ($node->types as $typeNode) {
if ($typeNode instanceof Node\IntersectionType) {
$types[] = '('. $this->p($typeNode) . ')';
continue;
}
$types[] = $this->p($typeNode);
}
return implode('|', $types);
}
protected function pIntersectionType(Node\IntersectionType $node) {
@@ -521,7 +529,7 @@ class Standard extends PrettyPrinterAbstract
}
protected function pExpr_StaticCall(Expr\StaticCall $node) {
return $this->pDereferenceLhs($node->class) . '::'
return $this->pStaticDereferenceLhs($node->class) . '::'
. ($node->name instanceof Expr
? ($node->name instanceof Expr\Variable
? $this->p($node->name)
@@ -598,7 +606,7 @@ class Standard extends PrettyPrinterAbstract
}
protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
return $this->pDereferenceLhs($node->class) . '::' . $this->p($node->name);
return $this->pStaticDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name);
}
protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
@@ -610,7 +618,7 @@ class Standard extends PrettyPrinterAbstract
}
protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) {
return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
return $this->pStaticDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
}
protected function pExpr_ShellExec(Expr\ShellExec $node) {
@@ -806,7 +814,9 @@ class Standard extends PrettyPrinterAbstract
protected function pStmt_ClassConst(Stmt\ClassConst $node) {
return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags)
. 'const ' . $this->pCommaSeparated($node->consts) . ';';
. 'const '
. (null !== $node->type ? $this->p($node->type) . ' ' : '')
. $this->pCommaSeparated($node->consts) . ';';
}
protected function pStmt_Function(Stmt\Function_ $node) {
@@ -1059,6 +1069,14 @@ class Standard extends PrettyPrinterAbstract
}
}
protected function pStaticDereferenceLhs(Node $node) {
if (!$this->staticDereferenceLhsRequiresParens($node)) {
return $this->p($node);
} else {
return '(' . $this->p($node) . ')';
}
}
protected function pCallLhs(Node $node) {
if (!$this->callLhsRequiresParens($node)) {
return $this->p($node);
@@ -1067,9 +1085,12 @@ class Standard extends PrettyPrinterAbstract
}
}
protected function pNewVariable(Node $node) {
// TODO: This is not fully accurate.
return $this->pDereferenceLhs($node);
protected function pNewVariable(Node $node): string {
if (!$this->newOperandRequiresParens($node)) {
return $this->p($node);
} else {
return '(' . $this->p($node) . ')';
}
}
/**

View File

@@ -21,6 +21,8 @@ abstract class PrettyPrinterAbstract
const FIXUP_BRACED_NAME = 4; // Name operand that may require bracing
const FIXUP_VAR_BRACED_NAME = 5; // Name operand that may require ${} bracing
const FIXUP_ENCAPSED = 6; // Encapsed string part
const FIXUP_NEW = 7; // New/instanceof operand
const FIXUP_STATIC_DEREF_LHS = 8; // LHS of static dereferencing operation
protected $precedenceMap = [
// [precedence, associativity]
@@ -774,7 +776,8 @@ abstract class PrettyPrinterAbstract
}
if ($skipRemovedNode) {
if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) {
if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
$this->origTokens->haveTagInRange($pos, $itemStartPos))) {
// We'd remove the brace of a code block.
// TODO: Preserve formatting.
$this->setIndentLevel($origIndentLevel);
@@ -877,7 +880,8 @@ abstract class PrettyPrinterAbstract
$pos, $itemStartPos, $indentAdjustment);
$skipRemovedNode = true;
} else {
if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) {
if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
$this->origTokens->haveTagInRange($pos, $itemStartPos))) {
// We'd remove the brace of a code block.
// TODO: Preserve formatting.
return null;
@@ -923,11 +927,14 @@ abstract class PrettyPrinterAbstract
foreach ($delayedAdd as $delayedAddNode) {
if (!$first) {
$result .= $insertStr;
if ($insertNewline) {
$result .= $this->nl;
}
}
$result .= $this->p($delayedAddNode, true);
$first = false;
}
$result .= $extraRight;
$result .= $extraRight === "\n" ? $this->nl : $extraRight;
}
return $result;
@@ -972,6 +979,19 @@ abstract class PrettyPrinterAbstract
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_STATIC_DEREF_LHS:
if ($this->staticDereferenceLhsRequiresParens($subNode)
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
) {
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_NEW:
if ($this->newOperandRequiresParens($subNode)
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_BRACED_NAME:
case self::FIXUP_VAR_BRACED_NAME:
if ($subNode instanceof Expr
@@ -1042,13 +1062,26 @@ abstract class PrettyPrinterAbstract
}
/**
* Determines whether the LHS of a dereferencing operation must be wrapped in parenthesis.
* Determines whether the LHS of an array/object operation must be wrapped in parentheses.
*
* @param Node $node LHS of dereferencing operation
*
* @return bool Whether parentheses are required
*/
protected function dereferenceLhsRequiresParens(Node $node) : bool {
// A constant can occur on the LHS of an array/object deref, but not a static deref.
return $this->staticDereferenceLhsRequiresParens($node)
&& !$node instanceof Expr\ConstFetch;
}
/**
* Determines whether the LHS of a static operation must be wrapped in parentheses.
*
* @param Node $node LHS of dereferencing operation
*
* @return bool Whether parentheses are required
*/
protected function staticDereferenceLhsRequiresParens(Node $node): bool {
return !($node instanceof Expr\Variable
|| $node instanceof Node\Name
|| $node instanceof Expr\ArrayDimFetch
@@ -1061,10 +1094,31 @@ abstract class PrettyPrinterAbstract
|| $node instanceof Expr\StaticCall
|| $node instanceof Expr\Array_
|| $node instanceof Scalar\String_
|| $node instanceof Expr\ConstFetch
|| $node instanceof Expr\ClassConstFetch);
}
/**
* Determines whether an expression used in "new" or "instanceof" requires parentheses.
*
* @param Node $node New or instanceof operand
*
* @return bool Whether parentheses are required
*/
protected function newOperandRequiresParens(Node $node): bool {
if ($node instanceof Node\Name || $node instanceof Expr\Variable) {
return false;
}
if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch ||
$node instanceof Expr\NullsafePropertyFetch
) {
return $this->newOperandRequiresParens($node->var);
}
if ($node instanceof Expr\StaticPropertyFetch) {
return $this->newOperandRequiresParens($node->class);
}
return true;
}
/**
* Print modifiers, including trailing whitespace.
*
@@ -1166,7 +1220,7 @@ abstract class PrettyPrinterAbstract
Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT],
Expr\Instanceof_::class => [
'expr' => self::FIXUP_PREC_LEFT,
'class' => self::FIXUP_PREC_RIGHT, // TODO: FIXUP_NEW_VARIABLE
'class' => self::FIXUP_NEW,
],
Expr\Ternary::class => [
'cond' => self::FIXUP_PREC_LEFT,
@@ -1174,10 +1228,13 @@ abstract class PrettyPrinterAbstract
],
Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
Expr\StaticCall::class => ['class' => self::FIXUP_DEREF_LHS],
Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
Expr\ClassConstFetch::class => ['var' => self::FIXUP_DEREF_LHS],
Expr\New_::class => ['class' => self::FIXUP_DEREF_LHS], // TODO: FIXUP_NEW_VARIABLE
Expr\ClassConstFetch::class => [
'class' => self::FIXUP_STATIC_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
],
Expr\New_::class => ['class' => self::FIXUP_NEW],
Expr\MethodCall::class => [
'var' => self::FIXUP_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
@@ -1187,7 +1244,7 @@ abstract class PrettyPrinterAbstract
'name' => self::FIXUP_BRACED_NAME,
],
Expr\StaticPropertyFetch::class => [
'class' => self::FIXUP_DEREF_LHS,
'class' => self::FIXUP_STATIC_DEREF_LHS,
'name' => self::FIXUP_VAR_BRACED_NAME,
],
Expr\PropertyFetch::class => [
@@ -1273,6 +1330,7 @@ abstract class PrettyPrinterAbstract
'Param->default' => $stripEquals,
'Stmt_Break->num' => $stripBoth,
'Stmt_Catch->var' => $stripLeft,
'Stmt_ClassConst->type' => $stripRight,
'Stmt_ClassMethod->returnType' => $stripColon,
'Stmt_Class->extends' => ['left' => \T_EXTENDS],
'Stmt_Enum->scalarType' => $stripColon,
@@ -1314,6 +1372,7 @@ abstract class PrettyPrinterAbstract
'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
'Stmt_Catch->var' => [null, false, ' ', null],
'Stmt_ClassMethod->returnType' => [')', false, ' : ', null],
'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
'Stmt_Class->extends' => [null, false, ' extends ', null],
'Stmt_Enum->scalarType' => [null, false, ' : ', null],
'Stmt_EnumCase->expr' => [null, false, ' = ', null],
@@ -1454,6 +1513,16 @@ abstract class PrettyPrinterAbstract
'Stmt_ClassMethod->params' => ['(', '', ''],
'Stmt_Interface->extends' => [null, ' extends ', ''],
'Stmt_Function->params' => ['(', '', ''],
'Stmt_Interface->attrGroups' => [null, '', "\n"],
'Stmt_Class->attrGroups' => [null, '', "\n"],
'Stmt_ClassConst->attrGroups' => [null, '', "\n"],
'Stmt_ClassMethod->attrGroups' => [null, '', "\n"],
'Stmt_Function->attrGroups' => [null, '', "\n"],
'Stmt_Property->attrGroups' => [null, '', "\n"],
'Stmt_Trait->attrGroups' => [null, '', "\n"],
'Expr_ArrowFunction->attrGroups' => [null, '', ' '],
'Expr_Closure->attrGroups' => [null, '', ' '],
'Expr_PrintableNewAnonClass->attrGroups' => [\T_NEW, ' ', ''],
/* These cannot be empty to start with:
* Expr_Isset->vars
@@ -1493,6 +1562,7 @@ abstract class PrettyPrinterAbstract
'Stmt_ClassMethod->flags' => \T_FUNCTION,
'Stmt_Class->flags' => \T_CLASS,
'Stmt_Property->flags' => \T_VARIABLE,
'Expr_PrintableNewAnonClass->flags' => \T_CLASS,
'Param->flags' => \T_VARIABLE,
//'Stmt_TraitUseAdaptation_Alias->newModifier' => 0, // TODO
];