Skip to content

Commit 092205e

Browse files
authored
Merge pull request #2 from phpactor/property-hook
Property hook
2 parents 0d3aad0 + 50911f6 commit 092205e

File tree

97 files changed

+3082
-115
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+3082
-115
lines changed

src/Node.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,11 +341,11 @@ public function getFullWidth() : int {
341341
* Gets string representing Node text (not including leading comment + whitespace trivia)
342342
* @return string
343343
*/
344-
public function getText() : string {
344+
public function getText(?string $fileContents= null) : string {
345345
$start = $this->getStartPosition();
346346
$end = $this->getEndPosition();
347347

348-
$fileContents = $this->getFileContents();
348+
$fileContents = $fileContents ?? $this->getFileContents();
349349
return \substr($fileContents, $start, $end - $start);
350350
}
351351

src/Node/Parameter.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class Parameter extends Node {
3131
public $equalsToken;
3232
/** @var null|Expression */
3333
public $default;
34+
/** @var PropertyHooks|null */
35+
public $propertyHooks;
3436

3537
const CHILD_NAMES = [
3638
'attributes',
@@ -42,7 +44,8 @@ class Parameter extends Node {
4244
'dotDotDotToken',
4345
'variableName',
4446
'equalsToken',
45-
'default'
47+
'default',
48+
'propertyHooks',
4649
];
4750

4851
public function isVariadic() {

src/Node/PropertyDeclaration.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Microsoft\PhpParser\ModifiedTypeInterface;
1111
use Microsoft\PhpParser\ModifiedTypeTrait;
1212
use Microsoft\PhpParser\Node;
13+
use Microsoft\PhpParser\Node\PropertyHooks;
1314
use Microsoft\PhpParser\Node\DelimitedList\QualifiedNameList;
1415
use Microsoft\PhpParser\Token;
1516

@@ -28,6 +29,9 @@ class PropertyDeclaration extends Node implements ModifiedTypeInterface {
2829
/** @var DelimitedList\ExpressionList */
2930
public $propertyElements;
3031

32+
/** @var PropertyHooks|null */
33+
public $propertyHooks;
34+
3135
/** @var Token */
3236
public $semicolon;
3337

@@ -37,6 +41,7 @@ class PropertyDeclaration extends Node implements ModifiedTypeInterface {
3741
'questionToken',
3842
'typeDeclarationList',
3943
'propertyElements',
44+
'propertyHooks',
4045
'semicolon'
4146
];
4247
}

src/Node/PropertyHook.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/*---------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All rights reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
namespace Microsoft\PhpParser\Node;
8+
9+
use Microsoft\PhpParser\FunctionLike;
10+
use Microsoft\PhpParser\Node;
11+
use Microsoft\PhpParser\Node\Statement\CompoundStatementNode;
12+
use Microsoft\PhpParser\Token;
13+
14+
class PropertyHook extends Node implements FunctionLike {
15+
/** @var AttributeGroup[]|null */
16+
public $attributes;
17+
/** @var Token */
18+
public $byRefToken;
19+
/** @var Token */
20+
public $name;
21+
/** @var Token|null */
22+
public $openParen;
23+
/** @var DelimitedList\ParameterDeclarationList|null */
24+
public $parameters;
25+
/** @var Token|null */
26+
public $closeParen;
27+
/** @var Token|null */
28+
public $arrowToken;
29+
/** @var CompoundStatementNode|Expression|Token|null */
30+
public $body;
31+
/** @var Token|null */
32+
public $semicolon;
33+
34+
const CHILD_NAMES = [
35+
'attributes',
36+
'byRefToken',
37+
'name',
38+
'openParen',
39+
'parameters',
40+
'closeParen',
41+
'arrowToken',
42+
'body',
43+
'semicolon',
44+
];
45+
}

src/Node/PropertyHooks.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
/*---------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All rights reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
namespace Microsoft\PhpParser\Node;
8+
9+
use Microsoft\PhpParser\Node;
10+
use Microsoft\PhpParser\Node\PropertyHooks;
11+
use Microsoft\PhpParser\Token;
12+
13+
class PropertyHooks extends Node {
14+
/** @var Token */
15+
public $openBrace;
16+
17+
/** @var PropertyHook[] */
18+
public $hookDeclarations;
19+
20+
/** @var Token */
21+
public $closeBrace;
22+
23+
const CHILD_NAMES = [
24+
'openBrace',
25+
'hookDeclarations',
26+
'closeBrace'
27+
];
28+
}

src/Parser.php

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
use Microsoft\PhpParser\Node\NumericLiteral;
7575
use Microsoft\PhpParser\Node\ParenthesizedIntersectionType;
7676
use Microsoft\PhpParser\Node\PropertyDeclaration;
77+
use Microsoft\PhpParser\Node\PropertyHooks;
78+
use Microsoft\PhpParser\Node\PropertyHook;
7779
use Microsoft\PhpParser\Node\ReservedWord;
7880
use Microsoft\PhpParser\Node\StringLiteral;
7981
use Microsoft\PhpParser\Node\MethodDeclaration;
@@ -877,6 +879,10 @@ private function parseParameterFn() {
877879
// TODO add post-parse rule that checks for invalid assignments
878880
$parameter->default = $this->parseExpression($parameter);
879881
}
882+
883+
if ($this->token->kind === TokenKind::OpenBraceToken) {
884+
$parameter->propertyHooks = $this->parsePropertyHooks($parameter);
885+
}
880886
return $parameter;
881887
};
882888
}
@@ -2186,6 +2192,7 @@ private function parseBinaryExpressionOrHigher($precedence, $parentNode) {
21862192
// the original operator, and the newly constructed exponentiation-expression as the operand.
21872193
$shouldOperatorTakePrecedenceOverUnary = false;
21882194
switch ($token->kind) {
2195+
case TokenKind::OpenBraceToken:
21892196
case TokenKind::AsteriskAsteriskToken:
21902197
$shouldOperatorTakePrecedenceOverUnary = $leftOperand instanceof UnaryExpression;
21912198
break;
@@ -3050,6 +3057,9 @@ private function parsePostfixExpressionRest($expression, $allowUpdateExpression
30503057
)) {
30513058
return $expression;
30523059
}
3060+
if ($tokenKind === TokenKind::OpenBraceToken) {
3061+
return $expression;
3062+
}
30533063
if ($tokenKind === TokenKind::ColonColonToken) {
30543064
$expression = $this->parseScopedPropertyAccessExpression($expression, null);
30553065
return $this->parsePostfixExpressionRest($expression);
@@ -3437,12 +3447,115 @@ private function parsePropertyDeclaration($parentNode, $modifiers, $questionToke
34373447
} elseif ($questionToken) {
34383448
$propertyDeclaration->typeDeclarationList = new MissingToken(TokenKind::PropertyType, $this->token->fullStart);
34393449
}
3440-
$propertyDeclaration->propertyElements = $this->parseExpressionList($propertyDeclaration);
3441-
$propertyDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
3450+
$propertyDeclaration->propertyElements = $this->parsePropertyNameList($propertyDeclaration);
3451+
if ($this->token->kind === TokenKind::OpenBraceToken) {
3452+
$propertyDeclaration->propertyHooks = $this->parsePropertyHooks($propertyDeclaration);
3453+
} else {
3454+
$propertyDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
3455+
}
34423456

34433457
return $propertyDeclaration;
34443458
}
34453459

3460+
/**
3461+
* @param PropertyDeclaration $parentNode
3462+
* @return DelimitedList\VariableNameList
3463+
*/
3464+
private function parsePropertyNameList($parentNode) {
3465+
// XXX this used to be implemented with parseExpressionList so keep the same classes.
3466+
return $this->parseDelimitedList(
3467+
DelimitedList\ExpressionList::class,
3468+
TokenKind::CommaToken,
3469+
function (Token $token) {
3470+
return $token->kind === TokenKind::VariableName;
3471+
},
3472+
$this->parsePropertyVariableNameAndDefault(),
3473+
$parentNode
3474+
);
3475+
}
3476+
3477+
private function parsePropertyVariableNameAndDefault() {
3478+
return function ($parentNode) {
3479+
// Imitate the format that parseExpression would have returned from when
3480+
// parseExpression was originally used.
3481+
// This is more precise and rules out parse errors such as `public $propName + 2;`
3482+
// This approach also avoids conflict with the deprecated $x{expr} array access syntax.
3483+
$variable = new Variable();
3484+
$variable->name = $this->eat1(TokenKind::VariableName);
3485+
$equalsToken = $this->eatOptional1(TokenKind::EqualsToken);
3486+
if ($equalsToken === null) {
3487+
$variable->parent = $parentNode;
3488+
return $variable;
3489+
}
3490+
3491+
$byRefToken = $this->eatOptional1(TokenKind::AmpersandToken); // byRef default is nonsense, but this is a compile-time error instead of a parse error.
3492+
$rightOperand = $this->parseExpression($parentNode); // gets overridden in makeBinaryExpression
3493+
return $this->makeBinaryExpression($variable, $equalsToken, $byRefToken, $rightOperand, $parentNode);
3494+
};
3495+
}
3496+
3497+
/**
3498+
* @param PropertyDeclaration|Parameter $parent
3499+
* @return PropertyHooks
3500+
*/
3501+
private function parsePropertyHooks(Node $parent) {
3502+
$propertyHooks = new PropertyHooks();
3503+
$propertyHooks->parent = $parent;
3504+
$propertyHooks->openBrace = $this->eat1(TokenKind::OpenBraceToken);
3505+
$hooks = [];
3506+
while (in_array($this->getCurrentToken()->kind, self::PROPERTY_HOOK_START_TOKENS, true)) {
3507+
$hooks[] = $this->parsePropertyHook($propertyHooks);
3508+
}
3509+
$propertyHooks->hookDeclarations = $hooks;
3510+
$propertyHooks->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
3511+
return $propertyHooks;
3512+
}
3513+
3514+
const PROPERTY_HOOK_START_TOKENS = [
3515+
TokenKind::Name,
3516+
TokenKind::AmpersandToken, // by reference
3517+
TokenKind::AttributeToken,
3518+
];
3519+
3520+
private function isPropertyHookStart() {
3521+
return function ($token) {
3522+
return \in_array($token->kind, self::PROPERTY_HOOK_START_TOKENS, true);
3523+
};
3524+
}
3525+
3526+
/**
3527+
* @param PropertyHooks $parentNode
3528+
* @return PropertyHook
3529+
*/
3530+
private function parsePropertyHook($parentNode) {
3531+
$node = new PropertyHook();
3532+
$node->parent = $parentNode;
3533+
if ($this->getCurrentToken()->kind === TokenKind::AttributeToken) {
3534+
$node->attributes = $this->parseAttributeGroups($node);
3535+
}
3536+
$node->byRefToken = $this->eatOptional1(TokenKind::AmpersandToken);
3537+
$node->name = $this->eat1(TokenKind::Name); // "get" or "set" - other values are compile errors, not parse errors.
3538+
$node->openParen = $this->eatOptional1(TokenKind::OpenParenToken);
3539+
if ($node->openParen) {
3540+
$node->parameters = $this->parseDelimitedList(
3541+
DelimitedList\ParameterDeclarationList::class,
3542+
TokenKind::CommaToken,
3543+
$this->isParameterStartFn(),
3544+
$this->parseParameterFn(),
3545+
$node);
3546+
$node->closeParen = $this->eat1(TokenKind::CloseParenToken);
3547+
}
3548+
// e.g. `get => expr;` or `get { return $expr; }`
3549+
$node->arrowToken = $this->eatOptional1(TokenKind::DoubleArrowToken);
3550+
if ($node->arrowToken) {
3551+
$node->body = $this->parseExpression($node);
3552+
$node->semicolon = $this->eat1(TokenKind::SemicolonToken);
3553+
} else {
3554+
$node->body = $this->parseCompoundStatement($node);
3555+
}
3556+
return $node;
3557+
}
3558+
34463559
/**
34473560
* Parse a comma separated qualified name list (e.g. interfaces implemented by a class)
34483561
*

tests/cases/lexical/keyword5.php.tokens

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@
44
"textLength": 6
55
},
66
{
7-
"kind": "YieldKeyword",
8-
"textLength": 5
9-
},
10-
{
11-
"kind": "Name",
12-
"textLength": 4
7+
"kind": "YieldFromKeyword",
8+
"textLength": 22
139
},
1410
{
1511
"kind": "EndOfFileToken",

tests/cases/parser/classMethods3.php.tree

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@
7272
"textLength": 8
7373
},
7474
"equalsToken": null,
75-
"default": null
75+
"default": null,
76+
"propertyHooks": null
7677
}
7778
},
7879
{
@@ -93,7 +94,8 @@
9394
"textLength": 6
9495
},
9596
"equalsToken": null,
96-
"default": null
97+
"default": null,
98+
"propertyHooks": null
9799
}
98100
}
99101
]

tests/cases/parser/classMethods4.php.tree

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@
7272
"textLength": 8
7373
},
7474
"equalsToken": null,
75-
"default": null
75+
"default": null,
76+
"propertyHooks": null
7677
}
7778
},
7879
{
@@ -93,7 +94,8 @@
9394
"textLength": 6
9495
},
9596
"equalsToken": null,
96-
"default": null
97+
"default": null,
98+
"propertyHooks": null
9799
}
98100
}
99101
]

tests/cases/parser/dnfTypesParameter1.php.tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@
111111
"textLength": 2
112112
},
113113
"equalsToken": null,
114-
"default": null
114+
"default": null,
115+
"propertyHooks": null
115116
}
116117
}
117118
]

tests/cases/parser/dnfTypesParameter2.php.tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@
100100
"textLength": 2
101101
},
102102
"equalsToken": null,
103-
"default": null
103+
"default": null,
104+
"propertyHooks": null
104105
}
105106
}
106107
]

tests/cases/parser/dnfTypesParameter3.php.tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
"textLength": 2
7272
},
7373
"equalsToken": null,
74-
"default": null
74+
"default": null,
75+
"propertyHooks": null
7576
}
7677
}
7778
]

tests/cases/parser/dnfTypesParameter4.php.tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@
7373
"textLength": 0
7474
},
7575
"equalsToken": null,
76-
"default": null
76+
"default": null,
77+
"propertyHooks": null
7778
}
7879
}
7980
]

tests/cases/parser/dnfTypesParameter5.php.tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@
104104
"textLength": 0
105105
},
106106
"equalsToken": null,
107-
"default": null
107+
"default": null,
108+
"propertyHooks": null
108109
}
109110
}
110111
]

0 commit comments

Comments
 (0)