mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-01-16 14:48:32 +01:00
Fix PropertyHook::getStmts() for set hook
Produce the correct desugaring if the propertyName attribute is set, and set it in the parser. Otherwise throw an exception. Fixes #1053.
This commit is contained in:
parent
8bb415902e
commit
6478c5ac53
@ -686,11 +686,13 @@ parameter:
|
|||||||
optional_attributes optional_property_modifiers optional_type_without_static
|
optional_attributes optional_property_modifiers optional_type_without_static
|
||||||
optional_arg_ref optional_ellipsis plain_variable optional_property_hook_list
|
optional_arg_ref optional_ellipsis plain_variable optional_property_hook_list
|
||||||
{ $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1, $7);
|
{ $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1, $7);
|
||||||
$this->checkParam($$); }
|
$this->checkParam($$);
|
||||||
|
$this->addPropertyNameToHooks($$); }
|
||||||
| optional_attributes optional_property_modifiers optional_type_without_static
|
| optional_attributes optional_property_modifiers optional_type_without_static
|
||||||
optional_arg_ref optional_ellipsis plain_variable '=' expr optional_property_hook_list
|
optional_arg_ref optional_ellipsis plain_variable '=' expr optional_property_hook_list
|
||||||
{ $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1, $9);
|
{ $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1, $9);
|
||||||
$this->checkParam($$); }
|
$this->checkParam($$);
|
||||||
|
$this->addPropertyNameToHooks($$); }
|
||||||
| optional_attributes optional_property_modifiers optional_type_without_static
|
| optional_attributes optional_property_modifiers optional_type_without_static
|
||||||
optional_arg_ref optional_ellipsis error
|
optional_arg_ref optional_ellipsis error
|
||||||
{ $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
|
{ $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
|
||||||
@ -841,7 +843,8 @@ class_statement:
|
|||||||
| optional_attributes variable_modifiers optional_type_without_static property_declaration_list '{' property_hook_list '}'
|
| optional_attributes variable_modifiers optional_type_without_static property_declaration_list '{' property_hook_list '}'
|
||||||
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1, $6);
|
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1, $6);
|
||||||
$this->checkPropertyHooksForMultiProperty($$, #5);
|
$this->checkPropertyHooksForMultiProperty($$, #5);
|
||||||
$this->checkEmptyPropertyHookList($6, #5); }
|
$this->checkEmptyPropertyHookList($6, #5);
|
||||||
|
$this->addPropertyNameToHooks($$); }
|
||||||
#endif
|
#endif
|
||||||
| optional_attributes method_modifiers T_CONST class_const_list semi
|
| optional_attributes method_modifiers T_CONST class_const_list semi
|
||||||
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
|
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
namespace PhpParser\Node;
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
use PhpParser\Modifiers;
|
use PhpParser\Modifiers;
|
||||||
|
use PhpParser\Node\Expr\Assign;
|
||||||
|
use PhpParser\Node\Expr\PropertyFetch;
|
||||||
|
use PhpParser\Node\Expr\Variable;
|
||||||
use PhpParser\Node\Stmt\Expression;
|
use PhpParser\Node\Stmt\Expression;
|
||||||
use PhpParser\Node\Stmt\Return_;
|
use PhpParser\Node\Stmt\Return_;
|
||||||
use PhpParser\NodeAbstract;
|
use PhpParser\NodeAbstract;
|
||||||
@ -74,8 +77,14 @@ class PropertyHook extends NodeAbstract implements FunctionLike {
|
|||||||
return [new Return_($this->body)];
|
return [new Return_($this->body)];
|
||||||
}
|
}
|
||||||
if ($name === 'set') {
|
if ($name === 'set') {
|
||||||
// TODO: This should generate $this->prop = $expr, but we don't know the property name.
|
if (!$this->hasAttribute('propertyName')) {
|
||||||
return [new Expression($this->body)];
|
throw new \LogicException(
|
||||||
|
'Can only use getStmts() on a "set" hook if the "propertyName" attribute is set');
|
||||||
|
}
|
||||||
|
|
||||||
|
$propName = $this->getAttribute('propertyName');
|
||||||
|
$prop = new PropertyFetch(new Variable('this'), (string) $propName);
|
||||||
|
return [new Expression(new Assign($prop, $this->body))];
|
||||||
}
|
}
|
||||||
throw new \LogicException('Unknown property hook "' . $name . '"');
|
throw new \LogicException('Unknown property hook "' . $name . '"');
|
||||||
}
|
}
|
||||||
|
@ -1844,10 +1844,12 @@ class Php7 extends \PhpParser\ParserAbstract
|
|||||||
290 => static function ($self, $stackPos) {
|
290 => static function ($self, $stackPos) {
|
||||||
$self->semValue = new Node\Param($self->semStack[$stackPos-(7-6)], null, $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-4)], $self->semStack[$stackPos-(7-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-7)]);
|
$self->semValue = new Node\Param($self->semStack[$stackPos-(7-6)], null, $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-4)], $self->semStack[$stackPos-(7-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-7)]);
|
||||||
$self->checkParam($self->semValue);
|
$self->checkParam($self->semValue);
|
||||||
|
$self->addPropertyNameToHooks($self->semValue);
|
||||||
},
|
},
|
||||||
291 => static function ($self, $stackPos) {
|
291 => static function ($self, $stackPos) {
|
||||||
$self->semValue = new Node\Param($self->semStack[$stackPos-(9-6)], $self->semStack[$stackPos-(9-8)], $self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-4)], $self->semStack[$stackPos-(9-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(9-2)], $self->semStack[$stackPos-(9-1)], $self->semStack[$stackPos-(9-9)]);
|
$self->semValue = new Node\Param($self->semStack[$stackPos-(9-6)], $self->semStack[$stackPos-(9-8)], $self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-4)], $self->semStack[$stackPos-(9-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(9-2)], $self->semStack[$stackPos-(9-1)], $self->semStack[$stackPos-(9-9)]);
|
||||||
$self->checkParam($self->semValue);
|
$self->checkParam($self->semValue);
|
||||||
|
$self->addPropertyNameToHooks($self->semValue);
|
||||||
},
|
},
|
||||||
292 => static function ($self, $stackPos) {
|
292 => static function ($self, $stackPos) {
|
||||||
$self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
|
$self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
|
||||||
|
@ -1839,10 +1839,12 @@ class Php8 extends \PhpParser\ParserAbstract
|
|||||||
290 => static function ($self, $stackPos) {
|
290 => static function ($self, $stackPos) {
|
||||||
$self->semValue = new Node\Param($self->semStack[$stackPos-(7-6)], null, $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-4)], $self->semStack[$stackPos-(7-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-7)]);
|
$self->semValue = new Node\Param($self->semStack[$stackPos-(7-6)], null, $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-4)], $self->semStack[$stackPos-(7-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-7)]);
|
||||||
$self->checkParam($self->semValue);
|
$self->checkParam($self->semValue);
|
||||||
|
$self->addPropertyNameToHooks($self->semValue);
|
||||||
},
|
},
|
||||||
291 => static function ($self, $stackPos) {
|
291 => static function ($self, $stackPos) {
|
||||||
$self->semValue = new Node\Param($self->semStack[$stackPos-(9-6)], $self->semStack[$stackPos-(9-8)], $self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-4)], $self->semStack[$stackPos-(9-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(9-2)], $self->semStack[$stackPos-(9-1)], $self->semStack[$stackPos-(9-9)]);
|
$self->semValue = new Node\Param($self->semStack[$stackPos-(9-6)], $self->semStack[$stackPos-(9-8)], $self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-4)], $self->semStack[$stackPos-(9-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(9-2)], $self->semStack[$stackPos-(9-1)], $self->semStack[$stackPos-(9-9)]);
|
||||||
$self->checkParam($self->semValue);
|
$self->checkParam($self->semValue);
|
||||||
|
$self->addPropertyNameToHooks($self->semValue);
|
||||||
},
|
},
|
||||||
292 => static function ($self, $stackPos) {
|
292 => static function ($self, $stackPos) {
|
||||||
$self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
|
$self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
|
||||||
@ -1995,6 +1997,7 @@ class Php8 extends \PhpParser\ParserAbstract
|
|||||||
$self->semValue = new Stmt\Property($self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-6)]);
|
$self->semValue = new Stmt\Property($self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-6)]);
|
||||||
$self->checkPropertyHooksForMultiProperty($self->semValue, $stackPos-(7-5));
|
$self->checkPropertyHooksForMultiProperty($self->semValue, $stackPos-(7-5));
|
||||||
$self->checkEmptyPropertyHookList($self->semStack[$stackPos-(7-6)], $stackPos-(7-5));
|
$self->checkEmptyPropertyHookList($self->semStack[$stackPos-(7-6)], $stackPos-(7-5));
|
||||||
|
$self->addPropertyNameToHooks($self->semValue);
|
||||||
},
|
},
|
||||||
349 => static function ($self, $stackPos) {
|
349 => static function ($self, $stackPos) {
|
||||||
$self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]);
|
$self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]);
|
||||||
|
@ -32,6 +32,7 @@ use PhpParser\Node\Stmt\Nop;
|
|||||||
use PhpParser\Node\Stmt\Property;
|
use PhpParser\Node\Stmt\Property;
|
||||||
use PhpParser\Node\Stmt\TryCatch;
|
use PhpParser\Node\Stmt\TryCatch;
|
||||||
use PhpParser\Node\UseItem;
|
use PhpParser\Node\UseItem;
|
||||||
|
use PhpParser\Node\VarLikeIdentifier;
|
||||||
use PhpParser\NodeVisitor\CommentAnnotatingVisitor;
|
use PhpParser\NodeVisitor\CommentAnnotatingVisitor;
|
||||||
|
|
||||||
abstract class ParserAbstract implements Parser {
|
abstract class ParserAbstract implements Parser {
|
||||||
@ -1201,6 +1202,20 @@ abstract class ParserAbstract implements Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Property|Param $node
|
||||||
|
*/
|
||||||
|
protected function addPropertyNameToHooks(Node $node): void {
|
||||||
|
if ($node instanceof Property) {
|
||||||
|
$name = $node->props[0]->name->toString();
|
||||||
|
} else {
|
||||||
|
$name = $node->var->name;
|
||||||
|
}
|
||||||
|
foreach ($node->hooks as $hook) {
|
||||||
|
$hook->setAttribute('propertyName', $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @param array<Node\Arg|Node\VariadicPlaceholder> $args */
|
/** @param array<Node\Arg|Node\VariadicPlaceholder> $args */
|
||||||
private function isSimpleExit(array $args): bool {
|
private function isSimpleExit(array $args): bool {
|
||||||
if (\count($args) === 0) {
|
if (\count($args) === 0) {
|
||||||
|
@ -3,9 +3,14 @@
|
|||||||
namespace PhpParser\Node;
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
use PhpParser\Modifiers;
|
use PhpParser\Modifiers;
|
||||||
|
use PhpParser\Node\Expr\Assign;
|
||||||
|
use PhpParser\Node\Expr\PropertyFetch;
|
||||||
use PhpParser\Node\Expr\Variable;
|
use PhpParser\Node\Expr\Variable;
|
||||||
|
use PhpParser\Node\Scalar\Int_;
|
||||||
use PhpParser\Node\Stmt\Expression;
|
use PhpParser\Node\Stmt\Expression;
|
||||||
use PhpParser\Node\Stmt\Return_;
|
use PhpParser\Node\Stmt\Return_;
|
||||||
|
use PhpParser\ParserFactory;
|
||||||
|
use PhpParser\PrettyPrinter\Standard;
|
||||||
|
|
||||||
class PropertyHookTest extends \PHPUnit\Framework\TestCase {
|
class PropertyHookTest extends \PHPUnit\Framework\TestCase {
|
||||||
/**
|
/**
|
||||||
@ -40,17 +45,45 @@ class PropertyHookTest extends \PHPUnit\Framework\TestCase {
|
|||||||
$get = new PropertyHook('get', $expr);
|
$get = new PropertyHook('get', $expr);
|
||||||
$this->assertEquals([new Return_($expr)], $get->getStmts());
|
$this->assertEquals([new Return_($expr)], $get->getStmts());
|
||||||
|
|
||||||
// TODO: This is incorrect.
|
$set = new PropertyHook('set', $expr, [], ['propertyName' => 'abc']);
|
||||||
$set = new PropertyHook('set', $expr);
|
$this->assertEquals([
|
||||||
$this->assertEquals([new Expression($expr)], $set->getStmts());
|
new Expression(new Assign(new PropertyFetch(new Variable('this'), 'abc'), $expr))
|
||||||
|
], $set->getStmts());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetStmtsUnknownHook(): void {
|
public function testGetStmtsSetHookFromParser(): void {
|
||||||
|
$parser = (new ParserFactory())->createForNewestSupportedVersion();
|
||||||
|
$prettyPrinter = new Standard();
|
||||||
|
$stmts = $parser->parse(<<<'CODE'
|
||||||
|
<?php
|
||||||
|
class Test {
|
||||||
|
public $prop1 { set => 123; }
|
||||||
|
|
||||||
|
public function __construct(public $prop2 { set => 456; }) {}
|
||||||
|
}
|
||||||
|
CODE);
|
||||||
|
|
||||||
|
$hook1 = $stmts[0]->stmts[0]->hooks[0];
|
||||||
|
$this->assertEquals('$this->prop1 = 123;', $prettyPrinter->prettyPrint($hook1->getStmts()));
|
||||||
|
|
||||||
|
$hook2 = $stmts[0]->stmts[1]->params[0]->hooks[0];
|
||||||
|
$this->assertEquals('$this->prop2 = 456;', $prettyPrinter->prettyPrint($hook2->getStmts()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStmtsUnknownHook(): void {
|
||||||
$expr = new Variable('test');
|
$expr = new Variable('test');
|
||||||
$get = new PropertyHook('foobar', $expr);
|
$hook = new PropertyHook('foobar', $expr);
|
||||||
|
|
||||||
$this->expectException(\LogicException::class);
|
$this->expectException(\LogicException::class);
|
||||||
$this->expectExceptionMessage('Unknown property hook "foobar"');
|
$this->expectExceptionMessage('Unknown property hook "foobar"');
|
||||||
$get->getStmts();
|
$hook->getStmts();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStmtsSetHookWithoutPropertyName(): void {
|
||||||
|
$expr = new Variable('test');
|
||||||
|
$set = new PropertyHook('set', $expr);
|
||||||
|
$this->expectException(\LogicException::class);
|
||||||
|
$this->expectExceptionMessage('Can only use getStmts() on a "set" hook if the "propertyName" attribute is set');
|
||||||
|
$set->getStmts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user