Compare commits

..

12 Commits

Author SHA1 Message Date
Nikita Popov
1c13d05035 Release PHP-Parser 4.10.0 2020-09-19 16:52:48 +02:00
Nikita Popov
c7dc3ce552 Add basic FPPP support for attributes 2020-09-19 16:43:45 +02:00
Nikita Popov
9f6ad686a7 Adjust skip list for moved test
The two remaining test_old failures are due to a bug on the PHP
side.
2020-09-19 15:55:07 +02:00
Nikita Popov
1899471f80 Update test for PHP 8 error behavior
Replace division by zero with a different error condition that
still warns, and adjust the expected message based on PHP version.
2020-09-19 15:52:05 +02:00
Nikita Popov
8505acd151 Correctly handle ?-> in encapsed strings
Followup upstream change.
2020-09-19 15:47:14 +02:00
Nikita Popov
c3e20d9970 Use 8.0.0beta4 to tests against 2020-09-19 15:27:27 +02:00
Tomas Votruba
4c22c62783 [PHP 8.0] Add attributes support (#661)
Adds support for PHP 8 attributes, represented using `AttrGroup` nodes
containing `Attribute` nodes. The `attrGroup` subnode is added to all
nodes that can have attributes.

This is still missing FPPP support.

Co-authored-by: Nikita Popov <nikita.ppv@gmail.com>
2020-09-13 21:01:17 +02:00
Nikita Popov
f66a32e2df Emulate PHP 8 attribute syntax
Perform emulation by replacing #[ with %[, then patching % back
to # and coalescing #[ into T_ATTRIBUTE if it is a freestanding
token.
2020-09-06 17:42:38 +02:00
Nikita Popov
75abbbd2d4 Handle flexible heredoc via TokenEmulator
Extend the interface to support preprocessing.
2020-09-06 17:05:23 +02:00
Nikita Popov
39b046007d Refactor token emulator registration
Only determine needed emulators based on PHP version once, and
add an adaptor that allows treating forward and reverse emulation
the same.

Previously the isEmulationNeeded() check was too conservative,
as it also considered emulators that are not relevant for the
version. Though possibly that check should just be dropped
altogether.
2020-09-06 15:50:52 +02:00
Nikita Popov
e3872b8906 Improve compatibility with other libs defining compat tokens 2020-09-06 15:00:28 +02:00
Ondřej Mirtes
4a40a84cf6 Fixed PHPDoc typo 2020-09-02 12:33:46 +02:00
89 changed files with 3678 additions and 1898 deletions

View File

@@ -35,7 +35,7 @@ matrix:
- name: PHP 8.0 Code on PHP 7.0 Integration Tests - name: PHP 8.0 Code on PHP 7.0 Integration Tests
php: 7.0 php: 7.0
script: script:
- test_old/run-php-src.sh 8.0.0beta1 - test_old/run-php-src.sh 8.0.0beta4
allow_failures: allow_failures:
- name: PHP 8.0 Code on PHP 7.0 Integration Tests - name: PHP 8.0 Code on PHP 7.0 Integration Tests
fast_finish: true fast_finish: true

View File

@@ -1,8 +1,25 @@
Version 4.9.2-dev Version 4.10.1-dev
----------------- ------------------
Nothing yet. Nothing yet.
Version 4.10.0 (2020-09-19)
---------------------------
### Added
* [PHP 8.0] Added support for attributes. These are represented using a new `AttributeGroup` node
containing `Attribute` nodes. A new `attrGroups` subnode is available on all node types that
support attributes, i.e. `Stmt\Class_`, `Stmt\Trait_`, `Stmt\Interface_`, `Stmt\Function_`,
`Stmt\ClassMethod`, `Stmt\ClassConst`, `Stmt\Property`, `Expr\Closure`, `Expr\ArrowFunction` and
`Param`.
* [PHP 8.0] Added support for nullsafe properties inside interpolated strings, in line with an
upstream change.
### Fixed
* Improved compatibility with other libraries that use forward compatibility defines for PHP tokens.
Version 4.9.1 (2020-08-30) Version 4.9.1 (2020-08-30)
-------------------------- --------------------------

View File

@@ -82,6 +82,31 @@ no_comma:
optional_comma: optional_comma:
/* empty */ /* empty */
| ',' | ','
;
attribute_decl:
class_name { $$ = Node\Attribute[$1, []]; }
| class_name argument_list { $$ = Node\Attribute[$1, $2]; }
;
attribute_group:
attribute_decl { init($1); }
| attribute_group ',' attribute_decl { push($1, $3); }
;
attribute:
T_ATTRIBUTE attribute_group optional_comma ']' { $$ = Node\AttributeGroup[$2]; }
;
attributes:
attribute { init($1); }
| attributes attribute { push($1, $2); }
;
optional_attributes:
/* empty */ { $$ = []; }
| attributes { $$ = $1; }
;
top_statement: top_statement:
statement { $$ = $1; } statement { $$ = $1; }
@@ -317,18 +342,23 @@ block_or_error:
function_declaration_statement: function_declaration_statement:
T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type block_or_error T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type block_or_error
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8]]; } { $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8, 'attrGroups' => []]]; }
| attributes T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type block_or_error
{ $$ = Stmt\Function_[$4, ['byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
; ;
class_declaration_statement: class_declaration_statement:
class_entry_type identifier extends_from implements_list '{' class_statement_list '}' class_entry_type identifier extends_from implements_list '{' class_statement_list '}'
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; { $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6, 'attrGroups' => []]];
$this->checkClass($$, #2); } $this->checkClass($$, #2); }
| T_INTERFACE identifier interface_extends_list '{' class_statement_list '}' | attributes class_entry_type identifier extends_from implements_list '{' class_statement_list '}'
{ $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; { $$ = Stmt\Class_[$3, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
$this->checkInterface($$, #2); } $this->checkClass($$, #3); }
| T_TRAIT identifier '{' class_statement_list '}' | optional_attributes T_INTERFACE identifier interface_extends_list '{' class_statement_list '}'
{ $$ = Stmt\Trait_[$2, ['stmts' => $4]]; } { $$ = Stmt\Interface_[$3, ['extends' => $4, 'stmts' => $6, 'attrGroups' => $1]];
$this->checkInterface($$, #3); }
| optional_attributes T_TRAIT identifier '{' class_statement_list '}'
{ $$ = Stmt\Trait_[$3, ['stmts' => $5, 'attrGroups' => $1]]; }
; ;
class_entry_type: class_entry_type:
@@ -489,14 +519,14 @@ optional_visibility_modifier:
; ;
parameter: parameter:
optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable
{ $$ = new Node\Param($5, null, $2, $3, $4, attributes(), $1); { $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1);
$this->checkParam($$); } $this->checkParam($$); }
| optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable '=' expr | optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable '=' expr
{ $$ = new Node\Param($5, $7, $2, $3, $4, attributes(), $1); { $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1);
$this->checkParam($$); } $this->checkParam($$); }
| optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis error | optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis error
{ $$ = new Node\Param(Expr\Error[], null, $2, $3, $4, attributes(), $1); } { $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
; ;
type_expr: type_expr:
@@ -600,14 +630,15 @@ class_statement_list:
; ;
class_statement: class_statement:
variable_modifiers optional_type_without_static property_declaration_list ';' optional_attributes variable_modifiers optional_type_without_static property_declaration_list ';'
{ $attrs = attributes(); { $$ = new Stmt\Property($2, $4, attributes(), $3, $1);
$$ = new Stmt\Property($1, $3, $attrs, $2); $this->checkProperty($$, #1); } $this->checkProperty($$, #2); }
| method_modifiers T_CONST class_const_list ';' | optional_attributes method_modifiers T_CONST class_const_list ';'
{ $$ = Stmt\ClassConst[$3, $1]; $this->checkClassConst($$, #1); } { $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
| method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body $this->checkClassConst($$, #2); }
{ $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; | optional_attributes method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body
$this->checkClassMethod($$, #1); } { $$ = Stmt\ClassMethod[$5, ['type' => $2, 'byRef' => $4, 'params' => $7, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]];
$this->checkClassMethod($$, #2); }
| T_USE class_name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; } | T_USE class_name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; }
| error { $$ = null; /* will be skipped */ } | error { $$ = null; /* will be skipped */ }
; ;
@@ -802,21 +833,27 @@ expr:
| T_THROW expr { $$ = Expr\Throw_[$2]; } | T_THROW expr { $$ = Expr\Throw_[$2]; }
| T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr | T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
{ $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8]]; } { $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8, 'attrGroups' => []]]; }
| T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr | T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
{ $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9]]; } { $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9, 'attrGroups' => []]]; }
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type block_or_error
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8, 'attrGroups' => []]]; }
| T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type block_or_error
{ $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => []]]; }
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type | attributes T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
block_or_error { $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9, 'attrGroups' => $1]]; }
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8]]; } | attributes T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
| T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type { $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $4, 'params' => $6, 'returnType' => $8, 'expr' => $10, 'attrGroups' => $1]]; }
block_or_error | attributes T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type block_or_error
{ $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9]]; } { $$ = Expr\Closure[['static' => false, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
| attributes T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type block_or_error
{ $$ = Expr\Closure[['static' => true, 'byRef' => $4, 'params' => $6, 'uses' => $8, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]]; }
; ;
anonymous_class: anonymous_class:
T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}' optional_attributes T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); { $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
$this->checkClass($$[0], -1); } $this->checkClass($$[0], -1); }
; ;

View File

