Skip to content

Commit

Permalink
Properly handle static deref LHS
Browse files Browse the repository at this point in the history
The rules for static and array/object deref are slightly different:
The former does not allow constants.

(cherry picked from commit 7b4a8c1)
  • Loading branch information
nikic committed Aug 13, 2023
1 parent 8f8e47b commit 21a61ec
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 10 deletions.
14 changes: 11 additions & 3 deletions lib/PhpParser/PrettyPrinter/Standard.php
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ protected function pExpr_NullsafeMethodCall(Expr\NullsafeMethodCall $node) {
}

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)
Expand Down Expand Up @@ -606,7 +606,7 @@ protected function pExpr_ConstFetch(Expr\ConstFetch $node) {
}

protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
return $this->pDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name);
return $this->pStaticDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name);
}

protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
Expand All @@ -618,7 +618,7 @@ protected function pExpr_NullsafePropertyFetch(Expr\NullsafePropertyFetch $node)
}

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) {
Expand Down Expand Up @@ -1069,6 +1069,14 @@ protected function pDereferenceLhs(Node $node) {
}
}

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);
Expand Down
30 changes: 25 additions & 5 deletions lib/PhpParser/PrettyPrinterAbstract.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ abstract class PrettyPrinterAbstract
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]
Expand Down Expand Up @@ -978,6 +979,13 @@ protected function pFixup(int $fixup, Node $subNode, $parentClass, int $subStart
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)) {
Expand Down Expand Up @@ -1054,13 +1062,26 @@ protected function callLhsRequiresParens(Node $node) : bool {
}

/**
* 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
Expand All @@ -1073,7 +1094,6 @@ protected function dereferenceLhsRequiresParens(Node $node) : bool {
|| $node instanceof Expr\StaticCall
|| $node instanceof Expr\Array_
|| $node instanceof Scalar\String_
|| $node instanceof Expr\ConstFetch
|| $node instanceof Expr\ClassConstFetch);
}

Expand Down Expand Up @@ -1208,9 +1228,9 @@ protected function initializeFixupMap() {
],

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\ClassConstFetch::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
Expr\New_::class => ['class' => self::FIXUP_NEW],
Expr\MethodCall::class => [
'var' => self::FIXUP_DEREF_LHS,
Expand All @@ -1221,7 +1241,7 @@ protected function initializeFixupMap() {
'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 => [
Expand Down
9 changes: 9 additions & 0 deletions test/code/formatPreservation/fixup.test
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ self :: $foo;
self :: $foo;
new Foo();
$x instanceof Foo;
Foo :: bar;
Foo :: $bar;
Foo :: bar();
-----
$stmts[0]->expr->name = new Expr\Variable('a');
$stmts[1]->expr->name = new Expr\BinaryOp\Concat(new Expr\Variable('a'), new Expr\Variable('b'));
Expand All @@ -58,6 +61,9 @@ $stmts[7]->expr->name = new Node\VarLikeIdentifier('bar');
$stmts[8]->expr->name = new Expr\BinaryOp\Concat(new Expr\Variable('a'), new Expr\Variable('b'));
$stmts[9]->expr->class = new Scalar\String_('Foo');
$stmts[10]->expr->class = new Scalar\String_('Foo');
$stmts[11]->expr->class = new Expr\ConstFetch(new Node\Name('FOO'));
$stmts[12]->expr->class = new Expr\ConstFetch(new Node\Name('FOO'));
$stmts[13]->expr->class = new Expr\ConstFetch(new Node\Name('FOO'));
-----
<?php
$a ();
Expand All @@ -71,3 +77,6 @@ self :: $bar;
self :: ${$a . $b};
new ('Foo')();
$x instanceof ('Foo');
(FOO) :: bar;
(FOO) :: $bar;
(FOO) :: bar();
2 changes: 1 addition & 1 deletion test/code/prettyPrinter/expr/newVariable.test
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ new (foo())();
new ('foo')();
new (x[0])();
new (x->y)();
new (x::$y)();
new ((x)::$y)();
$x instanceof ('a' . 'b');
$x instanceof ($y++);
8 changes: 7 additions & 1 deletion test/code/prettyPrinter/expr/uvs.test
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ A::$$b[$c]();
($a->b)();
(A::$b)();
('a' . 'b')::X;
(A)::X;
(A)::$x;
(A)::x();
-----
!!php7
(function () {
Expand All @@ -22,4 +25,7 @@ $A::{$b[$c]}();
A::${$b}[$c]();
($a->b)();
(A::$b)();
('a' . 'b')::X;
('a' . 'b')::X;
(A)::X;
(A)::$x;
(A)::x();

0 comments on commit 21a61ec

Please sign in to comment.