@@ -117,28 +117,28 @@ function resolveMacros($code) {
$matches['args'] $matches['args']
); );
if ('attributes' == $name) { if ('attributes' === $name) {
assertArgs(0, $args, $name); assertArgs(0, $args, $name);
return '$this->startAttributeStack[#1] + $this->endAttributes'; return '$this->startAttributeStack[#1] + $this->endAttributes';
} }
if ('stackAttributes' == $name) { if ('stackAttributes' === $name) {
assertArgs(1, $args, $name); assertArgs(1, $args, $name);
return '$this->startAttributeStack[' . $args[0] . ']' return '$this->startAttributeStack[' . $args[0] . ']'
. ' + $this->endAttributeStack[' . $args[0] . ']'; . ' + $this->endAttributeStack[' . $args[0] . ']';
} }
if ('init' == $name) { if ('init' === $name) {
return '$$ = array(' . implode(', ', $args) . ')'; return '$$ = array(' . implode(', ', $args) . ')';
} }
if ('push' == $name) { if ('push' === $name) {
assertArgs(2, $args, $name); assertArgs(2, $args, $name);
return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0]; return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
} }
if ('pushNormalizing' == $name) { if ('pushNormalizing' === $name) {
assertArgs(2, $args, $name); assertArgs(2, $args, $name);
return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }' return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
@@ -151,20 +151,20 @@ function resolveMacros($code) {
return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')'; return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
} }
if ('parseVar' == $name) { if ('parseVar' === $name) {
assertArgs(1, $args, $name); assertArgs(1, $args, $name);
return 'substr(' . $args[0] . ', 1)'; return 'substr(' . $args[0] . ', 1)';
} }
if ('parseEncapsed' == $name) { if ('parseEncapsed' === $name) {
assertArgs(3, $args, $name); assertArgs(3, $args, $name);
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {' return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }'; . ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
} }
if ('makeNop' == $name) { if ('makeNop' === $name) {
assertArgs(3, $args, $name); assertArgs(3, $args, $name);
return '$startAttributes = ' . $args[1] . ';' return '$startAttributes = ' . $args[1] . ';'
@@ -182,7 +182,7 @@ function resolveMacros($code) {
. ' else { ' . $args[0] . ' = null; }'; . ' else { ' . $args[0] . ' = null; }';
} }
if ('strKind' == $name) { if ('strKind' === $name) {
assertArgs(1, $args, $name); assertArgs(1, $args, $name);
return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && ' return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && '
@@ -190,7 +190,7 @@ function resolveMacros($code) {
. '? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED)'; . '? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED)';
} }
if ('prependLeadingComments' == $name) { if ('prependLeadingComments' === $name) {
assertArgs(1, $args, $name); assertArgs(1, $args, $name);
return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; ' return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; '

View File

@@ -110,3 +110,4 @@
%token T_NAME_FULLY_QUALIFIED %token T_NAME_FULLY_QUALIFIED
%token T_NAME_QUALIFIED %token T_NAME_QUALIFIED
%token T_NAME_RELATIVE %token T_NAME_RELATIVE
%token T_ATTRIBUTE

View File

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

View File

@@ -405,35 +405,60 @@ class Lexer
} }
private function defineCompatibilityTokens() { private function defineCompatibilityTokens() {
// PHP 7.4 static $compatTokensDefined = false;
if (!defined('T_BAD_CHARACTER')) { if ($compatTokensDefined) {
\define('T_BAD_CHARACTER', -1); return;
}
if (!defined('T_FN')) {
\define('T_FN', -2);
}
if (!defined('T_COALESCE_EQUAL')) {
\define('T_COALESCE_EQUAL', -3);
} }
$compatTokens = [
// PHP 7.4
'T_BAD_CHARACTER',
'T_FN',
'T_COALESCE_EQUAL',
// PHP 8.0 // PHP 8.0
if (!defined('T_NAME_QUALIFIED')) { 'T_NAME_QUALIFIED',
\define('T_NAME_QUALIFIED', -4); 'T_NAME_FULLY_QUALIFIED',
'T_NAME_RELATIVE',
'T_MATCH',
'T_NULLSAFE_OBJECT_OPERATOR',
'T_ATTRIBUTE',
];
// PHP-Parser might be used together with another library that also emulates some or all
// of these tokens. Perform a sanity-check that all already defined tokens have been
// assigned a unique ID.
$usedTokenIds = [];
foreach ($compatTokens as $token) {
if (\defined($token)) {
$tokenId = \constant($token);
$clashingToken = $usedTokenIds[$tokenId] ?? null;
if ($clashingToken !== null) {
throw new \Error(sprintf(
'Token %s has same ID as token %s, ' .
'you may be using a library with broken token emulation',
$token, $clashingToken
));
} }
if (!defined('T_NAME_FULLY_QUALIFIED')) { $usedTokenIds[$token] = $tokenId;
\define('T_NAME_FULLY_QUALIFIED', -5);
} }
if (!defined('T_NAME_RELATIVE')) {
\define('T_NAME_RELATIVE', -6);
} }
if (!defined('T_MATCH')) {
\define('T_MATCH', -7); // Now define any tokens that have not yet been emulated. Try to assign IDs from -1
// downwards, but skip any IDs that may already be in use.
$newTokenId = -1;
foreach ($compatTokens as $token) {
if (!\defined($token)) {
while (isset($usedTokenIds[$newTokenId])) {
$newTokenId--;
} }
if (!defined('T_NULLSAFE_OBJECT_OPERATOR')) { \define($token, $newTokenId);
\define('T_NULLSAFE_OBJECT_OPERATOR', -8); $newTokenId--;
} }
} }
$compatTokensDefined = true;
}
/** /**
* Creates the token map. * Creates the token map.
* *
@@ -486,6 +511,7 @@ class Lexer
$tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE; $tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE;
$tokenMap[\T_MATCH] = Tokens::T_MATCH; $tokenMap[\T_MATCH] = Tokens::T_MATCH;
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR; $tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR;
$tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE;
return $tokenMap; return $tokenMap;
} }

View File

@@ -5,12 +5,15 @@ namespace PhpParser\Lexer;
use PhpParser\Error; use PhpParser\Error;
use PhpParser\ErrorHandler; use PhpParser\ErrorHandler;
use PhpParser\Lexer; use PhpParser\Lexer;
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator; use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator; use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator; use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator; use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator; use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulatorInterface; use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
use PhpParser\Parser\Tokens; use PhpParser\Parser\Tokens;
class Emulative extends Lexer class Emulative extends Lexer
@@ -19,17 +22,11 @@ class Emulative extends Lexer
const PHP_7_4 = '7.4dev'; const PHP_7_4 = '7.4dev';
const PHP_8_0 = '8.0dev'; const PHP_8_0 = '8.0dev';
const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX'
/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n
(?:.*\r?\n)*?
(?<indentation>\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
REGEX;
/** @var mixed[] Patches used to reverse changes introduced in the code */ /** @var mixed[] Patches used to reverse changes introduced in the code */
private $patches = []; private $patches = [];
/** @var TokenEmulatorInterface[] */ /** @var TokenEmulator[] */
private $tokenEmulators = []; private $emulators = [];
/** @var string */ /** @var string */
private $targetPhpVersion; private $targetPhpVersion;
@@ -46,27 +43,47 @@ REGEX;
parent::__construct($options); parent::__construct($options);
$this->tokenEmulators[] = new FnTokenEmulator(); $emulators = [
$this->tokenEmulators[] = new MatchTokenEmulator(); new FlexibleDocStringEmulator(),
$this->tokenEmulators[] = new CoaleseEqualTokenEmulator(); new FnTokenEmulator(),
$this->tokenEmulators[] = new NumericLiteralSeparatorEmulator(); new MatchTokenEmulator(),
$this->tokenEmulators[] = new NullsafeTokenEmulator(); new CoaleseEqualTokenEmulator(),
new NumericLiteralSeparatorEmulator(),
new NullsafeTokenEmulator(),
new AttributeEmulator(),
];
// Collect emulators that are relevant for the PHP version we're running
// and the PHP version we're targeting for emulation.
foreach ($emulators as $emulator) {
$emulatorPhpVersion = $emulator->getPhpVersion();
if ($this->isForwardEmulationNeeded($emulatorPhpVersion)) {
$this->emulators[] = $emulator;
} else if ($this->isReverseEmulationNeeded($emulatorPhpVersion)) {
$this->emulators[] = new ReverseEmulator($emulator);
}
}
} }
public function startLexing(string $code, ErrorHandler $errorHandler = null) { public function startLexing(string $code, ErrorHandler $errorHandler = null) {
$this->patches = []; $emulators = array_filter($this->emulators, function($emulator) use($code) {
return $emulator->isEmulationNeeded($code);
});
if ($this->isEmulationNeeded($code) === false) { if (empty($emulators)) {
// Nothing to emulate, yay // Nothing to emulate, yay
parent::startLexing($code, $errorHandler); parent::startLexing($code, $errorHandler);
return; return;
} }
$collector = new ErrorHandler\Collecting(); $this->patches = [];
foreach ($emulators as $emulator) {
$code = $emulator->preprocessCode($code, $this->patches);
}
// 1. emulation of heredoc and nowdoc new syntax $collector = new ErrorHandler\Collecting();
$preparedCode = $this->processHeredocNowdoc($code); parent::startLexing($code, $collector);
parent::startLexing($preparedCode, $collector); $this->sortPatches();
$this->fixupTokens(); $this->fixupTokens();
$errors = $collector->getErrors(); $errors = $collector->getErrors();
@@ -77,84 +94,28 @@ REGEX;
} }
} }
foreach ($this->tokenEmulators as $tokenEmulator) { foreach ($emulators as $emulator) {
$emulatorPhpVersion = $tokenEmulator->getPhpVersion(); $this->tokens = $emulator->emulate($code, $this->tokens);
if (version_compare(\PHP_VERSION, $emulatorPhpVersion, '<')
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>=')
&& $tokenEmulator->isEmulationNeeded($code)) {
$this->tokens = $tokenEmulator->emulate($code, $this->tokens);
} else if (version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=')
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<')
&& $tokenEmulator->isEmulationNeeded($code)) {
$this->tokens = $tokenEmulator->reverseEmulate($code, $this->tokens);
}
} }
} }
private function isHeredocNowdocEmulationNeeded(string $code): bool private function isForwardEmulationNeeded(string $emulatorPhpVersion): bool {
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '<')
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>=');
}
private function isReverseEmulationNeeded(string $emulatorPhpVersion): bool {
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=')
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<');
}
private function sortPatches()
{ {
// skip version where this works without emulation // Patches may be contributed by different emulators.
if (version_compare(\PHP_VERSION, self::PHP_7_3, '>=')) { // Make sure they are sorted by increasing patch position.
return false; usort($this->patches, function($p1, $p2) {
} return $p1[0] <=> $p2[0];
});
return strpos($code, '<<<') !== false;
}
private function processHeredocNowdoc(string $code): string
{
if ($this->isHeredocNowdocEmulationNeeded($code) === false) {
return $code;
}
if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
// No heredoc/nowdoc found
return $code;
}
// Keep track of how much we need to adjust string offsets due to the modifications we
// already made
$posDelta = 0;
foreach ($matches as $match) {
$indentation = $match['indentation'][0];
$indentationStart = $match['indentation'][1];
$separator = $match['separator'][0];
$separatorStart = $match['separator'][1];
if ($indentation === '' && $separator !== '') {
// Ordinary heredoc/nowdoc
continue;
}
if ($indentation !== '') {
// Remove indentation
$indentationLen = strlen($indentation);
$code = substr_replace($code, '', $indentationStart + $posDelta, $indentationLen);
$this->patches[] = [$indentationStart + $posDelta, 'add', $indentation];
$posDelta -= $indentationLen;
}
if ($separator === '') {
// Insert newline as separator
$code = substr_replace($code, "\n", $separatorStart + $posDelta, 0);
$this->patches[] = [$separatorStart + $posDelta, 'remove', "\n"];
$posDelta += 1;
}
}
return $code;
}
private function isEmulationNeeded(string $code): bool
{
foreach ($this->tokenEmulators as $emulativeToken) {
if ($emulativeToken->isEmulationNeeded($code)) {
return true;
}
}
return $this->isHeredocNowdocEmulationNeeded($code);
} }
private function fixupTokens() private function fixupTokens()
@@ -173,7 +134,20 @@ REGEX;
for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) { for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) {
$token = $this->tokens[$i]; $token = $this->tokens[$i];
if (\is_string($token)) { if (\is_string($token)) {
// We assume that patches don't apply to string tokens if ($patchPos === $pos) {
// Only support replacement for string tokens.
assert($patchType === 'replace');
$this->tokens[$i] = $patchText;
// Fetch the next patch
$patchIdx++;
if ($patchIdx >= \count($this->patches)) {
// No more patches, we're done
return;
}
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
}
$pos += \strlen($token); $pos += \strlen($token);
continue; continue;
} }
@@ -201,6 +175,11 @@ REGEX;
$token[1], $patchText, $patchPos - $pos + $posDelta, 0 $token[1], $patchText, $patchPos - $pos + $posDelta, 0
); );
$posDelta += $patchTextLen; $posDelta += $patchTextLen;
} else if ($patchType === 'replace') {
// Replace inside the token string
$this->tokens[$i][1] = substr_replace(
$token[1], $patchText, $patchPos - $pos + $posDelta, $patchTextLen
);
} else { } else {
assert(false); assert(false);
} }
@@ -247,7 +226,7 @@ REGEX;
if ($patchType === 'add') { if ($patchType === 'add') {
$posDelta += strlen($patchText); $posDelta += strlen($patchText);
$lineDelta += substr_count($patchText, "\n"); $lineDelta += substr_count($patchText, "\n");
} else { } else if ($patchType === 'remove') {
$posDelta -= strlen($patchText); $posDelta -= strlen($patchText);
$lineDelta -= substr_count($patchText, "\n"); $lineDelta -= substr_count($patchText, "\n");
} }

View File

@@ -0,0 +1,56 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
final class AttributeEmulator extends TokenEmulator
{
public function getPhpVersion(): string
{
return Emulative::PHP_8_0;
}
public function isEmulationNeeded(string $code) : bool
{
return strpos($code, '#[') !== false;
}
public function emulate(string $code, array $tokens): array
{
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way.
$line = 1;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if ($tokens[$i] === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1] === '[') {
array_splice($tokens, $i, 2, [
[\T_ATTRIBUTE, '#[', $line]
]);
$c--;
continue;
}
if (\is_array($tokens[$i])) {
$line += substr_count($tokens[$i][1], "\n");
}
}
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array
{
// TODO
return $tokens;
}
public function preprocessCode(string $code, array &$patches): string {
$pos = 0;
while (false !== $pos = strpos($code, '#[', $pos)) {
// Replace #[ with %[
$code[$pos] = '%';
$patches[] = [$pos, 'replace', '#'];
$pos += 2;
}
return $code;
}
}

View File

@@ -4,7 +4,7 @@ namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative; use PhpParser\Lexer\Emulative;
final class CoaleseEqualTokenEmulator implements TokenEmulatorInterface final class CoaleseEqualTokenEmulator extends TokenEmulator
{ {
public function getPhpVersion(): string public function getPhpVersion(): string
{ {

View File

@@ -0,0 +1,76 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
final class FlexibleDocStringEmulator extends TokenEmulator
{
const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX'
/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n
(?:.*\r?\n)*?
(?<indentation>\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
REGEX;
public function getPhpVersion(): string
{
return Emulative::PHP_7_3;
}
public function isEmulationNeeded(string $code) : bool
{
return strpos($code, '<<<') !== false;
}
public function emulate(string $code, array $tokens): array
{
// Handled by preprocessing + fixup.
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array
{
// Not supported.
return $tokens;
}
public function preprocessCode(string $code, array &$patches): string {
if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
// No heredoc/nowdoc found
return $code;
}
// Keep track of how much we need to adjust string offsets due to the modifications we
// already made
$posDelta = 0;
foreach ($matches as $match) {
$indentation = $match['indentation'][0];
$indentationStart = $match['indentation'][1];
$separator = $match['separator'][0];
$separatorStart = $match['separator'][1];
if ($indentation === '' && $separator !== '') {
// Ordinary heredoc/nowdoc
continue;
}
if ($indentation !== '') {
// Remove indentation
$indentationLen = strlen($indentation);
$code = substr_replace($code, '', $indentationStart + $posDelta, $indentationLen);
$patches[] = [$indentationStart + $posDelta, 'add', $indentation];
$posDelta -= $indentationLen;
}
if ($separator === '') {
// Insert newline as separator
$code = substr_replace($code, "\n", $separatorStart + $posDelta, 0);
$patches[] = [$separatorStart + $posDelta, 'remove', "\n"];
$posDelta += 1;
}
}
return $code;
}
}

View File

@@ -2,7 +2,7 @@
namespace PhpParser\Lexer\TokenEmulator; namespace PhpParser\Lexer\TokenEmulator;
abstract class KeywordEmulator implements TokenEmulatorInterface abstract class KeywordEmulator extends TokenEmulator
{ {
abstract function getKeywordString(): string; abstract function getKeywordString(): string;
abstract function getKeywordToken(): int; abstract function getKeywordToken(): int;

View File

@@ -4,7 +4,7 @@ namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative; use PhpParser\Lexer\Emulative;
final class NullsafeTokenEmulator implements TokenEmulatorInterface final class NullsafeTokenEmulator extends TokenEmulator
{ {
public function getPhpVersion(): string public function getPhpVersion(): string
{ {
@@ -22,15 +22,35 @@ final class NullsafeTokenEmulator implements TokenEmulatorInterface
// the tokens array on the way // the tokens array on the way
$line = 1; $line = 1;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) { for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if (isset($tokens[$i + 1])) { if ($tokens[$i] === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) {
if ($tokens[$i] === '?' && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) {
array_splice($tokens, $i, 2, [ array_splice($tokens, $i, 2, [
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line] [\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]
]); ]);
$c--; $c--;
continue; continue;
} }
// Handle ?-> inside encapsed string.
if ($tokens[$i][0] === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1])
&& $tokens[$i - 1][0] === \T_VARIABLE
&& preg_match('/^\?->([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/', $tokens[$i][1], $matches)
) {
$replacement = [
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line],
[\T_STRING, $matches[1], $line],
];
if (\strlen($matches[0]) !== \strlen($tokens[$i][1])) {
$replacement[] = [
\T_ENCAPSED_AND_WHITESPACE,
\substr($tokens[$i][1], \strlen($matches[0])),
$line
];
} }
array_splice($tokens, $i, 1, $replacement);
$c += \count($replacement) - 1;
continue;
}
if (\is_array($tokens[$i])) { if (\is_array($tokens[$i])) {
$line += substr_count($tokens[$i][1], "\n"); $line += substr_count($tokens[$i][1], "\n");
} }

View File

@@ -4,7 +4,7 @@ namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative; use PhpParser\Lexer\Emulative;
final class NumericLiteralSeparatorEmulator implements TokenEmulatorInterface final class NumericLiteralSeparatorEmulator extends TokenEmulator
{ {
const BIN = '(?:0b[01]+(?:_[01]+)*)'; const BIN = '(?:0b[01]+(?:_[01]+)*)';
const HEX = '(?:0x[0-9a-f]+(?:_[0-9a-f]+)*)'; const HEX = '(?:0x[0-9a-f]+(?:_[0-9a-f]+)*)';

View File

@@ -0,0 +1,36 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
/**
* Reverses emulation direction of the inner emulator.
*/
final class ReverseEmulator extends TokenEmulator
{
/** @var TokenEmulator Inner emulator */
private $emulator;
public function __construct(TokenEmulator $emulator) {
$this->emulator = $emulator;
}
public function getPhpVersion(): string {
return $this->emulator->getPhpVersion();
}
public function isEmulationNeeded(string $code): bool {
return $this->emulator->isEmulationNeeded($code);
}
public function emulate(string $code, array $tokens): array {
return $this->emulator->reverseEmulate($code, $tokens);
}
public function reverseEmulate(string $code, array $tokens): array {
return $this->emulator->emulate($code, $tokens);
}
public function preprocessCode(string $code, array &$patches): string {
return $code;
}
}

View File

@@ -0,0 +1,25 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
/** @internal */
abstract class TokenEmulator
{
abstract public function getPhpVersion(): string;
abstract public function isEmulationNeeded(string $code): bool;
/**
* @return array Modified Tokens
*/
abstract public function emulate(string $code, array $tokens): array;
/**
* @return array Modified Tokens
*/
abstract public function reverseEmulate(string $code, array $tokens): array;
public function preprocessCode(string $code, array &$patches): string {
return $code;
}
}

View File

@@ -1,21 +0,0 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
/** @internal */
interface TokenEmulatorInterface
{
public function getPhpVersion(): string;
public function isEmulationNeeded(string $code): bool;
/**
* @return array Modified Tokens
*/
public function emulate(string $code, array $tokens): array;
/**
* @return array Modified Tokens
*/
public function reverseEmulate(string $code, array $tokens): array;
}

View File

@@ -0,0 +1,34 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
use PhpParser\NodeAbstract;
class Attribute extends NodeAbstract
{
/** @var Name Attribute name */
public $name;
/** @var Arg[] Attribute arguments */
public $args;
/**
* @param Node\Name $name Attribute name
* @param Arg[] $args Attribute arguments
* @param array $attributes Additional node attributes
*/
public function __construct(Name $name, array $args = [], array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
$this->args = $args;
}
public function getSubNodeNames() : array {
return ['name', 'args'];
}
public function getType() : string {
return 'Attribute';
}
}

View File

@@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
use PhpParser\NodeAbstract;
class AttributeGroup extends NodeAbstract
{
/** @var Attribute[] Attributes */
public $attrs;
/**
* @param Attribute[] $attrs PHP attributes
* @param array $attributes Additional node attributes
*/
public function __construct(array $attrs, array $attributes = []) {
$this->attributes = $attributes;
$this->attrs = $attrs;
}
public function getSubNodeNames() : array {
return ['attrs'];
}
public function getType() : string {
return 'AttributeGroup';
}
}

View File

@@ -5,7 +5,7 @@ namespace PhpParser\Node;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
/** /**
* @property Name $namespacedName Namespaced name (for class constants, if using NameResolver) * @property Name $namespacedName Namespaced name (for global constants, if using NameResolver)
*/ */
class Const_ extends NodeAbstract class Const_ extends NodeAbstract
{ {

View File

@@ -22,6 +22,8 @@ class ArrowFunction extends Expr implements FunctionLike
/** @var Expr */ /** @var Expr */
public $expr; public $expr;
/** @var Node\AttributeGroup[] */
public $attrGroups;
/** /**
* @param array $subNodes Array of the following optional subnodes: * @param array $subNodes Array of the following optional subnodes:
@@ -30,6 +32,7 @@ class ArrowFunction extends Expr implements FunctionLike
* 'params' => array() : Parameters * 'params' => array() : Parameters
* 'returnType' => null : Return type * 'returnType' => null : Return type
* 'expr' => Expr : Expression body * 'expr' => Expr : Expression body
* 'attrGroups' => array() : PHP attribute groups
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $subNodes = [], array $attributes = []) { public function __construct(array $subNodes = [], array $attributes = []) {
@@ -40,10 +43,11 @@ class ArrowFunction extends Expr implements FunctionLike
$returnType = $subNodes['returnType'] ?? null; $returnType = $subNodes['returnType'] ?? null;
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
$this->expr = $subNodes['expr'] ?? null; $this->expr = $subNodes['expr'] ?? null;
$this->attrGroups = $subNodes['attrGroups'] ?? [];
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['static', 'byRef', 'params', 'returnType', 'expr']; return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr'];
} }
public function returnsByRef() : bool { public function returnsByRef() : bool {
@@ -58,6 +62,10 @@ class ArrowFunction extends Expr implements FunctionLike
return $this->returnType; return $this->returnType;
} }
public function getAttrGroups() : array {
return $this->attrGroups;
}
/** /**
* @return Node\Stmt\Return_[] * @return Node\Stmt\Return_[]
*/ */

View File

@@ -20,6 +20,8 @@ class Closure extends Expr implements FunctionLike
public $returnType; public $returnType;
/** @var Node\Stmt[] Statements */ /** @var Node\Stmt[] Statements */
public $stmts; public $stmts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** /**
* Constructs a lambda function node. * Constructs a lambda function node.
@@ -31,6 +33,7 @@ class Closure extends Expr implements FunctionLike
* 'uses' => array(): use()s * 'uses' => array(): use()s
* 'returnType' => null : Return type * 'returnType' => null : Return type
* 'stmts' => array(): Statements * 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attributes groups
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $subNodes = [], array $attributes = []) { public function __construct(array $subNodes = [], array $attributes = []) {
@@ -42,10 +45,11 @@ class Closure extends Expr implements FunctionLike
$returnType = $subNodes['returnType'] ?? null; $returnType = $subNodes['returnType'] ?? null;
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
$this->stmts = $subNodes['stmts'] ?? []; $this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['static', 'byRef', 'params', 'uses', 'returnType', 'stmts']; return ['attrGroups', 'static', 'byRef', 'params', 'uses', 'returnType', 'stmts'];
} }
public function returnsByRef() : bool { public function returnsByRef() : bool {
@@ -65,6 +69,10 @@ class Closure extends Expr implements FunctionLike
return $this->stmts; return $this->stmts;
} }
public function getAttrGroups() : array {
return $this->attrGroups;
}
public function getType() : string { public function getType() : string {
return 'Expr_Closure'; return 'Expr_Closure';
} }

View File

@@ -16,21 +16,28 @@ interface FunctionLike extends Node
/** /**
* List of parameters * List of parameters
* *
* @return Node\Param[] * @return Param[]
*/ */
public function getParams() : array; public function getParams() : array;
/** /**
* Get the declared return type or null * Get the declared return type or null
* *
* @return null|Identifier|Node\Name|Node\NullableType|Node\UnionType * @return null|Identifier|Name|NullableType|UnionType
*/ */
public function getReturnType(); public function getReturnType();
/** /**
* The function body * The function body
* *
* @return Node\Stmt[]|null * @return Stmt[]|null
*/ */
public function getStmts(); public function getStmts();
/**
* Get PHP attribute groups.
*
* @return AttributeGroup[]
*/
public function getAttrGroups() : array;
} }

View File

@@ -18,6 +18,8 @@ class Param extends NodeAbstract
public $default; public $default;
/** @var int */ /** @var int */
public $flags; public $flags;
/** @var AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** /**
* Constructs a parameter node. * Constructs a parameter node.
@@ -27,14 +29,16 @@ class Param extends NodeAbstract
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration * @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
* @param bool $byRef Whether is passed by reference * @param bool $byRef Whether is passed by reference
* @param bool $variadic Whether this is a variadic argument * @param bool $variadic Whether this is a variadic argument
* @param array $flags Optional visibility flags
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
* @param int $flags Optional visibility flags
* @param AttributeGroup[] $attrGroups PHP attribute groups
*/ */
public function __construct( public function __construct(
$var, Expr $default = null, $type = null, $var, Expr $default = null, $type = null,
bool $byRef = false, bool $variadic = false, bool $byRef = false, bool $variadic = false,
array $attributes = [], array $attributes = [],
int $flags = 0 int $flags = 0,
array $attrGroups = []
) { ) {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->type = \is_string($type) ? new Identifier($type) : $type; $this->type = \is_string($type) ? new Identifier($type) : $type;
@@ -43,10 +47,11 @@ class Param extends NodeAbstract
$this->var = $var; $this->var = $var;
$this->default = $default; $this->default = $default;
$this->flags = $flags; $this->flags = $flags;
$this->attrGroups = $attrGroups;
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['flags', 'type', 'byRef', 'variadic', 'var', 'default']; return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default'];
} }
public function getType() : string { public function getType() : string {

View File

@@ -10,6 +10,8 @@ class ClassConst extends Node\Stmt
public $flags; public $flags;
/** @var Node\Const_[] Constant declarations */ /** @var Node\Const_[] Constant declarations */
public $consts; public $consts;
/** @var Node\AttributeGroup[] */
public $attrGroups;
/** /**
* Constructs a class const list node. * Constructs a class const list node.
@@ -17,15 +19,22 @@ class ClassConst extends Node\Stmt
* @param Node\Const_[] $consts Constant declarations * @param Node\Const_[] $consts Constant declarations
* @param int $flags Modifiers * @param int $flags Modifiers
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
*/ */
public function __construct(array $consts, int $flags = 0, array $attributes = []) { public function __construct(
array $consts,
int $flags = 0,
array $attributes = [],
array $attrGroups = []
) {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->flags = $flags; $this->flags = $flags;
$this->consts = $consts; $this->consts = $consts;
$this->attrGroups = $attrGroups;
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['flags', 'consts']; return ['attrGroups', 'flags', 'consts'];
} }
/** /**

View File

@@ -13,6 +13,8 @@ abstract class ClassLike extends Node\Stmt
public $name; public $name;
/** @var Node\Stmt[] Statements */ /** @var Node\Stmt[] Statements */
public $stmts; public $stmts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** /**
* @return TraitUse[] * @return TraitUse[]

View File

@@ -19,6 +19,8 @@ class ClassMethod extends Node\Stmt implements FunctionLike
public $returnType; public $returnType;
/** @var Node\Stmt[]|null Statements */ /** @var Node\Stmt[]|null Statements */
public $stmts; public $stmts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
private static $magicNames = [ private static $magicNames = [
'__construct' => true, '__construct' => true,
@@ -48,6 +50,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
* 'params' => array() : Parameters * 'params' => array() : Parameters
* 'returnType' => null : Return type * 'returnType' => null : Return type
* 'stmts' => array() : Statements * 'stmts' => array() : Statements
* 'attrGroups' => array() : PHP attribute groups
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $subNodes = [], array $attributes = []) { public function __construct($name, array $subNodes = [], array $attributes = []) {
@@ -59,10 +62,11 @@ class ClassMethod extends Node\Stmt implements FunctionLike
$returnType = $subNodes['returnType'] ?? null; $returnType = $subNodes['returnType'] ?? null;
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
$this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : []; $this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['flags', 'byRef', 'name', 'params', 'returnType', 'stmts']; return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'returnType', 'stmts'];
} }
public function returnsByRef() : bool { public function returnsByRef() : bool {
@@ -81,6 +85,10 @@ class ClassMethod extends Node\Stmt implements FunctionLike
return $this->stmts; return $this->stmts;
} }
public function getAttrGroups() : array {
return $this->attrGroups;
}
/** /**
* Whether the method is explicitly or implicitly public. * Whether the method is explicitly or implicitly public.
* *

View File

@@ -32,6 +32,7 @@ class Class_ extends ClassLike
* 'extends' => null : Name of extended class * 'extends' => null : Name of extended class
* 'implements' => array(): Names of implemented interfaces * 'implements' => array(): Names of implemented interfaces
* 'stmts' => array(): Statements * 'stmts' => array(): Statements
* '$attrGroups' => array(): PHP attribute groups
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $subNodes = [], array $attributes = []) { public function __construct($name, array $subNodes = [], array $attributes = []) {
@@ -41,10 +42,11 @@ class Class_ extends ClassLike
$this->extends = $subNodes['extends'] ?? null; $this->extends = $subNodes['extends'] ?? null;
$this->implements = $subNodes['implements'] ?? []; $this->implements = $subNodes['implements'] ?? [];
$this->stmts = $subNodes['stmts'] ?? []; $this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['flags', 'name', 'extends', 'implements', 'stmts']; return ['attrGroups', 'flags', 'name', 'extends', 'implements', 'stmts'];
} }
/** /**

View File

@@ -20,6 +20,8 @@ class Function_ extends Node\Stmt implements FunctionLike
public $returnType; public $returnType;
/** @var Node\Stmt[] Statements */ /** @var Node\Stmt[] Statements */
public $stmts; public $stmts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** /**
* Constructs a function node. * Constructs a function node.
@@ -30,6 +32,7 @@ class Function_ extends Node\Stmt implements FunctionLike
* 'params' => array(): Parameters * 'params' => array(): Parameters
* 'returnType' => null : Return type * 'returnType' => null : Return type
* 'stmts' => array(): Statements * 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attribute groups
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $subNodes = [], array $attributes = []) { public function __construct($name, array $subNodes = [], array $attributes = []) {
@@ -40,10 +43,11 @@ class Function_ extends Node\Stmt implements FunctionLike
$returnType = $subNodes['returnType'] ?? null; $returnType = $subNodes['returnType'] ?? null;
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
$this->stmts = $subNodes['stmts'] ?? []; $this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['byRef', 'name', 'params', 'returnType', 'stmts']; return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts'];
} }
public function returnsByRef() : bool { public function returnsByRef() : bool {
@@ -58,6 +62,10 @@ class Function_ extends Node\Stmt implements FunctionLike
return $this->returnType; return $this->returnType;
} }
public function getAttrGroups() : array {
return $this->attrGroups;
}
/** @return Node\Stmt[] */ /** @return Node\Stmt[] */
public function getStmts() : array { public function getStmts() : array {
return $this->stmts; return $this->stmts;

View File

@@ -16,6 +16,7 @@ class Interface_ extends ClassLike
* @param array $subNodes Array of the following optional subnodes: * @param array $subNodes Array of the following optional subnodes:
* 'extends' => array(): Name of extended interfaces * 'extends' => array(): Name of extended interfaces
* 'stmts' => array(): Statements * 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attribute groups
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $subNodes = [], array $attributes = []) { public function __construct($name, array $subNodes = [], array $attributes = []) {
@@ -23,10 +24,11 @@ class Interface_ extends ClassLike
$this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->extends = $subNodes['extends'] ?? []; $this->extends = $subNodes['extends'] ?? [];
$this->stmts = $subNodes['stmts'] ?? []; $this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['name', 'extends', 'stmts']; return ['attrGroups', 'name', 'extends', 'stmts'];
} }
public function getType() : string { public function getType() : string {

View File

@@ -16,6 +16,8 @@ class Property extends Node\Stmt
public $props; public $props;
/** @var null|Identifier|Name|NullableType|UnionType Type declaration */ /** @var null|Identifier|Name|NullableType|UnionType Type declaration */
public $type; public $type;
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** /**
* Constructs a class property list node. * Constructs a class property list node.
@@ -24,16 +26,18 @@ class Property extends Node\Stmt
* @param PropertyProperty[] $props Properties * @param PropertyProperty[] $props Properties
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration * @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
*/ */
public function __construct(int $flags, array $props, array $attributes = [], $type = null) { public function __construct(int $flags, array $props, array $attributes = [], $type = null, array $attrGroups = []) {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->flags = $flags; $this->flags = $flags;
$this->props = $props; $this->props = $props;
$this->type = \is_string($type) ? new Identifier($type) : $type; $this->type = \is_string($type) ? new Identifier($type) : $type;
$this->attrGroups = $attrGroups;
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['flags', 'type', 'props']; return ['attrGroups', 'flags', 'type', 'props'];
} }
/** /**

View File

@@ -12,16 +12,18 @@ class Trait_ extends ClassLike
* @param string|Node\Identifier $name Name * @param string|Node\Identifier $name Name
* @param array $subNodes Array of the following optional subnodes: * @param array $subNodes Array of the following optional subnodes:
* 'stmts' => array(): Statements * 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attribute groups
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $subNodes = [], array $attributes = []) { public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->stmts = $subNodes['stmts'] ?? []; $this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['name', 'stmts']; return ['attrGroups', 'name', 'stmts'];
} }
public function getType() : string { public function getType() : string {

View File

@@ -75,6 +75,7 @@ class NameResolver extends NodeVisitorAbstract
$interface = $this->resolveClassName($interface); $interface = $this->resolveClassName($interface);
} }
$this->resolveAttrGroups($node);
if (null !== $node->name) { if (null !== $node->name) {
$this->addNamespacedName($node); $this->addNamespacedName($node);
} }
@@ -83,25 +84,32 @@ class NameResolver extends NodeVisitorAbstract
$interface = $this->resolveClassName($interface); $interface = $this->resolveClassName($interface);
} }
$this->resolveAttrGroups($node);
$this->addNamespacedName($node); $this->addNamespacedName($node);
} elseif ($node instanceof Stmt\Trait_) { } elseif ($node instanceof Stmt\Trait_) {
$this->resolveAttrGroups($node);
$this->addNamespacedName($node); $this->addNamespacedName($node);
} elseif ($node instanceof Stmt\Function_) { } elseif ($node instanceof Stmt\Function_) {
$this->addNamespacedName($node);
$this->resolveSignature($node); $this->resolveSignature($node);
$this->resolveAttrGroups($node);
$this->addNamespacedName($node);
} elseif ($node instanceof Stmt\ClassMethod } elseif ($node instanceof Stmt\ClassMethod
|| $node instanceof Expr\Closure || $node instanceof Expr\Closure
|| $node instanceof Expr\ArrowFunction || $node instanceof Expr\ArrowFunction
) { ) {
$this->resolveSignature($node); $this->resolveSignature($node);
$this->resolveAttrGroups($node);
} elseif ($node instanceof Stmt\Property) { } elseif ($node instanceof Stmt\Property) {
if (null !== $node->type) { if (null !== $node->type) {
$node->type = $this->resolveType($node->type); $node->type = $this->resolveType($node->type);
} }
$this->resolveAttrGroups($node);
} elseif ($node instanceof Stmt\Const_) { } elseif ($node instanceof Stmt\Const_) {
foreach ($node->consts as $const) { foreach ($node->consts as $const) {
$this->addNamespacedName($const); $this->addNamespacedName($const);
} }
} else if ($node instanceof Stmt\ClassConst) {
$this->resolveAttrGroups($node);
} elseif ($node instanceof Expr\StaticCall } elseif ($node instanceof Expr\StaticCall
|| $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\StaticPropertyFetch
|| $node instanceof Expr\ClassConstFetch || $node instanceof Expr\ClassConstFetch
@@ -157,6 +165,7 @@ class NameResolver extends NodeVisitorAbstract
private function resolveSignature($node) { private function resolveSignature($node) {
foreach ($node->params as $param) { foreach ($node->params as $param) {
$param->type = $this->resolveType($param->type); $param->type = $this->resolveType($param->type);
$this->resolveAttrGroups($param);
} }
$node->returnType = $this->resolveType($node->returnType); $node->returnType = $this->resolveType($node->returnType);
} }
@@ -225,4 +234,13 @@ class NameResolver extends NodeVisitorAbstract
$node->namespacedName = Name::concat( $node->namespacedName = Name::concat(
$this->nameContext->getNamespace(), (string) $node->name); $this->nameContext->getNamespace(), (string) $node->name);
} }
protected function resolveAttrGroups(Node $node)
{
foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
$attr->name = $this->resolveClassName($attr->name);
}
}
}
} }

View File

@@ -17,11 +17,11 @@ use PhpParser\Node\Stmt;
*/ */
class Php5 extends \PhpParser\ParserAbstract class Php5 extends \PhpParser\ParserAbstract
{ {
protected $tokenToSymbolMapSize = 391; protected $tokenToSymbolMapSize = 392;
protected $actionTableSize = 1069; protected $actionTableSize = 1069;
protected $gotoTableSize = 580; protected $gotoTableSize = 580;
protected $invalidSymbol = 164; protected $invalidSymbol = 165;
protected $errorSymbol = 1; protected $errorSymbol = 1;
protected $defaultAction = -32766; protected $defaultAction = -32766;
protected $unexpectedTokenRule = 32767; protected $unexpectedTokenRule = 32767;
@@ -193,36 +193,37 @@ class Php5 extends \PhpParser\ParserAbstract
"'`'", "'`'",
"']'", "']'",
"'\"'", "'\"'",
"T_NULLSAFE_OBJECT_OPERATOR" "T_NULLSAFE_OBJECT_OPERATOR",
"T_ATTRIBUTE"
); );
protected $tokenToSymbol = array( protected $tokenToSymbol = array(
0, 164, 164, 164, 164, 164, 164, 164, 164, 164, 0, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 55, 162, 164, 159, 54, 37, 164, 165, 165, 165, 55, 162, 165, 159, 54, 37, 165,
157, 158, 52, 49, 8, 50, 51, 53, 164, 164, 157, 158, 52, 49, 8, 50, 51, 53, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 31, 154, 165, 165, 165, 165, 165, 165, 165, 165, 31, 154,
43, 16, 45, 30, 67, 164, 164, 164, 164, 164, 43, 16, 45, 30, 67, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 69, 164, 161, 36, 164, 160, 164, 164, 164, 165, 69, 165, 161, 36, 165, 160, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 155, 35, 156, 57, 164, 164, 164, 165, 165, 165, 155, 35, 156, 57, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
164, 164, 164, 164, 164, 164, 1, 2, 3, 4, 165, 165, 165, 165, 165, 165, 1, 2, 3, 4,
5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 32, 33, 34, 38, 39, 40, 41, 27, 28, 29, 32, 33, 34, 38, 39, 40, 41,
@@ -236,7 +237,7 @@ class Php5 extends \PhpParser\ParserAbstract
124, 125, 126, 127, 128, 129, 130, 131, 163, 132, 124, 125, 126, 127, 128, 129, 130, 131, 163, 132,
133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
153 153, 164
); );
protected $action = array( protected $action = array(

File diff suppressed because it is too large Load Diff

View File

@@ -140,4 +140,5 @@ final class Tokens
const T_NAME_FULLY_QUALIFIED = 388; const T_NAME_FULLY_QUALIFIED = 388;
const T_NAME_QUALIFIED = 389; const T_NAME_QUALIFIED = 389;
const T_NAME_RELATIVE = 390; const T_NAME_RELATIVE = 390;
const T_ATTRIBUTE = 391;
} }

View File

@@ -18,7 +18,8 @@ class Standard extends PrettyPrinterAbstract
// Special nodes // Special nodes
protected function pParam(Node\Param $node) { protected function pParam(Node\Param $node) {
return ($this->pModifiers($node->flags)) return $this->pAttrGroups($node->attrGroups, true)
. $this->pModifiers($node->flags)
. ($node->type ? $this->p($node->type) . ' ' : '') . ($node->type ? $this->p($node->type) . ' ' : '')
. ($node->byRef ? '&' : '') . ($node->byRef ? '&' : '')
. ($node->variadic ? '...' : '') . ($node->variadic ? '...' : '')
@@ -52,6 +53,15 @@ class Standard extends PrettyPrinterAbstract
return '$' . $node->name; return '$' . $node->name;
} }
protected function pAttribute(Node\Attribute $node) {
return $this->p($node->name)
. ($node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : '');
}
protected function pAttributeGroup(Node\AttributeGroup $node) {
return '#[' . $this->pCommaSeparated($node->attrs) . ']';
}
// Names // Names
protected function pName(Name $node) { protected function pName(Name $node) {
@@ -600,7 +610,8 @@ class Standard extends PrettyPrinterAbstract
} }
protected function pExpr_Closure(Expr\Closure $node) { protected function pExpr_Closure(Expr\Closure $node) {
return ($node->static ? 'static ' : '') return $this->pAttrGroups($node->attrGroups, true)
. ($node->static ? 'static ' : '')
. 'function ' . ($node->byRef ? '&' : '') . 'function ' . ($node->byRef ? '&' : '')
. '(' . $this->pCommaSeparated($node->params) . ')' . '(' . $this->pCommaSeparated($node->params) . ')'
. (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')' : '') . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')' : '')
@@ -621,7 +632,8 @@ class Standard extends PrettyPrinterAbstract
} }
protected function pExpr_ArrowFunction(Expr\ArrowFunction $node) { protected function pExpr_ArrowFunction(Expr\ArrowFunction $node) {
return ($node->static ? 'static ' : '') return $this->pAttrGroups($node->attrGroups, true)
. ($node->static ? 'static ' : '')
. 'fn' . ($node->byRef ? '&' : '') . 'fn' . ($node->byRef ? '&' : '')
. '(' . $this->pCommaSeparated($node->params) . ')' . '(' . $this->pCommaSeparated($node->params) . ')'
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
@@ -709,7 +721,8 @@ class Standard extends PrettyPrinterAbstract
} }
protected function pStmt_Interface(Stmt\Interface_ $node) { protected function pStmt_Interface(Stmt\Interface_ $node) {
return 'interface ' . $node->name return $this->pAttrGroups($node->attrGroups)
. 'interface ' . $node->name
. (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '') . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
} }
@@ -719,7 +732,8 @@ class Standard extends PrettyPrinterAbstract
} }
protected function pStmt_Trait(Stmt\Trait_ $node) { protected function pStmt_Trait(Stmt\Trait_ $node) {
return 'trait ' . $node->name return $this->pAttrGroups($node->attrGroups)
. 'trait ' . $node->name
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
} }
@@ -744,7 +758,8 @@ class Standard extends PrettyPrinterAbstract
} }
protected function pStmt_Property(Stmt\Property $node) { protected function pStmt_Property(Stmt\Property $node) {
return (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) return $this->pAttrGroups($node->attrGroups)
. (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags))
. ($node->type ? $this->p($node->type) . ' ' : '') . ($node->type ? $this->p($node->type) . ' ' : '')
. $this->pCommaSeparated($node->props) . ';'; . $this->pCommaSeparated($node->props) . ';';
} }
@@ -755,7 +770,8 @@ class Standard extends PrettyPrinterAbstract
} }
protected function pStmt_ClassMethod(Stmt\ClassMethod $node) { protected function pStmt_ClassMethod(Stmt\ClassMethod $node) {
return $this->pModifiers($node->flags) return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags)
. 'function ' . ($node->byRef ? '&' : '') . $node->name . 'function ' . ($node->byRef ? '&' : '') . $node->name
. '(' . $this->pMaybeMultiline($node->params) . ')' . '(' . $this->pMaybeMultiline($node->params) . ')'
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
@@ -765,12 +781,14 @@ class Standard extends PrettyPrinterAbstract
} }
protected function pStmt_ClassConst(Stmt\ClassConst $node) { protected function pStmt_ClassConst(Stmt\ClassConst $node) {
return $this->pModifiers($node->flags) return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags)
. 'const ' . $this->pCommaSeparated($node->consts) . ';'; . 'const ' . $this->pCommaSeparated($node->consts) . ';';
} }
protected function pStmt_Function(Stmt\Function_ $node) { protected function pStmt_Function(Stmt\Function_ $node) {
return 'function ' . ($node->byRef ? '&' : '') . $node->name return $this->pAttrGroups($node->attrGroups)
. 'function ' . ($node->byRef ? '&' : '') . $node->name
. '(' . $this->pCommaSeparated($node->params) . ')' . '(' . $this->pCommaSeparated($node->params) . ')'
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
@@ -925,7 +943,8 @@ class Standard extends PrettyPrinterAbstract
// Helpers // Helpers
protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) { protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) {
return $this->pModifiers($node->flags) return $this->pAttrGroups($node->attrGroups, $node->name === null)
. $this->pModifiers($node->flags)
. 'class' . $afterClassToken . 'class' . $afterClassToken
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '') . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
@@ -1030,11 +1049,21 @@ class Standard extends PrettyPrinterAbstract
return false; return false;
} }
private function pMaybeMultiline(array $nodes, $trailingComma = false) { private function pMaybeMultiline(array $nodes, bool $trailingComma = false) {
if (!$this->hasNodeWithComments($nodes)) { if (!$this->hasNodeWithComments($nodes)) {
return $this->pCommaSeparated($nodes); return $this->pCommaSeparated($nodes);
} else { } else {
return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl; return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl;
} }
} }
private function pAttrGroups(array $nodes, bool $inline = false): string {
$result = '';
$sep = $inline ? ' ' : $this->nl;
foreach ($nodes as $node) {
$result .= $this->p($node) . $sep;
}
return $result;
}
} }

View File

@@ -1375,6 +1375,7 @@ abstract class PrettyPrinterAbstract
'Stmt_Unset->vars' => ', ', 'Stmt_Unset->vars' => ', ',
'Stmt_Use->uses' => ', ', 'Stmt_Use->uses' => ', ',
'MatchArm->conds' => ', ', 'MatchArm->conds' => ', ',
'AttributeGroup->attrs' => ', ',
// statement lists // statement lists
'Expr_Closure->stmts' => "\n", 'Expr_Closure->stmts' => "\n",
@@ -1395,6 +1396,17 @@ abstract class PrettyPrinterAbstract
'Stmt_Function->stmts' => "\n", 'Stmt_Function->stmts' => "\n",
'Stmt_If->stmts' => "\n", 'Stmt_If->stmts' => "\n",
'Stmt_Namespace->stmts' => "\n", 'Stmt_Namespace->stmts' => "\n",
'Stmt_Class->attrGroups' => "\n",
'Stmt_Interface->attrGroups' => "\n",
'Stmt_Trait->attrGroups' => "\n",
'Stmt_Function->attrGroups' => "\n",
'Stmt_ClassMethod->attrGroups' => "\n",
'Stmt_ClassConst->attrGroups' => "\n",
'Stmt_Property->attrGroups' => "\n",
'Expr_PrintableNewAnonClass->attrGroups' => ' ',
'Expr_Closure->attrGroups' => ' ',
'Expr_ArrowFunction->attrGroups' => ' ',
'Param->attrGroups' => ' ',
'Stmt_Switch->cases' => "\n", 'Stmt_Switch->cases' => "\n",
'Stmt_TraitUse->adaptations' => "\n", 'Stmt_TraitUse->adaptations' => "\n",
'Stmt_TryCatch->stmts' => "\n", 'Stmt_TryCatch->stmts' => "\n",

View File

@@ -121,9 +121,11 @@ class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase
'Modulo by zero' 'Modulo by zero'
], ],
[ [
new Expr\BinaryOp\Div(new Scalar\LNumber(42), new Scalar\LNumber(0)), new Expr\BinaryOp\Plus(new Scalar\LNumber(42), new Scalar\String_("1foo")),
\ErrorException::class, \ErrorException::class,
'Division by zero' \PHP_VERSION_ID >= 80000
? 'A non-numeric value encountered'
: 'A non well formed numeric value encountered'
], ],
]; ];
} }

View File

@@ -275,6 +275,33 @@ class EmulativeTest extends LexerTest
['?->', [ ['?->', [
[Tokens::T_NULLSAFE_OBJECT_OPERATOR, '?->'], [Tokens::T_NULLSAFE_OBJECT_OPERATOR, '?->'],
]], ]],
['#[Attr]', [
[Tokens::T_ATTRIBUTE, '#['],
[Tokens::T_STRING, 'Attr'],
[ord(']'), ']'],
]],
["#[\nAttr\n]", [
[Tokens::T_ATTRIBUTE, '#['],
[Tokens::T_STRING, 'Attr'],
[ord(']'), ']'],
]],
// Test interaction of two patch-based emulators
["<<<LABEL\n LABEL, #[Attr]", [
[Tokens::T_START_HEREDOC, "<<<LABEL\n"],
[Tokens::T_END_HEREDOC, " LABEL"],
[ord(','), ','],
[Tokens::T_ATTRIBUTE, '#['],
[Tokens::T_STRING, 'Attr'],
[ord(']'), ']'],
]],
["#[Attr] <<<LABEL\n LABEL,", [
[Tokens::T_ATTRIBUTE, '#['],
[Tokens::T_STRING, 'Attr'],
[ord(']'), ']'],
[Tokens::T_START_HEREDOC, "<<<LABEL\n"],
[Tokens::T_END_HEREDOC, " LABEL"],
[ord(','), ','],
]],
]; ];
} }
@@ -293,6 +320,22 @@ class EmulativeTest extends LexerTest
['7.4', 'match', [[Tokens::T_STRING, 'match']]], ['7.4', 'match', [[Tokens::T_STRING, 'match']]],
['7.4', 'fn', [[Tokens::T_FN, 'fn']]], ['7.4', 'fn', [[Tokens::T_FN, 'fn']]],
['7.3', 'fn', [[Tokens::T_STRING, 'fn']]], ['7.3', 'fn', [[Tokens::T_STRING, 'fn']]],
// Tested here to skip testLeaveStuffAloneInStrings.
['8.0', '"$foo?->bar"', [
[ord('"'), '"'],
[Tokens::T_VARIABLE, '$foo'],
[Tokens::T_NULLSAFE_OBJECT_OPERATOR, '?->'],
[Tokens::T_STRING, 'bar'],
[ord('"'), '"'],
]],
['8.0', '"$foo?->bar baz"', [
[ord('"'), '"'],
[Tokens::T_VARIABLE, '$foo'],
[Tokens::T_NULLSAFE_OBJECT_OPERATOR, '?->'],
[Tokens::T_STRING, 'bar'],
[Tokens::T_ENCAPSED_AND_WHITESPACE, ' baz'],
[ord('"'), '"'],
]],
]; ];
} }
} }

View File

@@ -248,6 +248,7 @@ PHP;
} }
}, },
"flags": 0, "flags": 0,
"attrGroups": [],
"attributes": { "attributes": {
"startLine": 4, "startLine": 4,
"endLine": 4 "endLine": 4
@@ -275,6 +276,7 @@ PHP;
} }
}, },
"flags": 0, "flags": 0,
"attrGroups": [],
"attributes": { "attributes": {
"startLine": 4, "startLine": 4,
"endLine": 4 "endLine": 4
@@ -302,6 +304,7 @@ PHP;
} }
} }
], ],
"attrGroups": [],
"attributes": { "attributes": {
"startLine": 4, "startLine": 4,
"comments": [ "comments": [

View File

@@ -165,13 +165,8 @@ namespace Baz {
} }
EOC; EOC;
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
$prettyPrinter = new PhpParser\PrettyPrinter\Standard; $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$traverser = new PhpParser\NodeTraverser; $stmts = $this->parseAndResolve($code);
$traverser->addVisitor(new NameResolver);
$stmts = $parser->parse($code);
$stmts = $traverser->traverse($stmts);
$this->assertSame( $this->assertSame(
$this->canonicalize($expectedCode), $this->canonicalize($expectedCode),
@@ -187,33 +182,43 @@ EOC;
<?php <?php
namespace NS; namespace NS;
#[X]
class A extends B implements C, D { class A extends B implements C, D {
use E, F, G { use E, F, G {
f as private g; f as private g;
E::h as i; E::h as i;
E::j insteadof F, G; E::j insteadof F, G;
} }
#[X]
public float $php = 7.4;
public ?Foo $person;
protected static ?bool $probability;
public A|B|int $prop;
#[X]
const C = 1;
} }
#[X]
interface A extends C, D { interface A extends C, D {
public function a(A $a) : A; public function a(A $a) : A;
public function b(A|B|int $a): A|B|int; public function b(A|B|int $a): A|B|int;
} }
class ClassWithTypeProperties { #[X]
public float $php = 7.4; trait A {}
public ?Foo $person;
protected static ?bool $probability;
public A|B|int $prop;
}
function f(A $a) : A {} #[X]
function f(#[X] A $a) : A {}
function f2(array $a) : array {} function f2(array $a) : array {}
function(A $a) : A {};
function fn3(?A $a) : ?A {} function fn3(?A $a) : ?A {}
function fn4(?array $a) : ?array {} function fn4(?array $a) : ?array {}
#[X]
function(A $a) : A {};
#[X]
fn(array $a): array => $a; fn(array $a): array => $a;
fn(A $a): A => $a; fn(A $a): A => $a;
fn(?A $a): ?A => $a; fn(?A $a): ?A => $a;
@@ -236,6 +241,7 @@ EOC;
$expectedCode = <<<'EOC' $expectedCode = <<<'EOC'
namespace NS; namespace NS;
#[\NS\X]
class A extends \NS\B implements \NS\C, \NS\D class A extends \NS\B implements \NS\C, \NS\D
{ {
use \NS\E, \NS\F, \NS\G { use \NS\E, \NS\F, \NS\G {
@@ -243,34 +249,40 @@ class A extends \NS\B implements \NS\C, \NS\D
\NS\E::h as i; \NS\E::h as i;
\NS\E::j insteadof \NS\F, \NS\G; \NS\E::j insteadof \NS\F, \NS\G;
} }
#[\NS\X]
public float $php = 7.4;
public ?\NS\Foo $person;
protected static ?bool $probability;
public \NS\A|\NS\B|int $prop;
#[\NS\X]
const C = 1;
} }
#[\NS\X]
interface A extends \NS\C, \NS\D interface A extends \NS\C, \NS\D
{ {
public function a(\NS\A $a) : \NS\A; public function a(\NS\A $a) : \NS\A;
public function b(\NS\A|\NS\B|int $a) : \NS\A|\NS\B|int; public function b(\NS\A|\NS\B|int $a) : \NS\A|\NS\B|int;
} }
class ClassWithTypeProperties #[\NS\X]
trait A
{ {
public float $php = 7.4;
public ?\NS\Foo $person;
protected static ?bool $probability;
public \NS\A|\NS\B|int $prop;
} }
function f(\NS\A $a) : \NS\A #[\NS\X]
function f(#[\NS\X] \NS\A $a) : \NS\A
{ {
} }
function f2(array $a) : array function f2(array $a) : array
{ {
} }
function (\NS\A $a) : \NS\A {
};
function fn3(?\NS\A $a) : ?\NS\A function fn3(?\NS\A $a) : ?\NS\A
{ {
} }
function fn4(?array $a) : ?array function fn4(?array $a) : ?array
{ {
} }
fn(array $a): array => $a; #[\NS\X] function (\NS\A $a) : \NS\A {
};
#[\NS\X] fn(array $a): array => $a;
fn(\NS\A $a): \NS\A => $a; fn(\NS\A $a): \NS\A => $a;
fn(?\NS\A $a): ?\NS\A => $a; fn(?\NS\A $a): ?\NS\A => $a;
\NS\A::b(); \NS\A::b();
@@ -287,13 +299,8 @@ try {
} }
EOC; EOC;
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
$prettyPrinter = new PhpParser\PrettyPrinter\Standard; $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$traverser = new PhpParser\NodeTraverser; $stmts = $this->parseAndResolve($code);
$traverser->addVisitor(new NameResolver);
$stmts = $parser->parse($code);
$stmts = $traverser->traverse($stmts);
$this->assertSame( $this->assertSame(
$this->canonicalize($expectedCode), $this->canonicalize($expectedCode),
@@ -512,4 +519,14 @@ EOC;
$this->assertEquals( $this->assertEquals(
new Name\FullyQualified('Foo\bar'), $n2->getAttribute('namespacedName')); new Name\FullyQualified('Foo\bar'), $n2->getAttribute('namespacedName'));
} }
private function parseAndResolve(string $code): array
{
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$stmts = $parser->parse($code);
return $traverser->traverse($stmts);
}
} }

View File

@@ -0,0 +1,179 @@
Attributes
-----
<?php
#[A]
class X {
#[A]
public function m(#[A] & $p) {}
#[A]
public
$prop;
#[A]
const
X = 42;
}
#[A]
trait X {}
#[A]
interface X {}
#[A]
function f() {}
new #[A] class {};
#[A] function() {};
#[A] fn()
=> 42;
-----
$attrGroup = new Node\AttributeGroup([
new Node\Attribute(new Node\Name('B'), []),
]);
$stmts[0]->attrGroups[] = $attrGroup;
$stmts[0]->stmts[0]->attrGroups[] = $attrGroup;
$stmts[0]->stmts[0]->params[0]->attrGroups[] = $attrGroup;
$stmts[0]->stmts[1]->attrGroups[] = $attrGroup;
$stmts[0]->stmts[2]->attrGroups[] = $attrGroup;
$stmts[1]->attrGroups[] = $attrGroup;
$stmts[2]->attrGroups[] = $attrGroup;
$stmts[3]->attrGroups[] = $attrGroup;
$stmts[4]->expr->class->attrGroups[] = $attrGroup;
$stmts[5]->expr->attrGroups[] = $attrGroup;
$stmts[6]->expr->attrGroups[] = $attrGroup;
-----
<?php
#[A]
#[B]
class X {
#[A]
#[B]
public function m(#[A] #[B] & $p) {}
#[A]
#[B]
public
$prop;
#[A]
#[B]
const
X = 42;
}
#[A]
#[B]
trait X {}
#[A]
#[B]
interface X {}
#[A]
#[B]
function f() {}
new #[A] #[B] class {};
#[A] #[B] function() {};
#[A] #[B] fn()
=> 42;
-----
<?php
class X {
public function m() {}
public
$prop;
const
X = 42;
}
trait X {}
interface X {}
function f() {}
new class {};
function() {};
fn()
=> 42;
-----
// TODO: Currently we lose formatting for this case.
$attrGroup = new Node\AttributeGroup([
new Node\Attribute(new Node\Name('A'), []),
]);
$stmts[0]->attrGroups[] = $attrGroup;
$stmts[0]->stmts[0]->attrGroups[] = $attrGroup;
$stmts[0]->stmts[1]->attrGroups[] = $attrGroup;
$stmts[0]->stmts[2]->attrGroups[] = $attrGroup;
$stmts[1]->attrGroups[] = $attrGroup;
$stmts[2]->attrGroups[] = $attrGroup;
$stmts[3]->attrGroups[] = $attrGroup;
$stmts[4]->expr->class->attrGroups[] = $attrGroup;
$stmts[5]->expr->attrGroups[] = $attrGroup;
$stmts[6]->expr->attrGroups[] = $attrGroup;
-----
<?php
#[A]
class X
{
#[A]
public function m()
{
}
#[A]
public $prop;
#[A]
const X = 42;
}
#[A]
trait X
{
}
#[A]
interface X
{
}
#[A]
function f()
{
}
new #[A] class
{
};
#[A] function () {
};
#[A] fn() => 42;
-----
<?php
#[ A, B]
class X {};
#[
A,
B,
]
class X {};
-----
$attr = new Node\Attribute(new Node\Name('C'), []);
$stmts[0]->attrGroups[0]->attrs[] = $attr;
$stmts[1]->attrGroups[0]->attrs[] = $attr;
-----
<?php
#[ A, B, C]
class X {};
#[
A,
B,
C,
]
class X {};

View File

@@ -8,6 +8,8 @@ class MyClass {
----- -----
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: MyClass name: MyClass
@@ -17,6 +19,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PROTECTED (2) flags: MODIFIER_PROTECTED (2)
type: null type: null
props: array( props: array(

View File

@@ -161,6 +161,8 @@ function test() {
Syntax error, unexpected '}' from 4:1 to 4:1 Syntax error, unexpected '}' from 4:1 to 4:1
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
@@ -306,6 +308,8 @@ function foo() {
Syntax error, unexpected '}', expecting T_STRING or T_VARIABLE or '{' or '$' from 4:1 to 4:1 Syntax error, unexpected '}', expecting T_STRING or T_VARIABLE or '{' or '$' from 4:1 to 4:1
array( array(
0: Stmt_Function[2:1 - 4:1]( 0: Stmt_Function[2:1 - 4:1](
attrGroups: array(
)
byRef: false byRef: false
name: Identifier[2:10 - 2:12]( name: Identifier[2:10 - 2:12](
name: foo name: foo
@@ -683,6 +687,8 @@ array(
) )
) )
4: Stmt_Class( 4: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: X name: X
@@ -736,6 +742,8 @@ array(
) )
) )
2: Stmt_ClassConst( 2: Stmt_ClassConst(
attrGroups: array(
)
flags: 0 flags: 0
consts: array( consts: array(
0: Const( 0: Const(
@@ -749,6 +757,8 @@ array(
) )
) )
3: Stmt_Property( 3: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: null type: null
props: array( props: array(
@@ -763,6 +773,8 @@ array(
) )
) )
5: Stmt_Interface( 5: Stmt_Interface(
attrGroups: array(
)
name: Identifier( name: Identifier(
name: I name: I
) )
@@ -895,6 +907,8 @@ class Foo {
Syntax error, unexpected T_STRING from 4:5 to 4:9 Syntax error, unexpected T_STRING from 4:5 to 4:9
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Foo name: Foo
@@ -904,6 +918,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: null type: null
props: array( props: array(
@@ -998,12 +1014,16 @@ Syntax error, unexpected ')', expecting T_VARIABLE from 22:21 to 22:21
Syntax error, unexpected ')', expecting T_VARIABLE from 25:13 to 25:13 Syntax error, unexpected ')', expecting T_VARIABLE from 25:13 to 25:13
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: foo name: foo
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(
@@ -1027,12 +1047,16 @@ array(
) )
) )
1: Stmt_Function( 1: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: foo name: foo
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(
@@ -1047,6 +1071,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(
@@ -1070,12 +1096,16 @@ array(
) )
) )
2: Stmt_Function( 2: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: foo name: foo
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -1095,12 +1125,16 @@ array(
) )
) )
3: Stmt_Function( 3: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: foo name: foo
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: true byRef: true
@@ -1120,12 +1154,16 @@ array(
) )
) )
4: Stmt_Function( 4: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: foo name: foo
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(
@@ -1144,6 +1182,8 @@ array(
) )
) )
5: Stmt_Class( 5: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Bar name: Bar
@@ -1153,6 +1193,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: 0 flags: 0
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -1160,6 +1202,8 @@ array(
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(
@@ -1181,10 +1225,14 @@ array(
) )
6: Stmt_Expression( 6: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(
@@ -1219,9 +1267,9 @@ $array = [
]; ];
----- -----
!!php7 !!php7
Syntax error, unexpected T_VARIABLE, expecting ',' or ')' or ']' from 3:18 to 3:34 Syntax error, unexpected T_VARIABLE, expecting ',' or ']' or ')' from 3:18 to 3:34
Syntax error, unexpected T_VARIABLE, expecting ',' or ')' or ']' from 6:12 to 6:28 Syntax error, unexpected T_VARIABLE, expecting ',' or ']' or ')' from 6:12 to 6:28
Syntax error, unexpected T_VARIABLE, expecting ',' or ')' or ']' from 9:21 to 9:37 Syntax error, unexpected T_VARIABLE, expecting ',' or ']' or ')' from 9:21 to 9:37
array( array(
0: Stmt_Expression( 0: Stmt_Expression(
expr: Expr_Assign( expr: Expr_Assign(
@@ -1330,6 +1378,8 @@ function foo() :
Syntax error, unexpected '{' from 3:1 to 3:1 Syntax error, unexpected '{' from 3:1 to 3:1
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: foo name: foo
@@ -1351,4 +1401,4 @@ array(
$a = ["a "thing"]; $a = ["a "thing"];
----- -----
!!php7 !!php7
Syntax error, unexpected T_STRING, expecting ',' or ')' or ']' from 2:11 to 2:15 Syntax error, unexpected T_STRING, expecting ',' or ']' or ')' from 2:11 to 2:15

View File

@@ -59,6 +59,8 @@ array(
) )
) )
1: Stmt_Function( 1: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: getArr name: getArr
@@ -92,6 +94,8 @@ array(
) )
) )
2: Stmt_Function( 2: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: arrGen name: arrGen

View File

@@ -12,10 +12,14 @@ fn(): int => $x;
array( array(
0: Stmt_Expression( 0: Stmt_Expression(
expr: Expr_ArrowFunction( expr: Expr_ArrowFunction(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: bool name: bool
@@ -36,10 +40,14 @@ array(
) )
1: Stmt_Expression( 1: Stmt_Expression(
expr: Expr_ArrowFunction( expr: Expr_ArrowFunction(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -60,10 +68,14 @@ array(
) )
2: Stmt_Expression( 2: Stmt_Expression(
expr: Expr_ArrowFunction( expr: Expr_ArrowFunction(
attrGroups: array(
)
static: true static: true
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: true byRef: true
@@ -82,10 +94,14 @@ array(
) )
3: Stmt_Expression( 3: Stmt_Expression(
expr: Expr_ArrowFunction( expr: Expr_ArrowFunction(
attrGroups: array(
)
static: false static: false
byRef: true byRef: true
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -104,10 +120,14 @@ array(
) )
4: Stmt_Expression( 4: Stmt_Expression(
expr: Expr_ArrowFunction( expr: Expr_ArrowFunction(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -118,6 +138,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -136,6 +158,8 @@ array(
) )
5: Stmt_Expression( 5: Stmt_Expression(
expr: Expr_ArrowFunction( expr: Expr_ArrowFunction(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(

View File

@@ -12,10 +12,14 @@ function() use($a) : \Foo\Bar {};
array( array(
0: Stmt_Expression( 0: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -40,10 +44,14 @@ array(
) )
1: Stmt_Expression( 1: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -69,6 +77,8 @@ array(
) )
2: Stmt_Expression( 2: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
@@ -94,10 +104,14 @@ array(
) )
3: Stmt_Expression( 3: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: true byRef: true
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -117,6 +131,8 @@ array(
) )
4: Stmt_Expression( 4: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: true static: true
byRef: false byRef: false
params: array( params: array(
@@ -130,10 +146,14 @@ array(
) )
5: Stmt_Expression( 5: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -155,6 +175,8 @@ array(
) )
6: Stmt_Expression( 6: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(

View File

@@ -7,6 +7,8 @@ function() use($a,) {};
array( array(
0: Stmt_Expression( 0: Stmt_Expression(
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(

View File

@@ -71,11 +71,13 @@ array(
4: Stmt_Expression( 4: Stmt_Expression(
expr: Scalar_Encapsed( expr: Scalar_Encapsed(
parts: array( parts: array(
0: Expr_Variable( 0: Expr_NullsafePropertyFetch(
var: Expr_Variable(
name: a name: a
) )
1: Scalar_EncapsedStringPart( name: Identifier(
value: ?->b name: b
)
) )
) )
) )

View File

@@ -204,10 +204,14 @@ array(
name: Expr_FuncCall( name: Expr_FuncCall(
name: Expr_FuncCall( name: Expr_FuncCall(
name: Expr_Closure( name: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -274,10 +278,14 @@ array(
name: f name: f
) )
expr: Expr_Closure( expr: Expr_Closure(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false

View File

@@ -54,6 +54,8 @@ class Foo {
----- -----
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Test name: Test
@@ -63,6 +65,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: 0 flags: 0
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -75,6 +79,8 @@ array(
) )
) )
1: Stmt_ClassMethod( 1: Stmt_ClassMethod(
attrGroups: array(
)
flags: 0 flags: 0
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -87,6 +93,8 @@ array(
) )
) )
2: Stmt_ClassMethod( 2: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -99,6 +107,8 @@ array(
) )
) )
3: Stmt_ClassMethod( 3: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -111,6 +121,8 @@ array(
) )
) )
4: Stmt_Property( 4: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: null type: null
props: array( props: array(
@@ -123,6 +135,8 @@ array(
) )
) )
5: Stmt_Property( 5: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: null type: null
props: array( props: array(
@@ -135,6 +149,8 @@ array(
) )
) )
6: Stmt_ClassConst( 6: Stmt_ClassConst(
attrGroups: array(
)
flags: 0 flags: 0
consts: array( consts: array(
0: Const( 0: Const(
@@ -156,6 +172,8 @@ array(
) )
) )
7: Stmt_ClassConst( 7: Stmt_ClassConst(
attrGroups: array(
)
flags: 0 flags: 0
consts: array( consts: array(
0: Const( 0: Const(
@@ -344,6 +362,8 @@ array(
) )
) )
10: Stmt_Class( 10: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Foo name: Foo

View File

@@ -0,0 +1,388 @@
Attributes
-----
<?php
#[
A1,
A2(),
A3(0),
A4(x: 1),
]
function a() {
}
#[A5]
class C {
#[A6]
public function m(
#[A7] $param,
) {}
#[A14]
public $prop;
}
#[A8]
interface I {}
#[A9]
trait T {}
$x = #[A10] function() {};
$y = #[A11] fn() => 0;
$a = #[A12] static function() {};
$b = #[A13] static fn() => 0;
-----
!!php7
array(
0: Stmt_Function(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A1
)
)
args: array(
)
)
1: Attribute(
name: Name(
parts: array(
0: A2
)
)
args: array(
)
)
2: Attribute(
name: Name(
parts: array(
0: A3
)
)
args: array(
0: Arg(
name: null
value: Scalar_LNumber(
value: 0
)
byRef: false
unpack: false
)
)
)
3: Attribute(
name: Name(
parts: array(
0: A4
)
)
args: array(
0: Arg(
name: Identifier(
name: x
)
value: Scalar_LNumber(
value: 1
)
byRef: false
unpack: false
)
)
)
)
)
)
byRef: false
name: Identifier(
name: a
)
params: array(
)
returnType: null
stmts: array(
)
)
1: Stmt_Class(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A5
)
)
args: array(
)
)
)
)
)
flags: 0
name: Identifier(
name: C
)
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassMethod(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A6
)
)
args: array(
)
)
)
)
)
flags: MODIFIER_PUBLIC (1)
byRef: false
name: Identifier(
name: m
)
params: array(
0: Param(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A7
)
)
args: array(
)
)
)
)
)
flags: 0
type: null
byRef: false
variadic: false
var: Expr_Variable(
name: param
)
default: null
)
)
returnType: null
stmts: array(
)
)
1: Stmt_Property(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A14
)
)
args: array(
)
)
)
)
)
flags: MODIFIER_PUBLIC (1)
type: null
props: array(
0: Stmt_PropertyProperty(
name: VarLikeIdentifier(
name: prop
)
default: null
)
)
)
)
)
2: Stmt_Interface(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A8
)
)
args: array(
)
)
)
)
)
name: Identifier(
name: I
)
extends: array(
)
stmts: array(
)
)
3: Stmt_Trait(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A9
)
)
args: array(
)
)
)
)
)
name: Identifier(
name: T
)
stmts: array(
)
)
4: Stmt_Expression(
expr: Expr_Assign(
var: Expr_Variable(
name: x
)
expr: Expr_Closure(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A10
)
)
args: array(
)
)
)
)
)
static: false
byRef: false
params: array(
)
uses: array(
)
returnType: null
stmts: array(
)
)
)
)
5: Stmt_Expression(
expr: Expr_Assign(
var: Expr_Variable(
name: y
)
expr: Expr_ArrowFunction(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A11
)
)
args: array(
)
)
)
)
)
static: false
byRef: false
params: array(
)
returnType: null
expr: Scalar_LNumber(
value: 0
)
)
)
)
6: Stmt_Expression(
expr: Expr_Assign(
var: Expr_Variable(
name: a
)
expr: Expr_Closure(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A12
)
)
args: array(
)
)
)
)
)
static: true
byRef: false
params: array(
)
uses: array(
)
returnType: null
stmts: array(
)
)
)
)
7: Stmt_Expression(
expr: Expr_Assign(
var: Expr_Variable(
name: b
)
expr: Expr_ArrowFunction(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: A13
)
)
args: array(
)
)
)
)
)
static: true
byRef: false
params: array(
)
returnType: null
expr: Scalar_LNumber(
value: 0
)
)
)
)
)

View File

@@ -9,6 +9,8 @@ abstract class A {
----- -----
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: MODIFIER_ABSTRACT (16) flags: MODIFIER_ABSTRACT (16)
name: Identifier( name: Identifier(
name: A name: A
@@ -18,6 +20,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -30,6 +34,8 @@ array(
) )
) )
1: Stmt_ClassMethod( 1: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC | MODIFIER_ABSTRACT (17) flags: MODIFIER_PUBLIC | MODIFIER_ABSTRACT (17)
byRef: false byRef: false
name: Identifier( name: Identifier(

View File

@@ -25,6 +25,8 @@ array(
0: Stmt_Expression( 0: Stmt_Expression(
expr: Expr_New( expr: Expr_New(
class: Stmt_Class( class: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: null name: null
extends: null extends: null
@@ -32,6 +34,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -52,6 +56,8 @@ array(
1: Stmt_Expression( 1: Stmt_Expression(
expr: Expr_New( expr: Expr_New(
class: Stmt_Class( class: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: null name: null
extends: Name( extends: Name(
@@ -81,6 +87,8 @@ array(
2: Stmt_Expression( 2: Stmt_Expression(
expr: Expr_New( expr: Expr_New(
class: Stmt_Class( class: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: null name: null
extends: null extends: null
@@ -88,6 +96,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: null type: null
props: array( props: array(
@@ -108,6 +118,8 @@ array(
3: Stmt_Expression( 3: Stmt_Expression(
expr: Expr_New( expr: Expr_New(
class: Stmt_Class( class: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: null name: null
extends: Name( extends: Name(
@@ -152,6 +164,8 @@ array(
) )
) )
4: Stmt_Class( 4: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -161,6 +175,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -173,6 +189,8 @@ array(
0: Stmt_Return( 0: Stmt_Return(
expr: Expr_New( expr: Expr_New(
class: Stmt_Class( class: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: null name: null
extends: Name( extends: Name(
@@ -184,6 +202,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassConst( 0: Stmt_ClassConst(
attrGroups: array(
)
flags: 0 flags: 0
consts: array( consts: array(
0: Const( 0: Const(

View File

@@ -0,0 +1,66 @@
Class position
-----
<?php
class A {
}
try {
} catch (Exception $e) {
}
class B {
}
?>
-----
!!positions
array(
0: Stmt_Class[3:1 - 4:1](
attrGroups: array(
)
flags: 0
name: Identifier[3:7 - 3:7](
name: A
)
extends: null
implements: array(
)
stmts: array(
)
)
1: Stmt_TryCatch[6:1 - 8:1](
stmts: array(
)
catches: array(
0: Stmt_Catch[7:3 - 8:1](
types: array(
0: Name[7:10 - 7:18](
parts: array(
0: Exception
)
)
)
var: Expr_Variable[7:20 - 7:21](
name: e
)
stmts: array(
)
)
)
finally: null
)
2: Stmt_Class[10:1 - 11:1](
attrGroups: array(
)
flags: 0
name: Identifier[10:7 - 10:7](
name: B
)
extends: null
implements: array(
)
stmts: array(
)
)
)

View File

@@ -17,6 +17,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A

View File

@@ -9,6 +9,8 @@ class A {
Cannot use 'static' as constant modifier from 3:5 to 3:10 Cannot use 'static' as constant modifier from 3:5 to 3:10
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -18,6 +20,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassConst( 0: Stmt_ClassConst(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
consts: array( consts: array(
0: Const( 0: Const(
@@ -43,6 +47,8 @@ class A {
Cannot use 'abstract' as constant modifier from 3:5 to 3:12 Cannot use 'abstract' as constant modifier from 3:5 to 3:12
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -52,6 +58,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassConst( 0: Stmt_ClassConst(
attrGroups: array(
)
flags: MODIFIER_ABSTRACT (16) flags: MODIFIER_ABSTRACT (16)
consts: array( consts: array(
0: Const( 0: Const(
@@ -77,6 +85,8 @@ class A {
Cannot use 'final' as constant modifier from 3:5 to 3:9 Cannot use 'final' as constant modifier from 3:5 to 3:9
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -86,6 +96,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassConst( 0: Stmt_ClassConst(
attrGroups: array(
)
flags: MODIFIER_FINAL (32) flags: MODIFIER_FINAL (32)
consts: array( consts: array(
0: Const( 0: Const(
@@ -111,6 +123,8 @@ class A {
Multiple access type modifiers are not allowed from 3:12 to 3:17 Multiple access type modifiers are not allowed from 3:12 to 3:17
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -120,6 +134,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassConst( 0: Stmt_ClassConst(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
consts: array( consts: array(
0: Const( 0: Const(

View File

@@ -12,6 +12,8 @@ class Foo {
!!php7 !!php7
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Foo name: Foo
@@ -21,6 +23,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassConst( 0: Stmt_ClassConst(
attrGroups: array(
)
flags: 0 flags: 0
consts: array( consts: array(
0: Const( 0: Const(
@@ -34,6 +38,8 @@ array(
) )
) )
1: Stmt_ClassConst( 1: Stmt_ClassConst(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
consts: array( consts: array(
0: Const( 0: Const(
@@ -47,6 +53,8 @@ array(
) )
) )
2: Stmt_ClassConst( 2: Stmt_ClassConst(
attrGroups: array(
)
flags: MODIFIER_PROTECTED (2) flags: MODIFIER_PROTECTED (2)
consts: array( consts: array(
0: Const( 0: Const(
@@ -60,6 +68,8 @@ array(
) )
) )
3: Stmt_ClassConst( 3: Stmt_ClassConst(
attrGroups: array(
)
flags: MODIFIER_PRIVATE (4) flags: MODIFIER_PRIVATE (4)
consts: array( consts: array(
0: Const( 0: Const(

View File

@@ -6,6 +6,8 @@ final class A {}
----- -----
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: MODIFIER_FINAL (32) flags: MODIFIER_FINAL (32)
name: Identifier( name: Identifier(
name: A name: A

View File

@@ -14,6 +14,8 @@ abstract class A {
----- -----
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: MODIFIER_ABSTRACT (16) flags: MODIFIER_ABSTRACT (16)
name: Identifier( name: Identifier(
name: A name: A
@@ -23,6 +25,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
props: array( props: array(
@@ -35,6 +39,8 @@ array(
) )
) )
1: Stmt_Property( 1: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
type: null type: null
props: array( props: array(
@@ -47,6 +53,8 @@ array(
) )
) )
2: Stmt_ClassMethod( 2: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_ABSTRACT (16) flags: MODIFIER_ABSTRACT (16)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -58,6 +66,8 @@ array(
stmts: null stmts: null
) )
3: Stmt_ClassMethod( 3: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_FINAL (32) flags: MODIFIER_FINAL (32)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -70,6 +80,8 @@ array(
) )
) )
4: Stmt_ClassMethod( 4: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -82,6 +94,8 @@ array(
) )
) )
5: Stmt_ClassMethod( 5: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC | MODIFIER_FINAL (40) flags: MODIFIER_STATIC | MODIFIER_FINAL (40)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -94,6 +108,8 @@ array(
) )
) )
6: Stmt_ClassMethod( 6: Stmt_ClassMethod(
attrGroups: array(
)
flags: 0 flags: 0
byRef: false byRef: false
name: Identifier( name: Identifier(

View File

@@ -8,6 +8,8 @@ interface A extends C, D {
----- -----
array( array(
0: Stmt_Interface( 0: Stmt_Interface(
attrGroups: array(
)
name: Identifier( name: Identifier(
name: A name: A
) )
@@ -25,6 +27,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
byRef: false byRef: false
name: Identifier( name: Identifier(

View File

@@ -5,6 +5,8 @@ Invalid modifier combination
Multiple access type modifiers are not allowed from 1:24 to 1:29 Multiple access type modifiers are not allowed from 1:24 to 1:29
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -14,6 +16,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: null type: null
props: array( props: array(
@@ -34,6 +38,8 @@ array(
Multiple access type modifiers are not allowed from 1:24 to 1:32 Multiple access type modifiers are not allowed from 1:24 to 1:32
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -43,6 +49,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC | MODIFIER_PROTECTED (3) flags: MODIFIER_PUBLIC | MODIFIER_PROTECTED (3)
type: null type: null
props: array( props: array(
@@ -63,6 +71,8 @@ array(
Multiple abstract modifiers are not allowed from 1:26 to 1:33 Multiple abstract modifiers are not allowed from 1:26 to 1:33
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -72,6 +82,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_ABSTRACT (16) flags: MODIFIER_ABSTRACT (16)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -91,6 +103,8 @@ array(
Multiple static modifiers are not allowed from 1:24 to 1:29 Multiple static modifiers are not allowed from 1:24 to 1:29
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -100,6 +114,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
type: null type: null
props: array( props: array(
@@ -120,6 +136,8 @@ array(
Multiple final modifiers are not allowed from 1:23 to 1:27 Multiple final modifiers are not allowed from 1:23 to 1:27
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -129,6 +147,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_FINAL (32) flags: MODIFIER_FINAL (32)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -149,6 +169,8 @@ array(
Cannot use the final modifier on an abstract class member from 1:26 to 1:30 Cannot use the final modifier on an abstract class member from 1:26 to 1:30
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -158,6 +180,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_ABSTRACT | MODIFIER_FINAL (48) flags: MODIFIER_ABSTRACT | MODIFIER_FINAL (48)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -178,6 +202,8 @@ array(
Syntax error, unexpected T_FINAL, expecting T_CLASS from 1:16 to 1:20 Syntax error, unexpected T_FINAL, expecting T_CLASS from 1:16 to 1:20
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: MODIFIER_FINAL (32) flags: MODIFIER_FINAL (32)
name: Identifier( name: Identifier(
name: A name: A
@@ -200,6 +226,8 @@ array(
Properties cannot be declared abstract from 1:17 to 1:24 Properties cannot be declared abstract from 1:17 to 1:24
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -209,6 +237,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_ABSTRACT (16) flags: MODIFIER_ABSTRACT (16)
type: null type: null
props: array( props: array(
@@ -229,6 +259,8 @@ array(
Properties cannot be declared final from 1:17 to 1:21 Properties cannot be declared final from 1:17 to 1:21
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -238,6 +270,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_FINAL (32) flags: MODIFIER_FINAL (32)
type: null type: null
props: array( props: array(

View File

@@ -5,6 +5,8 @@ Invalid class name
Cannot use 'self' as class name as it is reserved from 1:13 to 1:16 Cannot use 'self' as class name as it is reserved from 1:13 to 1:16
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: self name: self
@@ -22,6 +24,8 @@ array(
Cannot use 'PARENT' as class name as it is reserved from 1:13 to 1:18 Cannot use 'PARENT' as class name as it is reserved from 1:13 to 1:18
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: PARENT name: PARENT
@@ -45,6 +49,8 @@ array(
Cannot use 'self' as class name as it is reserved from 1:23 to 1:26 Cannot use 'self' as class name as it is reserved from 1:23 to 1:26
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -66,6 +72,8 @@ array(
Cannot use 'PARENT' as class name as it is reserved from 1:23 to 1:28 Cannot use 'PARENT' as class name as it is reserved from 1:23 to 1:28
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -87,6 +95,8 @@ array(
Cannot use 'static' as class name as it is reserved from 1:23 to 1:28 Cannot use 'static' as class name as it is reserved from 1:23 to 1:28
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -108,6 +118,8 @@ array(
Cannot use 'self' as interface name as it is reserved from 1:26 to 1:29 Cannot use 'self' as interface name as it is reserved from 1:26 to 1:29
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -130,6 +142,8 @@ array(
Cannot use 'PARENT' as interface name as it is reserved from 1:26 to 1:31 Cannot use 'PARENT' as interface name as it is reserved from 1:26 to 1:31
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -152,6 +166,8 @@ array(
Cannot use 'static' as interface name as it is reserved from 1:26 to 1:31 Cannot use 'static' as interface name as it is reserved from 1:26 to 1:31
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -174,6 +190,8 @@ array(
Cannot use 'self' as class name as it is reserved from 1:17 to 1:20 Cannot use 'self' as class name as it is reserved from 1:17 to 1:20
array( array(
0: Stmt_Interface( 0: Stmt_Interface(
attrGroups: array(
)
name: Identifier( name: Identifier(
name: self name: self
) )
@@ -189,6 +207,8 @@ array(
Cannot use 'PARENT' as class name as it is reserved from 1:17 to 1:22 Cannot use 'PARENT' as class name as it is reserved from 1:17 to 1:22
array( array(
0: Stmt_Interface( 0: Stmt_Interface(
attrGroups: array(
)
name: Identifier( name: Identifier(
name: PARENT name: PARENT
) )
@@ -210,6 +230,8 @@ array(
Cannot use 'self' as interface name as it is reserved from 1:27 to 1:30 Cannot use 'self' as interface name as it is reserved from 1:27 to 1:30
array( array(
0: Stmt_Interface( 0: Stmt_Interface(
attrGroups: array(
)
name: Identifier( name: Identifier(
name: A name: A
) )
@@ -230,6 +252,8 @@ array(
Cannot use 'PARENT' as interface name as it is reserved from 1:27 to 1:32 Cannot use 'PARENT' as interface name as it is reserved from 1:27 to 1:32
array( array(
0: Stmt_Interface( 0: Stmt_Interface(
attrGroups: array(
)
name: Identifier( name: Identifier(
name: A name: A
) )
@@ -250,6 +274,8 @@ array(
Cannot use 'static' as interface name as it is reserved from 1:27 to 1:32 Cannot use 'static' as interface name as it is reserved from 1:27 to 1:32
array( array(
0: Stmt_Interface( 0: Stmt_Interface(
attrGroups: array(
)
name: Identifier( name: Identifier(
name: A name: A
) )

View File

@@ -10,6 +10,8 @@ class A {
----- -----
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -19,6 +21,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
props: array( props: array(
@@ -31,6 +35,8 @@ array(
) )
) )
1: Stmt_ClassMethod( 1: Stmt_ClassMethod(
attrGroups: array(
)
flags: 0 flags: 0
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -43,6 +49,8 @@ array(
) )
) )
2: Stmt_ClassMethod( 2: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_ABSTRACT | MODIFIER_STATIC (24) flags: MODIFIER_ABSTRACT | MODIFIER_STATIC (24)
byRef: false byRef: false
name: Identifier( name: Identifier(

View File

@@ -11,6 +11,8 @@ class A {
!!php7 !!php7
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -20,6 +22,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: Identifier( type: Identifier(
name: string name: string
@@ -34,6 +38,8 @@ array(
) )
) )
1: Stmt_Property( 1: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PROTECTED | MODIFIER_STATIC (10) flags: MODIFIER_PROTECTED | MODIFIER_STATIC (10)
type: Name( type: Name(
parts: array( parts: array(
@@ -50,6 +56,8 @@ array(
) )
) )
2: Stmt_Property( 2: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PRIVATE (4) flags: MODIFIER_PRIVATE (4)
type: NullableType( type: NullableType(
type: Identifier( type: Identifier(

View File

@@ -13,6 +13,8 @@ class Point {
!!php7 !!php7
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Point name: Point
@@ -22,6 +24,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -29,6 +33,8 @@ array(
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: Identifier( type: Identifier(
name: float name: float
@@ -43,6 +49,8 @@ array(
) )
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: MODIFIER_PROTECTED (2) flags: MODIFIER_PROTECTED (2)
type: Identifier( type: Identifier(
name: array name: array
@@ -58,6 +66,8 @@ array(
) )
) )
2: Param( 2: Param(
attrGroups: array(
)
flags: MODIFIER_PRIVATE (4) flags: MODIFIER_PRIVATE (4)
type: Identifier( type: Identifier(
name: string name: string

View File

@@ -18,6 +18,8 @@ class A extends B implements C, D {
----- -----
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -41,6 +43,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassConst( 0: Stmt_ClassConst(
attrGroups: array(
)
flags: 0 flags: 0
consts: array( consts: array(
0: Const( 0: Const(
@@ -62,6 +66,8 @@ array(
) )
) )
1: Stmt_Property( 1: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: null type: null
props: array( props: array(
@@ -84,6 +90,8 @@ array(
) )
) )
2: Stmt_Property( 2: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PROTECTED (2) flags: MODIFIER_PROTECTED (2)
type: null type: null
props: array( props: array(
@@ -96,6 +104,8 @@ array(
) )
) )
3: Stmt_Property( 3: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PRIVATE (4) flags: MODIFIER_PRIVATE (4)
type: null type: null
props: array( props: array(
@@ -108,6 +118,8 @@ array(
) )
) )
4: Stmt_ClassMethod( 4: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -120,6 +132,8 @@ array(
) )
) )
5: Stmt_ClassMethod( 5: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC | MODIFIER_STATIC (9) flags: MODIFIER_PUBLIC | MODIFIER_STATIC (9)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -127,6 +141,8 @@ array(
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -142,6 +158,8 @@ array(
) )
) )
6: Stmt_ClassMethod( 6: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC | MODIFIER_FINAL (33) flags: MODIFIER_PUBLIC | MODIFIER_FINAL (33)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -158,6 +176,8 @@ array(
) )
) )
7: Stmt_ClassMethod( 7: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PROTECTED (2) flags: MODIFIER_PROTECTED (2)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -170,6 +190,8 @@ array(
) )
) )
8: Stmt_ClassMethod( 8: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PRIVATE (4) flags: MODIFIER_PRIVATE (4)
byRef: false byRef: false
name: Identifier( name: Identifier(

View File

@@ -5,6 +5,8 @@ Some special methods cannot be static
Constructor __construct() cannot be static from 1:17 to 1:22 Constructor __construct() cannot be static from 1:17 to 1:22
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -14,6 +16,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -34,6 +38,8 @@ array(
Destructor __destruct() cannot be static from 1:17 to 1:22 Destructor __destruct() cannot be static from 1:17 to 1:22
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -43,6 +49,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -63,6 +71,8 @@ array(
Clone method __clone() cannot be static from 1:17 to 1:22 Clone method __clone() cannot be static from 1:17 to 1:22
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -72,6 +82,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -92,6 +104,8 @@ array(
Constructor __CONSTRUCT() cannot be static from 1:17 to 1:22 Constructor __CONSTRUCT() cannot be static from 1:17 to 1:22
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -101,6 +115,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -121,6 +137,8 @@ array(
Destructor __Destruct() cannot be static from 1:17 to 1:22 Destructor __Destruct() cannot be static from 1:17 to 1:22
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -130,6 +148,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -150,6 +170,8 @@ array(
Clone method __cLoNe() cannot be static from 1:17 to 1:22 Clone method __cLoNe() cannot be static from 1:17 to 1:22
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: A name: A
@@ -159,6 +181,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_STATIC (8) flags: MODIFIER_STATIC (8)
byRef: false byRef: false
name: Identifier( name: Identifier(

View File

@@ -8,6 +8,8 @@ class Test {
!!php7 !!php7
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Test name: Test
@@ -17,6 +19,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC | MODIFIER_STATIC (9) flags: MODIFIER_PUBLIC | MODIFIER_STATIC (9)
byRef: false byRef: false
name: Identifier( name: Identifier(

View File

@@ -23,11 +23,15 @@ class B {
----- -----
array( array(
0: Stmt_Trait( 0: Stmt_Trait(
attrGroups: array(
)
name: Identifier( name: Identifier(
name: A name: A
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -42,6 +46,8 @@ array(
) )
) )
1: Stmt_Class( 1: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: B name: B

View File

@@ -6,12 +6,16 @@ function test(bool $a, Int $b, FLOAT $c, StRiNg $d, iterable $e, object $f, mixe
!!php7 !!php7
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: bool name: bool
@@ -24,6 +28,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: int name: int
@@ -36,6 +42,8 @@ array(
default: null default: null
) )
2: Param( 2: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: float name: float
@@ -48,6 +56,8 @@ array(
default: null default: null
) )
3: Param( 3: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: string name: string
@@ -60,6 +70,8 @@ array(
default: null default: null
) )
4: Param( 4: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: iterable name: iterable
@@ -72,6 +84,8 @@ array(
default: null default: null
) )
5: Param( 5: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: object name: object
@@ -84,6 +98,8 @@ array(
default: null default: null
) )
6: Param( 6: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: mixed name: mixed

View File

@@ -7,12 +7,16 @@ function &a($b) {}
----- -----
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: a name: a
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: true byRef: true
@@ -28,12 +32,16 @@ array(
) )
) )
1: Stmt_Function( 1: Stmt_Function(
attrGroups: array(
)
byRef: true byRef: true
name: Identifier( name: Identifier(
name: a name: a
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false

View File

@@ -17,6 +17,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: A name: A

View File

@@ -16,12 +16,16 @@ function a(
----- -----
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: a name: a
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -38,6 +42,8 @@ array(
) )
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -50,6 +56,8 @@ array(
) )
) )
2: Param( 2: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -69,6 +77,8 @@ array(
) )
) )
3: Param( 3: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -83,6 +93,8 @@ array(
) )
) )
4: Param( 4: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -97,6 +109,8 @@ array(
) )
) )
5: Param( 5: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -110,6 +124,8 @@ array(
) )
) )
6: Param( 6: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -123,6 +139,8 @@ array(
) )
) )
7: Param( 7: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -144,6 +162,8 @@ array(
) )
) )
8: Param( 8: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false

View File

@@ -8,12 +8,16 @@ function test(?Foo $bar, ?string $foo) : ?Baz {
!!php7 !!php7
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: NullableType( type: NullableType(
type: Name( type: Name(
@@ -30,6 +34,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: NullableType( type: NullableType(
type: Identifier( type: Identifier(

View File

@@ -8,12 +8,16 @@ function foo($bar, ) {
!!php7 !!php7
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: foo name: foo
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -42,6 +46,8 @@ class Foo
!!php7 !!php7
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Foo name: Foo
@@ -51,6 +57,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_ClassMethod( 0: Stmt_ClassMethod(
attrGroups: array(
)
flags: 0 flags: 0
byRef: false byRef: false
name: Identifier( name: Identifier(
@@ -58,6 +66,8 @@ array(
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -68,6 +78,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -93,10 +105,14 @@ fn($foo, ) => $bar;
array( array(
0: Stmt_Expression( 0: Stmt_Expression(
expr: Expr_ArrowFunction( expr: Expr_ArrowFunction(
attrGroups: array(
)
static: false static: false
byRef: false byRef: false
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false

View File

@@ -9,6 +9,8 @@ function test4() : Foo\Bar {}
----- -----
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test1 name: test1
@@ -20,6 +22,8 @@ array(
) )
) )
1: Stmt_Function( 1: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test2 name: test2
@@ -33,6 +37,8 @@ array(
) )
) )
2: Stmt_Function( 2: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test3 name: test3
@@ -46,6 +52,8 @@ array(
) )
) )
3: Stmt_Function( 3: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test4 name: test4

View File

@@ -9,6 +9,8 @@ function a() {
----- -----
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: a name: a

View File

@@ -6,12 +6,16 @@ function a($b, array $c, callable $d, E $f) {}
----- -----
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: a name: a
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -22,6 +26,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: array name: array
@@ -34,6 +40,8 @@ array(
default: null default: null
) )
2: Param( 2: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Identifier( type: Identifier(
name: callable name: callable
@@ -46,6 +54,8 @@ array(
default: null default: null
) )
3: Param( 3: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(

View File

@@ -11,6 +11,8 @@ function test(A|B $a): int|false {}
!!php7 !!php7
array( array(
0: Stmt_Class( 0: Stmt_Class(
attrGroups: array(
)
flags: 0 flags: 0
name: Identifier( name: Identifier(
name: Test name: Test
@@ -20,6 +22,8 @@ array(
) )
stmts: array( stmts: array(
0: Stmt_Property( 0: Stmt_Property(
attrGroups: array(
)
flags: MODIFIER_PUBLIC (1) flags: MODIFIER_PUBLIC (1)
type: UnionType( type: UnionType(
types: array( types: array(
@@ -48,12 +52,16 @@ array(
) )
) )
1: Stmt_Function( 1: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: UnionType( type: UnionType(
types: array( types: array(

View File

@@ -8,12 +8,16 @@ function test($a, Type &... $b) {}
----- -----
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -24,6 +28,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -39,12 +45,16 @@ array(
) )
) )
1: Stmt_Function( 1: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -55,6 +65,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: true byRef: true
@@ -70,12 +82,16 @@ array(
) )
) )
2: Stmt_Function( 2: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -86,6 +102,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(
@@ -105,12 +123,16 @@ array(
) )
) )
3: Stmt_Function( 3: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false
@@ -121,6 +143,8 @@ array(
default: null default: null
) )
1: Param( 1: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: Name( type: Name(
parts: array( parts: array(

View File

@@ -6,12 +6,16 @@ function foo(...$foo = []) {}
Variadic parameter cannot have a default value from 2:24 to 2:25 Variadic parameter cannot have a default value from 2:24 to 2:25
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: foo name: foo
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0 flags: 0
type: null type: null
byRef: false byRef: false

View File

@@ -33,6 +33,8 @@ function gen() {
----- -----
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: gen name: gen

View File

@@ -17,6 +17,8 @@ function gen() {
!!php7 !!php7
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: gen name: gen

View File

@@ -10,6 +10,8 @@ function gen() {
----- -----
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: gen name: gen

View File

@@ -19,4 +19,4 @@ $a?->b($c)?->d;
$a?->b($c)(); $a?->b($c)();
new $a?->b(); new $a?->b();
"{$a?->b}"; "{$a?->b}";
"{$a}?->b"; "{$a?->b}";

View File

@@ -0,0 +1,61 @@
Attributes
-----
<?php
#[
A1,
A2(),
A3(0),
A4(x: 1),
]
function a() {
}
#[A5]
class C {
#[A6]
public function m(
#[A7] $param,
) {}
#[A12]
public $prop;
}
#[A8]
interface I {}
#[A9]
trait T {}
$x = #[A10] function() {};
$y = #[A11] fn() => 0;
new #[A13] class {};
-----
!!php7
#[A1, A2, A3(0), A4(x: 1)]
function a()
{
}
#[A5]
class C
{
#[A6]
public function m(#[A7] $param)
{
}
#[A12]
public $prop;
}
#[A8]
interface I
{
}
#[A9]
trait T
{
}
$x = #[A10] function () {
};
$y = #[A11] fn() => 0;
new #[A13] class
{
};

View File

@@ -101,6 +101,7 @@ switch ($testType) {
| Zend.tests.bug74947 | Zend.tests.bug74947
# pretty print differences due to negative LNumbers # pretty print differences due to negative LNumbers
| Zend.tests.neg_num_string | Zend.tests.neg_num_string
| Zend.tests.numeric_strings.neg_num_string
| Zend.tests.bug72918 | Zend.tests.bug72918
# pretty print difference due to nop statements # pretty print difference due to nop statements
| ext.mbstring.tests.htmlent | ext.mbstring.tests.htmlent