mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-06-25 17:43:47 +02:00
Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
4432ba399e | |||
37ac4ea9c2 | |||
4848a0d734 | |||
8eb194ea1f | |||
e03d63cffb | |||
ce91d139b5 | |||
33d7c8d3d8 | |||
6b409b96bb | |||
f68e1a43ff | |||
f767b9fd9f | |||
2a4bb7ef2c | |||
4abdcde5f1 | |||
d46f261ef9 | |||
38aa0920c9 | |||
a8223f228a | |||
8165cf69fa | |||
46221a0914 | |||
3b87eb721c | |||
e3471d94d3 | |||
c6d052fc58 | |||
8008d07bef | |||
7284a4d019 | |||
d3d1ee470a | |||
893a5bce3f | |||
bec74aceda | |||
7c09e096c9 | |||
0f64504317 | |||
5e36ef732e | |||
dbe56d23de | |||
c64986fa55 | |||
bc7a9bf9c2 | |||
2816485126 | |||
1d1bc8a364 | |||
d520bc9e1d | |||
51e0b30843 | |||
eff72eeffa | |||
658f1be311 | |||
b9b65a2996 | |||
1b479e7592 | |||
b5351f883a | |||
88be6127fa | |||
8a97fa157f | |||
1c13d05035 | |||
c7dc3ce552 | |||
9f6ad686a7 | |||
1899471f80 | |||
8505acd151 | |||
c3e20d9970 | |||
4c22c62783 | |||
f66a32e2df | |||
75abbbd2d4 | |||
39b046007d | |||
e3872b8906 | |||
4a40a84cf6 |
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*.y]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
16
.gitattributes
vendored
16
.gitattributes
vendored
@ -1,9 +1,9 @@
|
||||
/doc export-ignore
|
||||
/test export-ignore
|
||||
/test_old export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.travis.yml export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
/.github export-ignore
|
||||
/doc export-ignore
|
||||
/test export-ignore
|
||||
/test_old export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
UPGRADE-*.md export-ignore
|
||||
UPGRADE-*.md export-ignore
|
||||
|
86
.github/workflows/main.yml
vendored
Normal file
86
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
|
||||
name: Main
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests_70:
|
||||
runs-on: "ubuntu-latest"
|
||||
name: "PHP 7.0 Unit Tests"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "xdebug"
|
||||
php-version: "7.0"
|
||||
tools: composer:v2
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
composer require php-coveralls/php-coveralls:^2.2 --dev --no-update
|
||||
composer update --no-progress --prefer-dist
|
||||
- name: "Tests"
|
||||
run: "php vendor/bin/phpunit --coverage-clover build/logs/clover.xml"
|
||||
- name: Coveralls
|
||||
env:
|
||||
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: "php vendor/bin/php-coveralls"
|
||||
if: ${{ success() }}
|
||||
tests:
|
||||
runs-on: "ubuntu-latest"
|
||||
name: "PHP ${{ matrix.php-version }} Unit Tests"
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.1"
|
||||
- "7.2"
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: composer:v2
|
||||
- name: "Install dependencies"
|
||||
run: "composer update --no-progress --prefer-dist"
|
||||
- name: "PHPUnit"
|
||||
run: "php vendor/bin/phpunit"
|
||||
test_old_73_80:
|
||||
runs-on: "ubuntu-latest"
|
||||
name: "PHP 7.3 Code on PHP 8.0 Integration Tests"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.0"
|
||||
tools: composer:v2
|
||||
- name: "Install PHP 8 dependencies"
|
||||
run: "composer update --no-progress --prefer-dist"
|
||||
- name: "Tests"
|
||||
run: "test_old/run-php-src.sh 7.3.21"
|
||||
test_old_80_70:
|
||||
runs-on: "ubuntu-latest"
|
||||
name: "PHP 8.0 Code on PHP 7.0 Integration Tests"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "7.0"
|
||||
tools: composer:v2
|
||||
- name: "Install PHP 8 dependencies"
|
||||
run: "composer update --no-progress --prefer-dist"
|
||||
- name: "Tests"
|
||||
run: "test_old/run-php-src.sh 8.0.0"
|
43
.travis.yml
43
.travis.yml
@ -1,43 +0,0 @@
|
||||
language: php
|
||||
dist: xenial
|
||||
|
||||
before_install: composer self-update --2
|
||||
|
||||
install: composer update --no-progress --prefer-dist
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- name: PHP 7.0 Unit Tests
|
||||
php: 7.0
|
||||
install:
|
||||
- composer require php-coveralls/php-coveralls:^2.2 --dev --no-update
|
||||
- composer update --no-progress --prefer-dist
|
||||
script: vendor/bin/phpunit --coverage-clover build/logs/clover.xml
|
||||
after_success: php vendor/bin/coveralls
|
||||
- name: PHP 7.1 Unit Tests
|
||||
php: 7.1
|
||||
- name: PHP 7.2 Unit Tests
|
||||
php: 7.2
|
||||
- name: PHP 7.3 Unit Tests
|
||||
php: 7.3
|
||||
- name: PHP 7.4 Unit Tests
|
||||
php: 7.4
|
||||
- name: PHP 8.0 Unit Tests
|
||||
php: nightly
|
||||
install:
|
||||
- composer update --ignore-platform-req=php --no-progress --prefer-dist
|
||||
- name: PHP 7.3 Code on PHP 8.0 Integration Tests
|
||||
php: nightly
|
||||
install:
|
||||
- composer update --ignore-platform-req=php --no-progress --prefer-dist
|
||||
script:
|
||||
- test_old/run-php-src.sh 7.3.21
|
||||
- name: PHP 8.0 Code on PHP 7.0 Integration Tests
|
||||
php: 7.0
|
||||
script:
|
||||
- test_old/run-php-src.sh 8.0.0beta1
|
||||
allow_failures:
|
||||
- name: PHP 8.0 Code on PHP 7.0 Integration Tests
|
||||
fast_finish: true
|
||||
|
||||
script: vendor/bin/phpunit
|
81
CHANGELOG.md
81
CHANGELOG.md
@ -1,8 +1,85 @@
|
||||
Version 4.9.2-dev
|
||||
-----------------
|
||||
Version 4.10.6-dev
|
||||
------------------
|
||||
|
||||
Nothing yet.
|
||||
|
||||
Version 4.10.5 (2020-05-03)
|
||||
---------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* [PHP 8.1] Added support for enums. These are represented using the `Stmt\Enum_` and
|
||||
`Stmt\EnumCase` nodes.
|
||||
* [PHP 8.1] Added support for never type. This type will now be returned as an `Identifier` rather
|
||||
than `Name`.
|
||||
* Added `ClassConst` builder.
|
||||
|
||||
### Changed
|
||||
|
||||
* Non-UTF-8 code units in strings will now be hex-encoded.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed precedence of arrow functions.
|
||||
|
||||
Version 4.10.4 (2020-12-20)
|
||||
---------------------------
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed position information for variable-variables (#741).
|
||||
* Fixed position information for traits/interfaces preceded by if statement (#738).
|
||||
|
||||
Version 4.10.3 (2020-12-03)
|
||||
---------------------------
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed formatting-preserving pretty printing for `"{$x}"`.
|
||||
* Ternary expressions are now treated as non-associative in the pretty printer, in order to
|
||||
generate code that is compatible with the parentheses requirement introduced in PHP 8.
|
||||
* Removed no longer necessary `error_clear_last()` call in lexer, which may interfere with fatal
|
||||
error handlers if invoked during shutdown.
|
||||
|
||||
|
||||
Version 4.10.2 (2020-09-26)
|
||||
------------------
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed check for token emulation conflicts with other libraries.
|
||||
|
||||
Version 4.10.1 (2020-09-23)
|
||||
---------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* Added support for recovering from a missing semicolon after a property or class constant
|
||||
declaration.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix spurious whitespace in formatting-preserving pretty printer when both removing and adding
|
||||
elements at the start of a list.
|
||||
* Fix incorrect case-sensitivity in keyword token emulation.
|
||||
|
||||
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)
|
||||
--------------------------
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
PHP Parser
|
||||
==========
|
||||
|
||||
[](https://travis-ci.org/nikic/PHP-Parser) [](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
|
||||
[](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
|
||||
|
||||
This is a PHP 5.2 to PHP 7.4 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
This is a PHP 5.2 to PHP 8.0 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
manipulation.
|
||||
|
||||
[**Documentation for version 4.x**][doc_master] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 7.4).
|
||||
[**Documentation for version 4.x**][doc_master] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.0).
|
||||
|
||||
[Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).
|
||||
|
||||
@ -15,7 +15,7 @@ Features
|
||||
|
||||
The main features provided by this library are:
|
||||
|
||||
* Parsing PHP 5 and PHP 7 code into an abstract syntax tree (AST).
|
||||
* Parsing PHP 5, PHP 7, and PHP 8 code into an abstract syntax tree (AST).
|
||||
* Invalid code can be parsed into a partial AST.
|
||||
* The AST contains accurate location information.
|
||||
* Dumping the AST in human-readable form.
|
||||
|
@ -1,7 +1,7 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
This project is a PHP 5.2 to PHP 7.4 parser **written in PHP itself**.
|
||||
This project is a PHP 5.2 to PHP 8.0 parser **written in PHP itself**.
|
||||
|
||||
What is this for?
|
||||
-----------------
|
||||
@ -26,7 +26,11 @@ programmatic PHP code analysis are incidentally PHP developers, not C developers
|
||||
What can it parse?
|
||||
------------------
|
||||
|
||||
The parser supports parsing PHP 5.2-7.4.
|
||||
The parser supports parsing PHP 5.2-8.0, with the following exceptions:
|
||||
|
||||
* Namespaced names containing whitespace (e.g. `Foo \ Bar` instead of `Foo\Bar`) are not supported.
|
||||
These are illegal in PHP 8, but are legal in earlier version. However, PHP-Parser does not
|
||||
support them for any version.
|
||||
|
||||
As the parser is based on the tokens returned by `token_get_all` (which is only able to lex the PHP
|
||||
version it runs on), additionally a wrapper for emulating tokens from newer versions is provided.
|
||||
|
@ -36,7 +36,7 @@ if ($e->hasColumnInfo()) {
|
||||
echo $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code)
|
||||
. ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code);
|
||||
// or:
|
||||
echo $e->getMessageWithColumnInfo();
|
||||
echo $e->getMessageWithColumnInfo($code);
|
||||
} else {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
@ -72,4 +72,4 @@ if (null !== $stmts) {
|
||||
}
|
||||
```
|
||||
|
||||
The `NameResolver` visitor also accepts an `ErrorHandler` as a constructor argument.
|
||||
The `NameResolver` visitor also accepts an `ErrorHandler` as a constructor argument.
|
||||
|
140
grammar/php7.y
140
grammar/php7.y
@ -28,7 +28,7 @@ reserved_non_modifiers:
|
||||
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
|
||||
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
|
||||
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN
|
||||
| T_MATCH
|
||||
| T_MATCH | T_ENUM
|
||||
;
|
||||
|
||||
semi_reserved:
|
||||
@ -82,6 +82,31 @@ no_comma:
|
||||
optional_comma:
|
||||
/* 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:
|
||||
statement { $$ = $1; }
|
||||
@ -316,19 +341,33 @@ block_or_error:
|
||||
;
|
||||
|
||||
function_declaration_statement:
|
||||
T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type block_or_error
|
||||
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8]]; }
|
||||
T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type block_or_error
|
||||
{ $$ = 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_entry_type identifier extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]];
|
||||
$this->checkClass($$, #2); }
|
||||
| T_INTERFACE identifier interface_extends_list '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]];
|
||||
$this->checkInterface($$, #2); }
|
||||
| T_TRAIT identifier '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Trait_[$2, ['stmts' => $4]]; }
|
||||
optional_attributes class_entry_type identifier extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Class_[$3, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
|
||||
$this->checkClass($$, #3); }
|
||||
| optional_attributes T_INTERFACE identifier interface_extends_list '{' class_statement_list '}'
|
||||
{ $$ = 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]]; }
|
||||
| optional_attributes T_ENUM identifier enum_scalar_type implements_list '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Enum_[$3, ['scalarType' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
|
||||
$this->checkEnum($$, #3); }
|
||||
;
|
||||
|
||||
enum_scalar_type:
|
||||
/* empty */ { $$ = null; }
|
||||
| ':' type { $$ = $2; }
|
||||
|
||||
enum_case_expr:
|
||||
/* empty */ { $$ = null; }
|
||||
| '=' expr { $$ = $2; }
|
||||
;
|
||||
|
||||
class_entry_type:
|
||||
@ -489,14 +528,14 @@ optional_visibility_modifier:
|
||||
;
|
||||
|
||||
parameter:
|
||||
optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable
|
||||
{ $$ = new Node\Param($5, null, $2, $3, $4, attributes(), $1);
|
||||
optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable
|
||||
{ $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1);
|
||||
$this->checkParam($$); }
|
||||
| optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable '=' expr
|
||||
{ $$ = new Node\Param($5, $7, $2, $3, $4, attributes(), $1);
|
||||
| optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable '=' expr
|
||||
{ $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1);
|
||||
$this->checkParam($$); }
|
||||
| optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis error
|
||||
{ $$ = new Node\Param(Expr\Error[], null, $2, $3, $4, attributes(), $1); }
|
||||
| optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis error
|
||||
{ $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
|
||||
;
|
||||
|
||||
type_expr:
|
||||
@ -571,7 +610,7 @@ non_empty_global_var_list:
|
||||
;
|
||||
|
||||
global_var:
|
||||
simple_variable { $$ = Expr\Variable[$1]; }
|
||||
simple_variable { $$ = $1; }
|
||||
;
|
||||
|
||||
static_var_list:
|
||||
@ -600,15 +639,18 @@ class_statement_list:
|
||||
;
|
||||
|
||||
class_statement:
|
||||
variable_modifiers optional_type_without_static property_declaration_list ';'
|
||||
{ $attrs = attributes();
|
||||
$$ = new Stmt\Property($1, $3, $attrs, $2); $this->checkProperty($$, #1); }
|
||||
| method_modifiers T_CONST class_const_list ';'
|
||||
{ $$ = Stmt\ClassConst[$3, $1]; $this->checkClassConst($$, #1); }
|
||||
| method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body
|
||||
{ $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]];
|
||||
$this->checkClassMethod($$, #1); }
|
||||
optional_attributes variable_modifiers optional_type_without_static property_declaration_list semi
|
||||
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1);
|
||||
$this->checkProperty($$, #2); }
|
||||
| optional_attributes method_modifiers T_CONST class_const_list semi
|
||||
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
|
||||
$this->checkClassConst($$, #2); }
|
||||
| optional_attributes method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body
|
||||
{ $$ = 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]; }
|
||||
| optional_attributes T_CASE identifier enum_case_expr semi
|
||||
{ $$ = Stmt\EnumCase[$3, $4, $1]; }
|
||||
| error { $$ = null; /* will be skipped */ }
|
||||
;
|
||||
|
||||
@ -801,22 +843,28 @@ expr:
|
||||
| T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; }
|
||||
| T_THROW expr { $$ = Expr\Throw_[$2]; }
|
||||
|
||||
| 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]]; }
|
||||
| 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]]; }
|
||||
| T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
|
||||
{ $$ = 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 %prec T_THROW
|
||||
{ $$ = 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
|
||||
block_or_error
|
||||
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8]]; }
|
||||
| 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]]; }
|
||||
| attributes T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
|
||||
{ $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9, 'attrGroups' => $1]]; }
|
||||
| attributes T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
|
||||
{ $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $4, 'params' => $6, 'returnType' => $8, 'expr' => $10, 'attrGroups' => $1]]; }
|
||||
| attributes T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type block_or_error
|
||||
{ $$ = 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:
|
||||
T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2);
|
||||
optional_attributes T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
|
||||
$this->checkClass($$[0], -1); }
|
||||
;
|
||||
|
||||
@ -970,7 +1018,7 @@ callable_expr:
|
||||
;
|
||||
|
||||
callable_variable:
|
||||
simple_variable { $$ = Expr\Variable[$1]; }
|
||||
simple_variable { $$ = $1; }
|
||||
| array_object_dereferencable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| array_object_dereferencable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| function_call { $$ = $1; }
|
||||
@ -995,15 +1043,15 @@ variable:
|
||||
;
|
||||
|
||||
simple_variable:
|
||||
T_VARIABLE { $$ = parseVar($1); }
|
||||
| '$' '{' expr '}' { $$ = $3; }
|
||||
plain_variable { $$ = $1; }
|
||||
| '$' '{' expr '}' { $$ = Expr\Variable[$3]; }
|
||||
| '$' simple_variable { $$ = Expr\Variable[$2]; }
|
||||
| '$' error { $$ = Expr\Error[]; $this->errorState = 2; }
|
||||
| '$' error { $$ = Expr\Variable[Expr\Error[]]; $this->errorState = 2; }
|
||||
;
|
||||
|
||||
static_member_prop_name:
|
||||
simple_variable
|
||||
{ $var = $1; $$ = \is_string($var) ? Node\VarLikeIdentifier[$var] : $var; }
|
||||
{ $var = $1->name; $$ = \is_string($var) ? Node\VarLikeIdentifier[$var] : $var; }
|
||||
;
|
||||
|
||||
static_member:
|
||||
@ -1012,7 +1060,7 @@ static_member:
|
||||
;
|
||||
|
||||
new_variable:
|
||||
simple_variable { $$ = Expr\Variable[$1]; }
|
||||
simple_variable { $$ = $1; }
|
||||
| new_variable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| new_variable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| new_variable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; }
|
||||
@ -1026,13 +1074,13 @@ new_variable:
|
||||
member_name:
|
||||
identifier_ex { $$ = $1; }
|
||||
| '{' expr '}' { $$ = $2; }
|
||||
| simple_variable { $$ = Expr\Variable[$1]; }
|
||||
| simple_variable { $$ = $1; }
|
||||
;
|
||||
|
||||
property_name:
|
||||
identifier { $$ = $1; }
|
||||
| '{' expr '}' { $$ = $2; }
|
||||
| simple_variable { $$ = Expr\Variable[$1]; }
|
||||
| simple_variable { $$ = $1; }
|
||||
| error { $$ = Expr\Error[]; $this->errorState = 2; }
|
||||
;
|
||||
|
||||
|
192
grammar/phpyLang.php
Normal file
192
grammar/phpyLang.php
Normal file
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
///////////////////////////////
|
||||
/// Utility regex constants ///
|
||||
///////////////////////////////
|
||||
|
||||
const LIB = '(?(DEFINE)
|
||||
(?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
|
||||
(?<doubleQuotedString>"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
|
||||
(?<string>(?&singleQuotedString)|(?&doubleQuotedString))
|
||||
(?<comment>/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
|
||||
(?<code>\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
|
||||
)';
|
||||
|
||||
const PARAMS = '\[(?<params>[^[\]]*+(?:\[(?¶ms)\][^[\]]*+)*+)\]';
|
||||
const ARGS = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
|
||||
|
||||
///////////////////////////////
|
||||
/// Preprocessing functions ///
|
||||
///////////////////////////////
|
||||
|
||||
function preprocessGrammar($code) {
|
||||
$code = resolveNodes($code);
|
||||
$code = resolveMacros($code);
|
||||
$code = resolveStackAccess($code);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
function resolveNodes($code) {
|
||||
return preg_replace_callback(
|
||||
'~\b(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
|
||||
function($matches) {
|
||||
// recurse
|
||||
$matches['params'] = resolveNodes($matches['params']);
|
||||
|
||||
$params = magicSplit(
|
||||
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
|
||||
$matches['params']
|
||||
);
|
||||
|
||||
$paramCode = '';
|
||||
foreach ($params as $param) {
|
||||
$paramCode .= $param . ', ';
|
||||
}
|
||||
|
||||
return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())';
|
||||
},
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
function resolveMacros($code) {
|
||||
return preg_replace_callback(
|
||||
'~\b(?<!::|->)(?!array\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~',
|
||||
function($matches) {
|
||||
// recurse
|
||||
$matches['args'] = resolveMacros($matches['args']);
|
||||
|
||||
$name = $matches['name'];
|
||||
$args = magicSplit(
|
||||
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
|
||||
$matches['args']
|
||||
);
|
||||
|
||||
if ('attributes' === $name) {
|
||||
assertArgs(0, $args, $name);
|
||||
return '$this->startAttributeStack[#1] + $this->endAttributes';
|
||||
}
|
||||
|
||||
if ('stackAttributes' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
return '$this->startAttributeStack[' . $args[0] . ']'
|
||||
. ' + $this->endAttributeStack[' . $args[0] . ']';
|
||||
}
|
||||
|
||||
if ('init' === $name) {
|
||||
return '$$ = array(' . implode(', ', $args) . ')';
|
||||
}
|
||||
|
||||
if ('push' === $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
|
||||
}
|
||||
|
||||
if ('pushNormalizing' === $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
|
||||
. ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
|
||||
}
|
||||
|
||||
if ('toArray' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
|
||||
}
|
||||
|
||||
if ('parseVar' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return 'substr(' . $args[0] . ', 1)';
|
||||
}
|
||||
|
||||
if ('parseEncapsed' === $name) {
|
||||
assertArgs(3, $args, $name);
|
||||
|
||||
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
|
||||
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
|
||||
}
|
||||
|
||||
if ('makeNop' === $name) {
|
||||
assertArgs(3, $args, $name);
|
||||
|
||||
return '$startAttributes = ' . $args[1] . ';'
|
||||
. ' if (isset($startAttributes[\'comments\']))'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop($startAttributes + ' . $args[2] . '); }'
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('makeZeroLengthNop' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return '$startAttributes = ' . $args[1] . ';'
|
||||
. ' if (isset($startAttributes[\'comments\']))'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createCommentNopAttributes($startAttributes[\'comments\'])); }'
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('strKind' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && '
|
||||
. '(' . $args[0] . '[0] === \'b\' || ' . $args[0] . '[0] === \'B\')) '
|
||||
. '? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED)';
|
||||
}
|
||||
|
||||
if ('prependLeadingComments' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; '
|
||||
. 'if (!empty($attrs[\'comments\'])) {'
|
||||
. '$stmts[0]->setAttribute(\'comments\', '
|
||||
. 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }';
|
||||
}
|
||||
|
||||
return $matches[0];
|
||||
},
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
function assertArgs($num, $args, $name) {
|
||||
if ($num != count($args)) {
|
||||
die('Wrong argument count for ' . $name . '().');
|
||||
}
|
||||
}
|
||||
|
||||
function resolveStackAccess($code) {
|
||||
$code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
|
||||
$code = preg_replace('/#(\d+)/', '$$1', $code);
|
||||
return $code;
|
||||
}
|
||||
|
||||
function removeTrailingWhitespace($code) {
|
||||
$lines = explode("\n", $code);
|
||||
$lines = array_map('rtrim', $lines);
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
/// Regex helper functions ///
|
||||
//////////////////////////////
|
||||
|
||||
function regex($regex) {
|
||||
return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
|
||||
}
|
||||
|
||||
function magicSplit($regex, $string) {
|
||||
$pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
|
||||
|
||||
foreach ($pieces as &$piece) {
|
||||
$piece = trim($piece);
|
||||
}
|
||||
|
||||
if ($pieces === ['']) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $pieces;
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/phpyLang.php';
|
||||
|
||||
$grammarFileToName = [
|
||||
__DIR__ . '/php5.y' => 'Php5',
|
||||
__DIR__ . '/php7.y' => 'Php7',
|
||||
@ -23,21 +25,6 @@ $options = array_flip($argv);
|
||||
$optionDebug = isset($options['--debug']);
|
||||
$optionKeepTmpGrammar = isset($options['--keep-tmp-grammar']);
|
||||
|
||||
///////////////////////////////
|
||||
/// Utility regex constants ///
|
||||
///////////////////////////////
|
||||
|
||||
const LIB = '(?(DEFINE)
|
||||
(?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
|
||||
(?<doubleQuotedString>"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
|
||||
(?<string>(?&singleQuotedString)|(?&doubleQuotedString))
|
||||
(?<comment>/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
|
||||
(?<code>\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
|
||||
)';
|
||||
|
||||
const PARAMS = '\[(?<params>[^[\]]*+(?:\[(?¶ms)\][^[\]]*+)*+)\]';
|
||||
const ARGS = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
|
||||
|
||||
///////////////////
|
||||
/// Main script ///
|
||||
///////////////////
|
||||
@ -49,10 +36,7 @@ foreach ($grammarFileToName as $grammarFile => $name) {
|
||||
|
||||
$grammarCode = file_get_contents($grammarFile);
|
||||
$grammarCode = str_replace('%tokens', $tokens, $grammarCode);
|
||||
|
||||
$grammarCode = resolveNodes($grammarCode);
|
||||
$grammarCode = resolveMacros($grammarCode);
|
||||
$grammarCode = resolveStackAccess($grammarCode);
|
||||
$grammarCode = preprocessGrammar($grammarCode);
|
||||
|
||||
file_put_contents($tmpGrammarFile, $grammarCode);
|
||||
|
||||
@ -77,151 +61,9 @@ foreach ($grammarFileToName as $grammarFile => $name) {
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
/// Preprocessing functions ///
|
||||
///////////////////////////////
|
||||
|
||||
function resolveNodes($code) {
|
||||
return preg_replace_callback(
|
||||
'~\b(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
|
||||
function($matches) {
|
||||
// recurse
|
||||
$matches['params'] = resolveNodes($matches['params']);
|
||||
|
||||
$params = magicSplit(
|
||||
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
|
||||
$matches['params']
|
||||
);
|
||||
|
||||
$paramCode = '';
|
||||
foreach ($params as $param) {
|
||||
$paramCode .= $param . ', ';
|
||||
}
|
||||
|
||||
return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())';
|
||||
},
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
function resolveMacros($code) {
|
||||
return preg_replace_callback(
|
||||
'~\b(?<!::|->)(?!array\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~',
|
||||
function($matches) {
|
||||
// recurse
|
||||
$matches['args'] = resolveMacros($matches['args']);
|
||||
|
||||
$name = $matches['name'];
|
||||
$args = magicSplit(
|
||||
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
|
||||
$matches['args']
|
||||
);
|
||||
|
||||
if ('attributes' == $name) {
|
||||
assertArgs(0, $args, $name);
|
||||
return '$this->startAttributeStack[#1] + $this->endAttributes';
|
||||
}
|
||||
|
||||
if ('stackAttributes' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
return '$this->startAttributeStack[' . $args[0] . ']'
|
||||
. ' + $this->endAttributeStack[' . $args[0] . ']';
|
||||
}
|
||||
|
||||
if ('init' == $name) {
|
||||
return '$$ = array(' . implode(', ', $args) . ')';
|
||||
}
|
||||
|
||||
if ('push' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
|
||||
}
|
||||
|
||||
if ('pushNormalizing' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
|
||||
. ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
|
||||
}
|
||||
|
||||
if ('toArray' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
|
||||
}
|
||||
|
||||
if ('parseVar' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return 'substr(' . $args[0] . ', 1)';
|
||||
}
|
||||
|
||||
if ('parseEncapsed' == $name) {
|
||||
assertArgs(3, $args, $name);
|
||||
|
||||
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
|
||||
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
|
||||
}
|
||||
|
||||
if ('makeNop' == $name) {
|
||||
assertArgs(3, $args, $name);
|
||||
|
||||
return '$startAttributes = ' . $args[1] . ';'
|
||||
. ' if (isset($startAttributes[\'comments\']))'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop($startAttributes + ' . $args[2] . '); }'
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('makeZeroLengthNop' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return '$startAttributes = ' . $args[1] . ';'
|
||||
. ' if (isset($startAttributes[\'comments\']))'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createCommentNopAttributes($startAttributes[\'comments\'])); }'
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('strKind' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && '
|
||||
. '(' . $args[0] . '[0] === \'b\' || ' . $args[0] . '[0] === \'B\')) '
|
||||
. '? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED)';
|
||||
}
|
||||
|
||||
if ('prependLeadingComments' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; '
|
||||
. 'if (!empty($attrs[\'comments\'])) {'
|
||||
. '$stmts[0]->setAttribute(\'comments\', '
|
||||
. 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }';
|
||||
}
|
||||
|
||||
return $matches[0];
|
||||
},
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
function assertArgs($num, $args, $name) {
|
||||
if ($num != count($args)) {
|
||||
die('Wrong argument count for ' . $name . '().');
|
||||
}
|
||||
}
|
||||
|
||||
function resolveStackAccess($code) {
|
||||
$code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
|
||||
$code = preg_replace('/#(\d+)/', '$$1', $code);
|
||||
return $code;
|
||||
}
|
||||
|
||||
function removeTrailingWhitespace($code) {
|
||||
$lines = explode("\n", $code);
|
||||
$lines = array_map('rtrim', $lines);
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
////////////////////////////////
|
||||
/// Utility helper functions ///
|
||||
////////////////////////////////
|
||||
|
||||
function ensureDirExists($dir) {
|
||||
if (!is_dir($dir)) {
|
||||
@ -237,25 +79,3 @@ function execCmd($cmd) {
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
/// Regex helper functions ///
|
||||
//////////////////////////////
|
||||
|
||||
function regex($regex) {
|
||||
return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
|
||||
}
|
||||
|
||||
function magicSplit($regex, $string) {
|
||||
$pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
|
||||
|
||||
foreach ($pieces as &$piece) {
|
||||
$piece = trim($piece);
|
||||
}
|
||||
|
||||
if ($pieces === ['']) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $pieces;
|
||||
}
|
||||
|
@ -83,6 +83,7 @@
|
||||
%token T_CLASS
|
||||
%token T_TRAIT
|
||||
%token T_INTERFACE
|
||||
%token T_ENUM
|
||||
%token T_EXTENDS
|
||||
%token T_IMPLEMENTS
|
||||
%token T_OBJECT_OPERATOR
|
||||
@ -109,4 +110,6 @@
|
||||
%token T_ELLIPSIS
|
||||
%token T_NAME_FULLY_QUALIFIED
|
||||
%token T_NAME_QUALIFIED
|
||||
%token T_NAME_RELATIVE
|
||||
%token T_NAME_RELATIVE
|
||||
%token T_ATTRIBUTE
|
||||
%token T_ENUM
|
||||
|
103
lib/PhpParser/Builder/ClassConst.php
Normal file
103
lib/PhpParser/Builder/ClassConst.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node\Const_;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class ClassConst implements PhpParser\Builder
|
||||
{
|
||||
protected $flags = 0;
|
||||
protected $attributes = [];
|
||||
protected $constants = [];
|
||||
|
||||
/**
|
||||
* Creates a class constant builder
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value Value
|
||||
*/
|
||||
public function __construct($name, $value) {
|
||||
$this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another constant to const group
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value Value
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addConst($name, $value) {
|
||||
$this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant public.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePublic() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant protected.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtected() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant private.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivate() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the constant.
|
||||
*
|
||||
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDocComment($docComment) {
|
||||
$this->attributes = [
|
||||
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\ClassConst The built constant node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
return new Stmt\ClassConst(
|
||||
$this->constants,
|
||||
$this->flags,
|
||||
$this->attributes
|
||||
);
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ class Namespace_ extends Declaration
|
||||
/**
|
||||
* Returns the built node.
|
||||
*
|
||||
* @return Node The built node
|
||||
* @return Stmt\Namespace_ The built node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
|
||||
|
@ -39,7 +39,7 @@ class Use_ implements Builder
|
||||
/**
|
||||
* Returns the built node.
|
||||
*
|
||||
* @return Node The built node
|
||||
* @return Stmt\Use_ The built node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
return new Stmt\Use_([
|
||||
|
@ -161,6 +161,18 @@ class BuilderFactory
|
||||
return new Builder\Use_($name, Use_::TYPE_CONSTANT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a class constant builder.
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value Value
|
||||
*
|
||||
* @return Builder\ClassConst The created use const builder
|
||||
*/
|
||||
public function classConst($name, $value) : Builder\ClassConst {
|
||||
return new Builder\ClassConst($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates node a for a literal value.
|
||||
*
|
||||
|
@ -17,6 +17,8 @@ use PhpParser\Node\Expr;
|
||||
*/
|
||||
class PrintableNewAnonClassNode extends Expr
|
||||
{
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
/** @var Node\Arg[] Arguments */
|
||||
public $args;
|
||||
/** @var null|Node\Name Name of extended class */
|
||||
@ -27,9 +29,11 @@ class PrintableNewAnonClassNode extends Expr
|
||||
public $stmts;
|
||||
|
||||
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);
|
||||
$this->attrGroups = $attrGroups;
|
||||
$this->args = $args;
|
||||
$this->extends = $extends;
|
||||
$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
|
||||
// to anonymous classes for their own purposes. We simplify ignore the name here.
|
||||
return new self(
|
||||
$newNode->args, $class->extends, $class->implements,
|
||||
$class->attrGroups, $newNode->args, $class->extends, $class->implements,
|
||||
$class->stmts, $newNode->getAttributes()
|
||||
);
|
||||
}
|
||||
@ -52,6 +56,6 @@ class PrintableNewAnonClassNode extends Expr
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['args', 'extends', 'implements', 'stmts'];
|
||||
return ['attrGroups', 'args', 'extends', 'implements', 'stmts'];
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ class TokenStream
|
||||
* @return bool
|
||||
*/
|
||||
public function haveBraces(int $startPos, int $endPos) : bool {
|
||||
return $this->haveTokenImmediatelyBefore($startPos, '{')
|
||||
return ($this->haveTokenImmediatelyBefore($startPos, '{')
|
||||
|| $this->haveTokenImmediatelyBefore($startPos, T_CURLY_OPEN))
|
||||
&& $this->haveTokenImmediatelyAfter($endPos, '}');
|
||||
}
|
||||
|
||||
@ -201,6 +202,7 @@ class TokenStream
|
||||
|
||||
public function haveBracesInRange(int $startPos, int $endPos) {
|
||||
return $this->haveTokenInRange($startPos, $endPos, '{')
|
||||
|| $this->haveTokenInRange($startPos, $endPos, T_CURLY_OPEN)
|
||||
|| $this->haveTokenInRange($startPos, $endPos, '}');
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,6 @@ class Lexer
|
||||
|
||||
$scream = ini_set('xdebug.scream', '0');
|
||||
|
||||
error_clear_last();
|
||||
$this->tokens = @token_get_all($code);
|
||||
$this->postprocessTokens($errorHandler);
|
||||
|
||||
@ -323,7 +322,8 @@ class Lexer
|
||||
$value = $token[1];
|
||||
$id = $this->tokenMap[$token[0]];
|
||||
if (\T_CLOSE_TAG === $token[0]) {
|
||||
$this->prevCloseTagHasNewline = false !== strpos($token[1], "\n");
|
||||
$this->prevCloseTagHasNewline = false !== strpos($token[1], "\n")
|
||||
|| false !== strpos($token[1], "\r");
|
||||
} elseif (\T_INLINE_HTML === $token[0]) {
|
||||
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
|
||||
}
|
||||
@ -405,33 +405,60 @@ class Lexer
|
||||
}
|
||||
|
||||
private function defineCompatibilityTokens() {
|
||||
// PHP 7.4
|
||||
if (!defined('T_BAD_CHARACTER')) {
|
||||
\define('T_BAD_CHARACTER', -1);
|
||||
}
|
||||
if (!defined('T_FN')) {
|
||||
\define('T_FN', -2);
|
||||
}
|
||||
if (!defined('T_COALESCE_EQUAL')) {
|
||||
\define('T_COALESCE_EQUAL', -3);
|
||||
static $compatTokensDefined = false;
|
||||
if ($compatTokensDefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// PHP 8.0
|
||||
if (!defined('T_NAME_QUALIFIED')) {
|
||||
\define('T_NAME_QUALIFIED', -4);
|
||||
$compatTokens = [
|
||||
// PHP 7.4
|
||||
'T_BAD_CHARACTER',
|
||||
'T_FN',
|
||||
'T_COALESCE_EQUAL',
|
||||
// PHP 8.0
|
||||
'T_NAME_QUALIFIED',
|
||||
'T_NAME_FULLY_QUALIFIED',
|
||||
'T_NAME_RELATIVE',
|
||||
'T_MATCH',
|
||||
'T_NULLSAFE_OBJECT_OPERATOR',
|
||||
'T_ATTRIBUTE',
|
||||
// PHP 8.1
|
||||
'T_ENUM',
|
||||
];
|
||||
|
||||
// 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
|
||||
));
|
||||
}
|
||||
$usedTokenIds[$tokenId] = $token;
|
||||
}
|
||||
}
|
||||
if (!defined('T_NAME_FULLY_QUALIFIED')) {
|
||||
\define('T_NAME_FULLY_QUALIFIED', -5);
|
||||
}
|
||||
if (!defined('T_NAME_RELATIVE')) {
|
||||
\define('T_NAME_RELATIVE', -6);
|
||||
}
|
||||
if (!defined('T_MATCH')) {
|
||||
\define('T_MATCH', -7);
|
||||
}
|
||||
if (!defined('T_NULLSAFE_OBJECT_OPERATOR')) {
|
||||
\define('T_NULLSAFE_OBJECT_OPERATOR', -8);
|
||||
|
||||
// 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--;
|
||||
}
|
||||
\define($token, $newTokenId);
|
||||
$newTokenId--;
|
||||
}
|
||||
}
|
||||
|
||||
$compatTokensDefined = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -486,6 +513,8 @@ class Lexer
|
||||
$tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE;
|
||||
$tokenMap[\T_MATCH] = Tokens::T_MATCH;
|
||||
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR;
|
||||
$tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE;
|
||||
$tokenMap[\T_ENUM] = Tokens::T_ENUM;
|
||||
|
||||
return $tokenMap;
|
||||
}
|
||||
|
@ -5,31 +5,29 @@ namespace PhpParser\Lexer;
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\TokenEmulatorInterface;
|
||||
use PhpParser\Parser\Tokens;
|
||||
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
||||
|
||||
class Emulative extends Lexer
|
||||
{
|
||||
const PHP_7_3 = '7.3dev';
|
||||
const PHP_7_4 = '7.4dev';
|
||||
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;
|
||||
const PHP_8_1 = '8.1dev';
|
||||
|
||||
/** @var mixed[] Patches used to reverse changes introduced in the code */
|
||||
private $patches = [];
|
||||
|
||||
/** @var TokenEmulatorInterface[] */
|
||||
private $tokenEmulators = [];
|
||||
/** @var TokenEmulator[] */
|
||||
private $emulators = [];
|
||||
|
||||
/** @var string */
|
||||
private $targetPhpVersion;
|
||||
@ -41,32 +39,53 @@ REGEX;
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_0;
|
||||
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_1;
|
||||
unset($options['phpVersion']);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->tokenEmulators[] = new FnTokenEmulator();
|
||||
$this->tokenEmulators[] = new MatchTokenEmulator();
|
||||
$this->tokenEmulators[] = new CoaleseEqualTokenEmulator();
|
||||
$this->tokenEmulators[] = new NumericLiteralSeparatorEmulator();
|
||||
$this->tokenEmulators[] = new NullsafeTokenEmulator();
|
||||
$emulators = [
|
||||
new FlexibleDocStringEmulator(),
|
||||
new FnTokenEmulator(),
|
||||
new MatchTokenEmulator(),
|
||||
new CoaleseEqualTokenEmulator(),
|
||||
new NumericLiteralSeparatorEmulator(),
|
||||
new NullsafeTokenEmulator(),
|
||||
new AttributeEmulator(),
|
||||
new EnumTokenEmulator(),
|
||||
];
|
||||
|
||||
// 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) {
|
||||
$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
|
||||
parent::startLexing($code, $errorHandler);
|
||||
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
|
||||
$preparedCode = $this->processHeredocNowdoc($code);
|
||||
parent::startLexing($preparedCode, $collector);
|
||||
$collector = new ErrorHandler\Collecting();
|
||||
parent::startLexing($code, $collector);
|
||||
$this->sortPatches();
|
||||
$this->fixupTokens();
|
||||
|
||||
$errors = $collector->getErrors();
|
||||
@ -77,84 +96,28 @@ REGEX;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->tokenEmulators as $tokenEmulator) {
|
||||
$emulatorPhpVersion = $tokenEmulator->getPhpVersion();
|
||||
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);
|
||||
}
|
||||
foreach ($emulators as $emulator) {
|
||||
$this->tokens = $emulator->emulate($code, $this->tokens);
|
||||
}
|
||||
}
|
||||
|
||||
private function isHeredocNowdocEmulationNeeded(string $code): bool
|
||||
{
|
||||
// skip version where this works without emulation
|
||||
if (version_compare(\PHP_VERSION, self::PHP_7_3, '>=')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strpos($code, '<<<') !== false;
|
||||
private function isForwardEmulationNeeded(string $emulatorPhpVersion): bool {
|
||||
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '<')
|
||||
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>=');
|
||||
}
|
||||
|
||||
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 isReverseEmulationNeeded(string $emulatorPhpVersion): bool {
|
||||
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=')
|
||||
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<');
|
||||
}
|
||||
|
||||
private function isEmulationNeeded(string $code): bool
|
||||
private function sortPatches()
|
||||
{
|
||||
foreach ($this->tokenEmulators as $emulativeToken) {
|
||||
if ($emulativeToken->isEmulationNeeded($code)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->isHeredocNowdocEmulationNeeded($code);
|
||||
// Patches may be contributed by different emulators.
|
||||
// Make sure they are sorted by increasing patch position.
|
||||
usort($this->patches, function($p1, $p2) {
|
||||
return $p1[0] <=> $p2[0];
|
||||
});
|
||||
}
|
||||
|
||||
private function fixupTokens()
|
||||
@ -173,7 +136,20 @@ REGEX;
|
||||
for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) {
|
||||
$token = $this->tokens[$i];
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
@ -201,6 +177,11 @@ REGEX;
|
||||
$token[1], $patchText, $patchPos - $pos + $posDelta, 0
|
||||
);
|
||||
$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 {
|
||||
assert(false);
|
||||
}
|
||||
@ -247,7 +228,7 @@ REGEX;
|
||||
if ($patchType === 'add') {
|
||||
$posDelta += strlen($patchText);
|
||||
$lineDelta += substr_count($patchText, "\n");
|
||||
} else {
|
||||
} else if ($patchType === 'remove') {
|
||||
$posDelta -= strlen($patchText);
|
||||
$lineDelta -= substr_count($patchText, "\n");
|
||||
}
|
||||
|
56
lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php
Normal file
56
lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php
Normal 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;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class CoaleseEqualTokenEmulator implements TokenEmulatorInterface
|
||||
final class CoaleseEqualTokenEmulator extends TokenEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
|
31
lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php
Normal file
31
lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class EnumTokenEmulator extends KeywordEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_8_1;
|
||||
}
|
||||
|
||||
public function getKeywordString(): string
|
||||
{
|
||||
return 'enum';
|
||||
}
|
||||
|
||||
public function getKeywordToken(): int
|
||||
{
|
||||
return \T_ENUM;
|
||||
}
|
||||
|
||||
protected function isKeywordContext(array $tokens, int $pos): bool
|
||||
{
|
||||
return parent::isKeywordContext($tokens, $pos)
|
||||
&& isset($tokens[$pos + 2])
|
||||
&& $tokens[$pos + 1][0] === \T_WHITESPACE
|
||||
&& $tokens[$pos + 2][0] === \T_STRING;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -2,26 +2,28 @@
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
abstract class KeywordEmulator implements TokenEmulatorInterface
|
||||
abstract class KeywordEmulator extends TokenEmulator
|
||||
{
|
||||
abstract function getKeywordString(): string;
|
||||
abstract function getKeywordToken(): int;
|
||||
|
||||
public function isEmulationNeeded(string $code): bool
|
||||
{
|
||||
return strpos($code, $this->getKeywordString()) !== false;
|
||||
return strpos(strtolower($code), $this->getKeywordString()) !== false;
|
||||
}
|
||||
|
||||
protected function isKeywordContext(array $tokens, int $pos): bool
|
||||
{
|
||||
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos);
|
||||
return $previousNonSpaceToken === null || $previousNonSpaceToken[0] !== \T_OBJECT_OPERATOR;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
$keywordString = $this->getKeywordString();
|
||||
foreach ($tokens as $i => $token) {
|
||||
if ($token[0] === T_STRING && strtolower($token[1]) === $keywordString) {
|
||||
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $i);
|
||||
if ($previousNonSpaceToken !== null && $previousNonSpaceToken[0] === \T_OBJECT_OPERATOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token[0] === T_STRING && strtolower($token[1]) === $keywordString
|
||||
&& $this->isKeywordContext($tokens, $i)) {
|
||||
$tokens[$i][0] = $this->getKeywordToken();
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class NullsafeTokenEmulator implements TokenEmulatorInterface
|
||||
final class NullsafeTokenEmulator extends TokenEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
@ -22,15 +22,35 @@ final class NullsafeTokenEmulator implements TokenEmulatorInterface
|
||||
// the tokens array on the way
|
||||
$line = 1;
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
if (isset($tokens[$i + 1])) {
|
||||
if ($tokens[$i] === '?' && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) {
|
||||
array_splice($tokens, $i, 2, [
|
||||
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]
|
||||
]);
|
||||
$c--;
|
||||
continue;
|
||||
}
|
||||
if ($tokens[$i] === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) {
|
||||
array_splice($tokens, $i, 2, [
|
||||
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]
|
||||
]);
|
||||
$c--;
|
||||
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])) {
|
||||
$line += substr_count($tokens[$i][1], "\n");
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class NumericLiteralSeparatorEmulator implements TokenEmulatorInterface
|
||||
final class NumericLiteralSeparatorEmulator extends TokenEmulator
|
||||
{
|
||||
const BIN = '(?:0b[01]+(?:_[01]+)*)';
|
||||
const HEX = '(?:0x[0-9a-f]+(?:_[0-9a-f]+)*)';
|
||||
|
36
lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php
Normal file
36
lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php
Normal 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;
|
||||
}
|
||||
}
|
25
lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php
Normal file
25
lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
34
lib/PhpParser/Node/Attribute.php
Normal file
34
lib/PhpParser/Node/Attribute.php
Normal 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';
|
||||
}
|
||||
}
|
29
lib/PhpParser/Node/AttributeGroup.php
Normal file
29
lib/PhpParser/Node/AttributeGroup.php
Normal 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';
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ namespace PhpParser\Node;
|
||||
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
|
||||
{
|
||||
|
@ -22,6 +22,8 @@ class ArrowFunction extends Expr implements FunctionLike
|
||||
|
||||
/** @var Expr */
|
||||
public $expr;
|
||||
/** @var Node\AttributeGroup[] */
|
||||
public $attrGroups;
|
||||
|
||||
/**
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
@ -30,6 +32,7 @@ class ArrowFunction extends Expr implements FunctionLike
|
||||
* 'params' => array() : Parameters
|
||||
* 'returnType' => null : Return type
|
||||
* 'expr' => Expr : Expression body
|
||||
* 'attrGroups' => array() : PHP attribute groups
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $subNodes = [], array $attributes = []) {
|
||||
@ -40,10 +43,11 @@ class ArrowFunction extends Expr implements FunctionLike
|
||||
$returnType = $subNodes['returnType'] ?? null;
|
||||
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
|
||||
$this->expr = $subNodes['expr'] ?? null;
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['static', 'byRef', 'params', 'returnType', 'expr'];
|
||||
return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr'];
|
||||
}
|
||||
|
||||
public function returnsByRef() : bool {
|
||||
@ -58,6 +62,10 @@ class ArrowFunction extends Expr implements FunctionLike
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
public function getAttrGroups() : array {
|
||||
return $this->attrGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node\Stmt\Return_[]
|
||||
*/
|
||||
|
@ -20,6 +20,8 @@ class Closure extends Expr implements FunctionLike
|
||||
public $returnType;
|
||||
/** @var Node\Stmt[] Statements */
|
||||
public $stmts;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
/**
|
||||
* Constructs a lambda function node.
|
||||
@ -31,6 +33,7 @@ class Closure extends Expr implements FunctionLike
|
||||
* 'uses' => array(): use()s
|
||||
* 'returnType' => null : Return type
|
||||
* 'stmts' => array(): Statements
|
||||
* 'attrGroups' => array(): PHP attributes groups
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $subNodes = [], array $attributes = []) {
|
||||
@ -42,10 +45,11 @@ class Closure extends Expr implements FunctionLike
|
||||
$returnType = $subNodes['returnType'] ?? null;
|
||||
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
|
||||
$this->stmts = $subNodes['stmts'] ?? [];
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['static', 'byRef', 'params', 'uses', 'returnType', 'stmts'];
|
||||
return ['attrGroups', 'static', 'byRef', 'params', 'uses', 'returnType', 'stmts'];
|
||||
}
|
||||
|
||||
public function returnsByRef() : bool {
|
||||
@ -64,7 +68,11 @@ class Closure extends Expr implements FunctionLike
|
||||
public function getStmts() : array {
|
||||
return $this->stmts;
|
||||
}
|
||||
|
||||
|
||||
public function getAttrGroups() : array {
|
||||
return $this->attrGroups;
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
return 'Expr_Closure';
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class Variable extends Expr
|
||||
* Constructs a variable node.
|
||||
*
|
||||
* @param string|Expr $name Name
|
||||
* @param array $attributes Additional attributes
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $attributes = []) {
|
||||
$this->attributes = $attributes;
|
||||
|
@ -16,21 +16,28 @@ interface FunctionLike extends Node
|
||||
/**
|
||||
* List of parameters
|
||||
*
|
||||
* @return Node\Param[]
|
||||
* @return Param[]
|
||||
*/
|
||||
public function getParams() : array;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* The function body
|
||||
*
|
||||
* @return Node\Stmt[]|null
|
||||
* @return Stmt[]|null
|
||||
*/
|
||||
public function getStmts();
|
||||
|
||||
/**
|
||||
* Get PHP attribute groups.
|
||||
*
|
||||
* @return AttributeGroup[]
|
||||
*/
|
||||
public function getAttrGroups() : array;
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ class Param extends NodeAbstract
|
||||
public $default;
|
||||
/** @var int */
|
||||
public $flags;
|
||||
/** @var AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
/**
|
||||
* Constructs a parameter node.
|
||||
@ -27,14 +29,16 @@ class Param extends NodeAbstract
|
||||
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
|
||||
* @param bool $byRef Whether is passed by reference
|
||||
* @param bool $variadic Whether this is a variadic argument
|
||||
* @param array $flags Optional visibility flags
|
||||
* @param array $attributes Additional attributes
|
||||
* @param int $flags Optional visibility flags
|
||||
* @param AttributeGroup[] $attrGroups PHP attribute groups
|
||||
*/
|
||||
public function __construct(
|
||||
$var, Expr $default = null, $type = null,
|
||||
bool $byRef = false, bool $variadic = false,
|
||||
array $attributes = [],
|
||||
int $flags = 0
|
||||
int $flags = 0,
|
||||
array $attrGroups = []
|
||||
) {
|
||||
$this->attributes = $attributes;
|
||||
$this->type = \is_string($type) ? new Identifier($type) : $type;
|
||||
@ -43,10 +47,11 @@ class Param extends NodeAbstract
|
||||
$this->var = $var;
|
||||
$this->default = $default;
|
||||
$this->flags = $flags;
|
||||
$this->attrGroups = $attrGroups;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['flags', 'type', 'byRef', 'variadic', 'var', 'default'];
|
||||
return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default'];
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
|
@ -10,22 +10,31 @@ class ClassConst extends Node\Stmt
|
||||
public $flags;
|
||||
/** @var Node\Const_[] Constant declarations */
|
||||
public $consts;
|
||||
/** @var Node\AttributeGroup[] */
|
||||
public $attrGroups;
|
||||
|
||||
/**
|
||||
* Constructs a class const list node.
|
||||
*
|
||||
* @param Node\Const_[] $consts Constant declarations
|
||||
* @param int $flags Modifiers
|
||||
* @param array $attributes Additional attributes
|
||||
* @param Node\Const_[] $consts Constant declarations
|
||||
* @param int $flags Modifiers
|
||||
* @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->flags = $flags;
|
||||
$this->consts = $consts;
|
||||
$this->attrGroups = $attrGroups;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['flags', 'consts'];
|
||||
return ['attrGroups', 'flags', 'consts'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +64,7 @@ class ClassConst extends Node\Stmt
|
||||
public function isPrivate() : bool {
|
||||
return (bool) ($this->flags & Class_::MODIFIER_PRIVATE);
|
||||
}
|
||||
|
||||
|
||||
public function getType() : string {
|
||||
return 'Stmt_ClassConst';
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ abstract class ClassLike extends Node\Stmt
|
||||
public $name;
|
||||
/** @var Node\Stmt[] Statements */
|
||||
public $stmts;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
/**
|
||||
* @return TraitUse[]
|
||||
|
@ -19,6 +19,8 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
public $returnType;
|
||||
/** @var Node\Stmt[]|null Statements */
|
||||
public $stmts;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
private static $magicNames = [
|
||||
'__construct' => true,
|
||||
@ -48,6 +50,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
* 'params' => array() : Parameters
|
||||
* 'returnType' => null : Return type
|
||||
* 'stmts' => array() : Statements
|
||||
* 'attrGroups' => array() : PHP attribute groups
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = [], array $attributes = []) {
|
||||
@ -59,10 +62,11 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
$returnType = $subNodes['returnType'] ?? null;
|
||||
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
|
||||
$this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : [];
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['flags', 'byRef', 'name', 'params', 'returnType', 'stmts'];
|
||||
return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'returnType', 'stmts'];
|
||||
}
|
||||
|
||||
public function returnsByRef() : bool {
|
||||
@ -81,6 +85,10 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
return $this->stmts;
|
||||
}
|
||||
|
||||
public function getAttrGroups() : array {
|
||||
return $this->attrGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the method is explicitly or implicitly public.
|
||||
*
|
||||
@ -120,7 +128,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
|
||||
/**
|
||||
* Whether the method is final.
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFinal() : bool {
|
||||
@ -144,7 +152,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
public function isMagic() : bool {
|
||||
return isset(self::$magicNames[$this->name->toLowerString()]);
|
||||
}
|
||||
|
||||
|
||||
public function getType() : string {
|
||||
return 'Stmt_ClassMethod';
|
||||
}
|
||||
|
@ -28,10 +28,11 @@ class Class_ extends ClassLike
|
||||
*
|
||||
* @param string|Node\Identifier|null $name Name
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
* 'flags' => 0 : Flags
|
||||
* 'extends' => null : Name of extended class
|
||||
* 'implements' => array(): Names of implemented interfaces
|
||||
* 'stmts' => array(): Statements
|
||||
* 'flags' => 0 : Flags
|
||||
* 'extends' => null : Name of extended class
|
||||
* 'implements' => array(): Names of implemented interfaces
|
||||
* 'stmts' => array(): Statements
|
||||
* 'attrGroups' => array(): PHP attribute groups
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = [], array $attributes = []) {
|
||||
@ -41,10 +42,11 @@ class Class_ extends ClassLike
|
||||
$this->extends = $subNodes['extends'] ?? null;
|
||||
$this->implements = $subNodes['implements'] ?? [];
|
||||
$this->stmts = $subNodes['stmts'] ?? [];
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['flags', 'name', 'extends', 'implements', 'stmts'];
|
||||
return ['attrGroups', 'flags', 'name', 'extends', 'implements', 'stmts'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
37
lib/PhpParser/Node/Stmt/EnumCase.php
Normal file
37
lib/PhpParser/Node/Stmt/EnumCase.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\AttributeGroup;
|
||||
|
||||
class EnumCase extends Node\Stmt
|
||||
{
|
||||
/** @var Node\Identifier Enum case name */
|
||||
public $name;
|
||||
/** @var Node\Expr|null Enum case expression */
|
||||
public $expr;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
/**
|
||||
* @param string|Node\Identifier $name Enum case name
|
||||
* @param Node\Expr|null $expr Enum case expression
|
||||
* @param AttributeGroup[] $attrGroups PHP attribute groups
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, Node\Expr $expr = null, array $attrGroups = [], array $attributes = []) {
|
||||
parent::__construct($attributes);
|
||||
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
|
||||
$this->expr = $expr;
|
||||
$this->attrGroups = $attrGroups;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['attrGroups', 'name', 'expr'];
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
return 'Stmt_EnumCase';
|
||||
}
|
||||
}
|
40
lib/PhpParser/Node/Stmt/Enum_.php
Normal file
40
lib/PhpParser/Node/Stmt/Enum_.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node;
|
||||
|
||||
class Enum_ extends ClassLike
|
||||
{
|
||||
/** @var null|Node\Identifier Scalar Type */
|
||||
public $scalarType;
|
||||
/** @var Node\Name[] Names of implemented interfaces */
|
||||
public $implements;
|
||||
|
||||
/**
|
||||
* @param string|Node\Identifier|null $name Name
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
* 'scalarType' => null : Scalar type
|
||||
* 'implements' => array() : Names of implemented interfaces
|
||||
* 'stmts' => array() : Statements
|
||||
* 'attrGroups' => array() : PHP attribute groups
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = [], array $attributes = []) {
|
||||
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
|
||||
$this->scalarType = $subNodes['scalarType'] ?? null;
|
||||
$this->implements = $subNodes['implements'] ?? [];
|
||||
$this->stmts = $subNodes['stmts'] ?? [];
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['attrGroups', 'name', 'scalarType', 'implements', 'stmts'];
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
return 'Stmt_Enum';
|
||||
}
|
||||
}
|
@ -20,6 +20,8 @@ class Function_ extends Node\Stmt implements FunctionLike
|
||||
public $returnType;
|
||||
/** @var Node\Stmt[] Statements */
|
||||
public $stmts;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
/**
|
||||
* Constructs a function node.
|
||||
@ -30,6 +32,7 @@ class Function_ extends Node\Stmt implements FunctionLike
|
||||
* 'params' => array(): Parameters
|
||||
* 'returnType' => null : Return type
|
||||
* 'stmts' => array(): Statements
|
||||
* 'attrGroups' => array(): PHP attribute groups
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = [], array $attributes = []) {
|
||||
@ -40,10 +43,11 @@ class Function_ extends Node\Stmt implements FunctionLike
|
||||
$returnType = $subNodes['returnType'] ?? null;
|
||||
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
|
||||
$this->stmts = $subNodes['stmts'] ?? [];
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['byRef', 'name', 'params', 'returnType', 'stmts'];
|
||||
return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts'];
|
||||
}
|
||||
|
||||
public function returnsByRef() : bool {
|
||||
@ -58,11 +62,15 @@ class Function_ extends Node\Stmt implements FunctionLike
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
public function getAttrGroups() : array {
|
||||
return $this->attrGroups;
|
||||
}
|
||||
|
||||
/** @return Node\Stmt[] */
|
||||
public function getStmts() : array {
|
||||
return $this->stmts;
|
||||
}
|
||||
|
||||
|
||||
public function getType() : string {
|
||||
return 'Stmt_Function';
|
||||
}
|
||||
|
@ -14,8 +14,9 @@ class Interface_ extends ClassLike
|
||||
*
|
||||
* @param string|Node\Identifier $name Name
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
* 'extends' => array(): Name of extended interfaces
|
||||
* 'stmts' => array(): Statements
|
||||
* 'extends' => array(): Name of extended interfaces
|
||||
* 'stmts' => array(): Statements
|
||||
* 'attrGroups' => array(): PHP attribute groups
|
||||
* @param array $attributes Additional 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->extends = $subNodes['extends'] ?? [];
|
||||
$this->stmts = $subNodes['stmts'] ?? [];
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['name', 'extends', 'stmts'];
|
||||
return ['attrGroups', 'name', 'extends', 'stmts'];
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
|
@ -16,6 +16,8 @@ class Property extends Node\Stmt
|
||||
public $props;
|
||||
/** @var null|Identifier|Name|NullableType|UnionType Type declaration */
|
||||
public $type;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
/**
|
||||
* Constructs a class property list node.
|
||||
@ -24,16 +26,18 @@ class Property extends Node\Stmt
|
||||
* @param PropertyProperty[] $props Properties
|
||||
* @param array $attributes Additional attributes
|
||||
* @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->flags = $flags;
|
||||
$this->props = $props;
|
||||
$this->type = \is_string($type) ? new Identifier($type) : $type;
|
||||
$this->attrGroups = $attrGroups;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['flags', 'type', 'props'];
|
||||
return ['attrGroups', 'flags', 'type', 'props'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,17 +11,19 @@ class Trait_ extends ClassLike
|
||||
*
|
||||
* @param string|Node\Identifier $name Name
|
||||
* @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
|
||||
*/
|
||||
public function __construct($name, array $subNodes = [], array $attributes = []) {
|
||||
$this->attributes = $attributes;
|
||||
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
|
||||
$this->stmts = $subNodes['stmts'] ?? [];
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['name', 'stmts'];
|
||||
return ['attrGroups', 'name', 'stmts'];
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
|
@ -75,6 +75,7 @@ class NameResolver extends NodeVisitorAbstract
|
||||
$interface = $this->resolveClassName($interface);
|
||||
}
|
||||
|
||||
$this->resolveAttrGroups($node);
|
||||
if (null !== $node->name) {
|
||||
$this->addNamespacedName($node);
|
||||
}
|
||||
@ -83,25 +84,32 @@ class NameResolver extends NodeVisitorAbstract
|
||||
$interface = $this->resolveClassName($interface);
|
||||
}
|
||||
|
||||
$this->resolveAttrGroups($node);
|
||||
$this->addNamespacedName($node);
|
||||
} elseif ($node instanceof Stmt\Trait_) {
|
||||
$this->resolveAttrGroups($node);
|
||||
$this->addNamespacedName($node);
|
||||
} elseif ($node instanceof Stmt\Function_) {
|
||||
$this->addNamespacedName($node);
|
||||
$this->resolveSignature($node);
|
||||
$this->resolveAttrGroups($node);
|
||||
$this->addNamespacedName($node);
|
||||
} elseif ($node instanceof Stmt\ClassMethod
|
||||
|| $node instanceof Expr\Closure
|
||||
|| $node instanceof Expr\ArrowFunction
|
||||
) {
|
||||
$this->resolveSignature($node);
|
||||
$this->resolveAttrGroups($node);
|
||||
} elseif ($node instanceof Stmt\Property) {
|
||||
if (null !== $node->type) {
|
||||
$node->type = $this->resolveType($node->type);
|
||||
}
|
||||
$this->resolveAttrGroups($node);
|
||||
} elseif ($node instanceof Stmt\Const_) {
|
||||
foreach ($node->consts as $const) {
|
||||
$this->addNamespacedName($const);
|
||||
}
|
||||
} else if ($node instanceof Stmt\ClassConst) {
|
||||
$this->resolveAttrGroups($node);
|
||||
} elseif ($node instanceof Expr\StaticCall
|
||||
|| $node instanceof Expr\StaticPropertyFetch
|
||||
|| $node instanceof Expr\ClassConstFetch
|
||||
@ -157,6 +165,7 @@ class NameResolver extends NodeVisitorAbstract
|
||||
private function resolveSignature($node) {
|
||||
foreach ($node->params as $param) {
|
||||
$param->type = $this->resolveType($param->type);
|
||||
$this->resolveAttrGroups($param);
|
||||
}
|
||||
$node->returnType = $this->resolveType($node->returnType);
|
||||
}
|
||||
@ -225,4 +234,13 @@ class NameResolver extends NodeVisitorAbstract
|
||||
$node->namespacedName = Name::concat(
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,11 @@ use PhpParser\Node\Stmt;
|
||||
*/
|
||||
class Php5 extends \PhpParser\ParserAbstract
|
||||
{
|
||||
protected $tokenToSymbolMapSize = 391;
|
||||
protected $tokenToSymbolMapSize = 393;
|
||||
protected $actionTableSize = 1069;
|
||||
protected $gotoTableSize = 580;
|
||||
|
||||
protected $invalidSymbol = 164;
|
||||
protected $invalidSymbol = 166;
|
||||
protected $errorSymbol = 1;
|
||||
protected $defaultAction = -32766;
|
||||
protected $unexpectedTokenRule = 32767;
|
||||
@ -193,36 +193,38 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
"'`'",
|
||||
"']'",
|
||||
"'\"'",
|
||||
"T_NULLSAFE_OBJECT_OPERATOR"
|
||||
"T_ENUM",
|
||||
"T_NULLSAFE_OBJECT_OPERATOR",
|
||||
"T_ATTRIBUTE"
|
||||
);
|
||||
|
||||
protected $tokenToSymbol = array(
|
||||
0, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 55, 162, 164, 159, 54, 37, 164,
|
||||
157, 158, 52, 49, 8, 50, 51, 53, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 31, 154,
|
||||
43, 16, 45, 30, 67, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 69, 164, 161, 36, 164, 160, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 155, 35, 156, 57, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
|
||||
164, 164, 164, 164, 164, 164, 1, 2, 3, 4,
|
||||
0, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 55, 162, 166, 159, 54, 37, 166,
|
||||
157, 158, 52, 49, 8, 50, 51, 53, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 31, 154,
|
||||
43, 16, 45, 30, 67, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 69, 166, 161, 36, 166, 160, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 155, 35, 156, 57, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 166, 166, 166, 166,
|
||||
166, 166, 166, 166, 166, 166, 1, 2, 3, 4,
|
||||
5, 6, 7, 9, 10, 11, 12, 13, 14, 15,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 32, 33, 34, 38, 39, 40, 41,
|
||||
@ -233,10 +235,10 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
|
||||
104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
|
||||
114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
|
||||
124, 125, 126, 127, 128, 129, 130, 131, 163, 132,
|
||||
133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
|
||||
143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
|
||||
153
|
||||
124, 125, 126, 127, 128, 163, 129, 130, 131, 164,
|
||||
132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
|
||||
142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
|
||||
152, 153, 165
|
||||
);
|
||||
|
||||
protected $action = array(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -114,30 +114,32 @@ final class Tokens
|
||||
const T_CLASS = 362;
|
||||
const T_TRAIT = 363;
|
||||
const T_INTERFACE = 364;
|
||||
const T_EXTENDS = 365;
|
||||
const T_IMPLEMENTS = 366;
|
||||
const T_OBJECT_OPERATOR = 367;
|
||||
const T_NULLSAFE_OBJECT_OPERATOR = 368;
|
||||
const T_LIST = 369;
|
||||
const T_ARRAY = 370;
|
||||
const T_CALLABLE = 371;
|
||||
const T_CLASS_C = 372;
|
||||
const T_TRAIT_C = 373;
|
||||
const T_METHOD_C = 374;
|
||||
const T_FUNC_C = 375;
|
||||
const T_LINE = 376;
|
||||
const T_FILE = 377;
|
||||
const T_START_HEREDOC = 378;
|
||||
const T_END_HEREDOC = 379;
|
||||
const T_DOLLAR_OPEN_CURLY_BRACES = 380;
|
||||
const T_CURLY_OPEN = 381;
|
||||
const T_PAAMAYIM_NEKUDOTAYIM = 382;
|
||||
const T_NAMESPACE = 383;
|
||||
const T_NS_C = 384;
|
||||
const T_DIR = 385;
|
||||
const T_NS_SEPARATOR = 386;
|
||||
const T_ELLIPSIS = 387;
|
||||
const T_NAME_FULLY_QUALIFIED = 388;
|
||||
const T_NAME_QUALIFIED = 389;
|
||||
const T_NAME_RELATIVE = 390;
|
||||
const T_ENUM = 365;
|
||||
const T_EXTENDS = 366;
|
||||
const T_IMPLEMENTS = 367;
|
||||
const T_OBJECT_OPERATOR = 368;
|
||||
const T_NULLSAFE_OBJECT_OPERATOR = 369;
|
||||
const T_LIST = 370;
|
||||
const T_ARRAY = 371;
|
||||
const T_CALLABLE = 372;
|
||||
const T_CLASS_C = 373;
|
||||
const T_TRAIT_C = 374;
|
||||
const T_METHOD_C = 375;
|
||||
const T_FUNC_C = 376;
|
||||
const T_LINE = 377;
|
||||
const T_FILE = 378;
|
||||
const T_START_HEREDOC = 379;
|
||||
const T_END_HEREDOC = 380;
|
||||
const T_DOLLAR_OPEN_CURLY_BRACES = 381;
|
||||
const T_CURLY_OPEN = 382;
|
||||
const T_PAAMAYIM_NEKUDOTAYIM = 383;
|
||||
const T_NAMESPACE = 384;
|
||||
const T_NS_C = 385;
|
||||
const T_DIR = 386;
|
||||
const T_NS_SEPARATOR = 387;
|
||||
const T_ELLIPSIS = 388;
|
||||
const T_NAME_FULLY_QUALIFIED = 389;
|
||||
const T_NAME_QUALIFIED = 390;
|
||||
const T_NAME_RELATIVE = 391;
|
||||
const T_ATTRIBUTE = 392;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Enum_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
@ -60,7 +61,7 @@ abstract class ParserAbstract implements Parser
|
||||
|
||||
/** @var int[] Map of states to a displacement into the $action table. The corresponding action for this
|
||||
* state/symbol pair is $action[$actionBase[$state] + $symbol]. If $actionBase[$state] is 0, the
|
||||
action is defaulted, i.e. $actionDefault[$state] should be used instead. */
|
||||
* action is defaulted, i.e. $actionDefault[$state] should be used instead. */
|
||||
protected $actionBase;
|
||||
/** @var int[] Table of actions. Indexed according to $actionBase comment. */
|
||||
protected $action;
|
||||
@ -219,10 +220,7 @@ abstract class ParserAbstract implements Parser
|
||||
));
|
||||
}
|
||||
|
||||
// This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get
|
||||
// the attributes of the next token, even though they don't contain it themselves.
|
||||
$this->startAttributeStack[$stackPos+1] = $startAttributes;
|
||||
$this->endAttributeStack[$stackPos+1] = $endAttributes;
|
||||
// Allow productions to access the start attributes of the lookahead token.
|
||||
$this->lookaheadStartAttributes = $startAttributes;
|
||||
|
||||
//$this->traceRead($symbol);
|
||||
@ -294,7 +292,8 @@ abstract class ParserAbstract implements Parser
|
||||
|
||||
/* Goto - shift nonterminal */
|
||||
$lastEndAttributes = $this->endAttributeStack[$stackPos];
|
||||
$stackPos -= $this->ruleToLength[$rule];
|
||||
$ruleLength = $this->ruleToLength[$rule];
|
||||
$stackPos -= $ruleLength;
|
||||
$nonTerminal = $this->ruleToNonTerminal[$rule];
|
||||
$idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos];
|
||||
if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) {
|
||||
@ -307,6 +306,10 @@ abstract class ParserAbstract implements Parser
|
||||
$stateStack[$stackPos] = $state;
|
||||
$this->semStack[$stackPos] = $this->semValue;
|
||||
$this->endAttributeStack[$stackPos] = $lastEndAttributes;
|
||||
if ($ruleLength === 0) {
|
||||
// Empty productions use the start attributes of the lookahead token.
|
||||
$this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes;
|
||||
}
|
||||
} else {
|
||||
/* error */
|
||||
switch ($this->errorState) {
|
||||
@ -340,6 +343,7 @@ abstract class ParserAbstract implements Parser
|
||||
|
||||
// We treat the error symbol as being empty, so we reset the end attributes
|
||||
// to the end attributes of the last non-error symbol
|
||||
$this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes;
|
||||
$this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1];
|
||||
$this->endAttributes = $this->endAttributeStack[$stackPos - 1];
|
||||
break;
|
||||
@ -659,6 +663,7 @@ abstract class ParserAbstract implements Parser
|
||||
'null' => true,
|
||||
'false' => true,
|
||||
'mixed' => true,
|
||||
'never' => true,
|
||||
];
|
||||
|
||||
if (!$name->isUnqualified()) {
|
||||
@ -909,22 +914,17 @@ abstract class ParserAbstract implements Parser
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkClass(Class_ $node, $namePos) {
|
||||
if (null !== $node->name && $node->name->isSpecialClassName()) {
|
||||
private function checkClassName($name, $namePos) {
|
||||
if (null !== $name && $name->isSpecialClassName()) {
|
||||
$this->emitError(new Error(
|
||||
sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name),
|
||||
sprintf('Cannot use \'%s\' as class name as it is reserved', $name),
|
||||
$this->getAttributesAt($namePos)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($node->extends && $node->extends->isSpecialClassName()) {
|
||||
$this->emitError(new Error(
|
||||
sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends),
|
||||
$node->extends->getAttributes()
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($node->implements as $interface) {
|
||||
private function checkImplementedInterfaces(array $interfaces) {
|
||||
foreach ($interfaces as $interface) {
|
||||
if ($interface->isSpecialClassName()) {
|
||||
$this->emitError(new Error(
|
||||
sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
|
||||
@ -934,22 +934,27 @@ abstract class ParserAbstract implements Parser
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkInterface(Interface_ $node, $namePos) {
|
||||
if (null !== $node->name && $node->name->isSpecialClassName()) {
|
||||
protected function checkClass(Class_ $node, $namePos) {
|
||||
$this->checkClassName($node->name, $namePos);
|
||||
|
||||
if ($node->extends && $node->extends->isSpecialClassName()) {
|
||||
$this->emitError(new Error(
|
||||
sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name),
|
||||
$this->getAttributesAt($namePos)
|
||||
sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends),
|
||||
$node->extends->getAttributes()
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($node->extends as $interface) {
|
||||
if ($interface->isSpecialClassName()) {
|
||||
$this->emitError(new Error(
|
||||
sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
|
||||
$interface->getAttributes()
|
||||
));
|
||||
}
|
||||
}
|
||||
$this->checkImplementedInterfaces($node->implements);
|
||||
}
|
||||
|
||||
protected function checkInterface(Interface_ $node, $namePos) {
|
||||
$this->checkClassName($node->name, $namePos);
|
||||
$this->checkImplementedInterfaces($node->extends);
|
||||
}
|
||||
|
||||
protected function checkEnum(Enum_ $node, $namePos) {
|
||||
$this->checkClassName($node->name, $namePos);
|
||||
$this->checkImplementedInterfaces($node->implements);
|
||||
}
|
||||
|
||||
protected function checkClassMethod(ClassMethod $node, $modifierPos) {
|
||||
|
@ -18,7 +18,8 @@ class Standard extends PrettyPrinterAbstract
|
||||
// Special nodes
|
||||
|
||||
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->byRef ? '&' : '')
|
||||
. ($node->variadic ? '...' : '')
|
||||
@ -52,6 +53,15 @@ class Standard extends PrettyPrinterAbstract
|
||||
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
|
||||
|
||||
protected function pName(Name $node) {
|
||||
@ -600,7 +610,8 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
protected function pExpr_Closure(Expr\Closure $node) {
|
||||
return ($node->static ? 'static ' : '')
|
||||
return $this->pAttrGroups($node->attrGroups, true)
|
||||
. ($node->static ? 'static ' : '')
|
||||
. 'function ' . ($node->byRef ? '&' : '')
|
||||
. '(' . $this->pCommaSeparated($node->params) . ')'
|
||||
. (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')' : '')
|
||||
@ -621,7 +632,8 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
protected function pExpr_ArrowFunction(Expr\ArrowFunction $node) {
|
||||
return ($node->static ? 'static ' : '')
|
||||
return $this->pAttrGroups($node->attrGroups, true)
|
||||
. ($node->static ? 'static ' : '')
|
||||
. 'fn' . ($node->byRef ? '&' : '')
|
||||
. '(' . $this->pCommaSeparated($node->params) . ')'
|
||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||
@ -709,20 +721,36 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
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) : '')
|
||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||
}
|
||||
|
||||
protected function pStmt_Enum(Stmt\Enum_ $node) {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. 'enum ' . $node->name
|
||||
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
|
||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||
}
|
||||
|
||||
protected function pStmt_Class(Stmt\Class_ $node) {
|
||||
return $this->pClassCommon($node, ' ' . $node->name);
|
||||
}
|
||||
|
||||
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 . '}';
|
||||
}
|
||||
|
||||
protected function pStmt_EnumCase(Stmt\EnumCase $node) {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. 'case ' . $node->name
|
||||
. ($node->expr ? ' = ' . $this->p($node->expr) : '')
|
||||
. ';';
|
||||
}
|
||||
|
||||
protected function pStmt_TraitUse(Stmt\TraitUse $node) {
|
||||
return 'use ' . $this->pCommaSeparated($node->traits)
|
||||
. (empty($node->adaptations)
|
||||
@ -744,7 +772,8 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
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) . ' ' : '')
|
||||
. $this->pCommaSeparated($node->props) . ';';
|
||||
}
|
||||
@ -755,7 +784,8 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
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
|
||||
. '(' . $this->pMaybeMultiline($node->params) . ')'
|
||||
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
|
||||
@ -765,12 +795,14 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
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) . ';';
|
||||
}
|
||||
|
||||
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) . ')'
|
||||
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
|
||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||
@ -925,11 +957,12 @@ class Standard extends PrettyPrinterAbstract
|
||||
// Helpers
|
||||
|
||||
protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) {
|
||||
return $this->pModifiers($node->flags)
|
||||
. 'class' . $afterClassToken
|
||||
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
|
||||
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
|
||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||
return $this->pAttrGroups($node->attrGroups, $node->name === null)
|
||||
. $this->pModifiers($node->flags)
|
||||
. 'class' . $afterClassToken
|
||||
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
|
||||
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
|
||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||
}
|
||||
|
||||
protected function pObjectProperty($node) {
|
||||
@ -965,14 +998,27 @@ class Standard extends PrettyPrinterAbstract
|
||||
$escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
|
||||
}
|
||||
|
||||
// Escape other control characters
|
||||
return preg_replace_callback('/([\0-\10\16-\37])(?=([0-7]?))/', function ($matches) {
|
||||
$oct = decoct(ord($matches[1]));
|
||||
if ($matches[2] !== '') {
|
||||
// If there is a trailing digit, use the full three character form
|
||||
return '\\' . str_pad($oct, 3, '0', \STR_PAD_LEFT);
|
||||
}
|
||||
return '\\' . $oct;
|
||||
// Escape control characters and non-UTF-8 characters.
|
||||
// Regex based on https://stackoverflow.com/a/11709412/385378.
|
||||
$regex = '/(
|
||||
[\x00-\x08\x0E-\x1F] # Control characters
|
||||
| [\xC0-\xC1] # Invalid UTF-8 Bytes
|
||||
| [\xF5-\xFF] # Invalid UTF-8 Bytes
|
||||
| \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point
|
||||
| \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point
|
||||
| [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
|
||||
| [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
|
||||
| [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
|
||||
| (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
|
||||
| (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
|
||||
| (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
|
||||
| (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
|
||||
| (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
|
||||
)/x';
|
||||
return preg_replace_callback($regex, function ($matches) {
|
||||
assert(strlen($matches[0]) === 1);
|
||||
$hex = dechex(ord($matches[0]));;
|
||||
return '\\x' . str_pad($hex, 2, '0', \STR_PAD_LEFT);
|
||||
}, $escaped);
|
||||
}
|
||||
|
||||
@ -1021,7 +1067,7 @@ class Standard extends PrettyPrinterAbstract
|
||||
* @param Node[] $nodes
|
||||
* @return bool
|
||||
*/
|
||||
private function hasNodeWithComments(array $nodes) {
|
||||
protected function hasNodeWithComments(array $nodes) {
|
||||
foreach ($nodes as $node) {
|
||||
if ($node && $node->getComments()) {
|
||||
return true;
|
||||
@ -1030,11 +1076,21 @@ class Standard extends PrettyPrinterAbstract
|
||||
return false;
|
||||
}
|
||||
|
||||
private function pMaybeMultiline(array $nodes, $trailingComma = false) {
|
||||
protected function pMaybeMultiline(array $nodes, bool $trailingComma = false) {
|
||||
if (!$this->hasNodeWithComments($nodes)) {
|
||||
return $this->pCommaSeparated($nodes);
|
||||
} else {
|
||||
return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl;
|
||||
}
|
||||
}
|
||||
|
||||
protected function pAttrGroups(array $nodes, bool $inline = false): string {
|
||||
$result = '';
|
||||
$sep = $inline ? ' ' : $this->nl;
|
||||
foreach ($nodes as $node) {
|
||||
$result .= $this->p($node) . $sep;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ abstract class PrettyPrinterAbstract
|
||||
BinaryOp\BooleanAnd::class => [120, -1],
|
||||
BinaryOp\BooleanOr::class => [130, -1],
|
||||
BinaryOp\Coalesce::class => [140, 1],
|
||||
Expr\Ternary::class => [150, -1],
|
||||
Expr\Ternary::class => [150, 0],
|
||||
// parser uses %left for assignments, but they really behave as %right
|
||||
Expr\Assign::class => [160, 1],
|
||||
Expr\AssignRef::class => [160, 1],
|
||||
@ -756,7 +756,7 @@ abstract class PrettyPrinterAbstract
|
||||
|
||||
$itemStartPos = $origArrItem->getStartTokenPos();
|
||||
$itemEndPos = $origArrItem->getEndTokenPos();
|
||||
\assert($itemStartPos >= 0 && $itemEndPos >= 0);
|
||||
\assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
|
||||
|
||||
$origIndentLevel = $this->indentLevel;
|
||||
$lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
|
||||
@ -767,16 +767,25 @@ abstract class PrettyPrinterAbstract
|
||||
$commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
|
||||
\assert($commentStartPos >= 0);
|
||||
|
||||
$commentsChanged = $comments !== $origComments;
|
||||
if ($commentsChanged) {
|
||||
// Remove old comments
|
||||
$itemStartPos = $commentStartPos;
|
||||
if ($commentStartPos < $pos) {
|
||||
// Comments may be assigned to multiple nodes if they start at the same position.
|
||||
// Make sure we don't try to print them multiple times.
|
||||
$commentStartPos = $itemStartPos;
|
||||
}
|
||||
|
||||
if ($skipRemovedNode) {
|
||||
if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) {
|
||||
// We'd remove the brace of a code block.
|
||||
// TODO: Preserve formatting.
|
||||
$this->setIndentLevel($origIndentLevel);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$result .= $this->origTokens->getTokenCode(
|
||||
$pos, $commentStartPos, $indentAdjustment);
|
||||
}
|
||||
|
||||
if (!empty($delayedAdd)) {
|
||||
$result .= $this->origTokens->getTokenCode(
|
||||
$pos, $commentStartPos, $indentAdjustment);
|
||||
|
||||
/** @var Node $delayedAddNode */
|
||||
foreach ($delayedAdd as $delayedAddNode) {
|
||||
if ($insertNewline) {
|
||||
@ -795,25 +804,16 @@ abstract class PrettyPrinterAbstract
|
||||
}
|
||||
}
|
||||
|
||||
$result .= $this->origTokens->getTokenCode(
|
||||
$commentStartPos, $itemStartPos, $indentAdjustment);
|
||||
|
||||
$delayedAdd = [];
|
||||
} else if (!$skipRemovedNode) {
|
||||
$result .= $this->origTokens->getTokenCode(
|
||||
$pos, $itemStartPos, $indentAdjustment);
|
||||
} else {
|
||||
if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) {
|
||||
// We'd remove the brace of a code block.
|
||||
// TODO: Preserve formatting.
|
||||
$this->setIndentLevel($origIndentLevel);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($commentsChanged && $comments) {
|
||||
// Add new comments
|
||||
$result .= $this->pComments($comments) . $this->nl;
|
||||
if ($comments !== $origComments) {
|
||||
if ($comments) {
|
||||
$result .= $this->pComments($comments) . $this->nl;
|
||||
}
|
||||
} else {
|
||||
$result .= $this->origTokens->getTokenCode(
|
||||
$commentStartPos, $itemStartPos, $indentAdjustment);
|
||||
}
|
||||
|
||||
// If we had to remove anything, we have done so now.
|
||||
@ -1242,7 +1242,7 @@ abstract class PrettyPrinterAbstract
|
||||
/**
|
||||
* Lazily initializes the removal map.
|
||||
*
|
||||
* The removal map is used to determine which additional tokens should be returned when a
|
||||
* The removal map is used to determine which additional tokens should be removed when a
|
||||
* certain node is replaced by null.
|
||||
*/
|
||||
protected function initializeRemovalMap() {
|
||||
@ -1269,6 +1269,8 @@ abstract class PrettyPrinterAbstract
|
||||
'Stmt_Catch->var' => $stripLeft,
|
||||
'Stmt_ClassMethod->returnType' => $stripColon,
|
||||
'Stmt_Class->extends' => ['left' => \T_EXTENDS],
|
||||
'Stmt_Enum->scalarType' => $stripColon,
|
||||
'Stmt_EnumCase->expr' => $stripEquals,
|
||||
'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS],
|
||||
'Stmt_Continue->num' => $stripBoth,
|
||||
'Stmt_Foreach->keyVar' => $stripDoubleArrow,
|
||||
@ -1307,6 +1309,8 @@ abstract class PrettyPrinterAbstract
|
||||
'Stmt_Catch->var' => [null, false, ' ', null],
|
||||
'Stmt_ClassMethod->returnType' => [')', false, ' : ', null],
|
||||
'Stmt_Class->extends' => [null, false, ' extends ', null],
|
||||
'Stmt_Enum->scalarType' => [null, false, ' : ', null],
|
||||
'Stmt_EnumCase->expr' => [null, false, ' = ', null],
|
||||
'Expr_PrintableNewAnonClass->extends' => [null, ' extends ', null],
|
||||
'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null],
|
||||
'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '],
|
||||
@ -1356,6 +1360,7 @@ abstract class PrettyPrinterAbstract
|
||||
'Stmt_ClassConst->consts' => ', ',
|
||||
'Stmt_ClassMethod->params' => ', ',
|
||||
'Stmt_Class->implements' => ', ',
|
||||
'Stmt_Enum->implements' => ', ',
|
||||
'Expr_PrintableNewAnonClass->implements' => ', ',
|
||||
'Stmt_Const->consts' => ', ',
|
||||
'Stmt_Declare->declares' => ', ',
|
||||
@ -1375,12 +1380,14 @@ abstract class PrettyPrinterAbstract
|
||||
'Stmt_Unset->vars' => ', ',
|
||||
'Stmt_Use->uses' => ', ',
|
||||
'MatchArm->conds' => ', ',
|
||||
'AttributeGroup->attrs' => ', ',
|
||||
|
||||
// statement lists
|
||||
'Expr_Closure->stmts' => "\n",
|
||||
'Stmt_Case->stmts' => "\n",
|
||||
'Stmt_Catch->stmts' => "\n",
|
||||
'Stmt_Class->stmts' => "\n",
|
||||
'Stmt_Enum->stmts' => "\n",
|
||||
'Expr_PrintableNewAnonClass->stmts' => "\n",
|
||||
'Stmt_Interface->stmts' => "\n",
|
||||
'Stmt_Trait->stmts' => "\n",
|
||||
@ -1395,6 +1402,19 @@ abstract class PrettyPrinterAbstract
|
||||
'Stmt_Function->stmts' => "\n",
|
||||
'Stmt_If->stmts' => "\n",
|
||||
'Stmt_Namespace->stmts' => "\n",
|
||||
'Stmt_Class->attrGroups' => "\n",
|
||||
'Stmt_Enum->attrGroups' => "\n",
|
||||
'Stmt_EnumCase->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_TraitUse->adaptations' => "\n",
|
||||
'Stmt_TryCatch->stmts' => "\n",
|
||||
@ -1423,6 +1443,7 @@ abstract class PrettyPrinterAbstract
|
||||
'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''],
|
||||
'Expr_StaticCall->args' => ['(', '', ''],
|
||||
'Stmt_Class->implements' => [null, ' implements ', ''],
|
||||
'Stmt_Enum->implements' => [null, ' implements ', ''],
|
||||
'Stmt_ClassMethod->params' => ['(', '', ''],
|
||||
'Stmt_Interface->extends' => [null, ' extends ', ''],
|
||||
'Stmt_Function->params' => ['(', '', ''],
|
||||
|
166
test/PhpParser/Builder/ClassConstTest.php
Normal file
166
test/PhpParser/Builder/ClassConstTest.php
Normal file
@ -0,0 +1,166 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser\Comment;
|
||||
use PhpParser\Node\Const_;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class ClassConstTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function createClassConstBuilder($name, $value) {
|
||||
return new ClassConst($name, $value);
|
||||
}
|
||||
|
||||
public function testModifiers() {
|
||||
$node = $this->createClassConstBuilder("TEST", 1)
|
||||
->makePrivate()
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
new Stmt\ClassConst(
|
||||
[
|
||||
new Const_("TEST", new LNumber(1))
|
||||
],
|
||||
Stmt\Class_::MODIFIER_PRIVATE
|
||||
),
|
||||
$node
|
||||
);
|
||||
|
||||
$node = $this->createClassConstBuilder("TEST", 1)
|
||||
->makeProtected()
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
new Stmt\ClassConst(
|
||||
[
|
||||
new Const_("TEST", new LNumber(1) )
|
||||
],
|
||||
Stmt\Class_::MODIFIER_PROTECTED
|
||||
),
|
||||
$node
|
||||
);
|
||||
|
||||
$node = $this->createClassConstBuilder("TEST", 1)
|
||||
->makePublic()
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
new Stmt\ClassConst(
|
||||
[
|
||||
new Const_("TEST", new LNumber(1) )
|
||||
],
|
||||
Stmt\Class_::MODIFIER_PUBLIC
|
||||
),
|
||||
$node
|
||||
);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
$node = $this->createClassConstBuilder('TEST',1)
|
||||
->setDocComment('/** Test */')
|
||||
->makePublic()
|
||||
->getNode();
|
||||
|
||||
$this->assertEquals(
|
||||
new Stmt\ClassConst(
|
||||
[
|
||||
new Const_("TEST", new LNumber(1) )
|
||||
],
|
||||
Stmt\Class_::MODIFIER_PUBLIC,
|
||||
[
|
||||
'comments' => [new Comment\Doc('/** Test */')]
|
||||
]
|
||||
),
|
||||
$node
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddConst() {
|
||||
$node = $this->createClassConstBuilder('FIRST_TEST',1)
|
||||
->addConst("SECOND_TEST",2)
|
||||
->getNode();
|
||||
|
||||
$this->assertEquals(
|
||||
new Stmt\ClassConst(
|
||||
[
|
||||
new Const_("FIRST_TEST", new LNumber(1)),
|
||||
new Const_("SECOND_TEST", new LNumber(2))
|
||||
]
|
||||
),
|
||||
$node
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestDefaultValues
|
||||
*/
|
||||
public function testValues($value, $expectedValueNode) {
|
||||
$node = $this->createClassConstBuilder('TEST', $value)
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals($expectedValueNode, $node->consts[0]->value);
|
||||
}
|
||||
|
||||
public function provideTestDefaultValues() {
|
||||
return [
|
||||
[
|
||||
null,
|
||||
new Expr\ConstFetch(new Name('null'))
|
||||
],
|
||||
[
|
||||
true,
|
||||
new Expr\ConstFetch(new Name('true'))
|
||||
],
|
||||
[
|
||||
false,
|
||||
new Expr\ConstFetch(new Name('false'))
|
||||
],
|
||||
[
|
||||
31415,
|
||||
new Scalar\LNumber(31415)
|
||||
],
|
||||
[
|
||||
3.1415,
|
||||
new Scalar\DNumber(3.1415)
|
||||
],
|
||||
[
|
||||
'Hallo World',
|
||||
new Scalar\String_('Hallo World')
|
||||
],
|
||||
[
|
||||
[1, 2, 3],
|
||||
new Expr\Array_([
|
||||
new Expr\ArrayItem(new Scalar\LNumber(1)),
|
||||
new Expr\ArrayItem(new Scalar\LNumber(2)),
|
||||
new Expr\ArrayItem(new Scalar\LNumber(3)),
|
||||
])
|
||||
],
|
||||
[
|
||||
['foo' => 'bar', 'bar' => 'foo'],
|
||||
new Expr\Array_([
|
||||
new Expr\ArrayItem(
|
||||
new Scalar\String_('bar'),
|
||||
new Scalar\String_('foo')
|
||||
),
|
||||
new Expr\ArrayItem(
|
||||
new Scalar\String_('foo'),
|
||||
new Scalar\String_('bar')
|
||||
),
|
||||
])
|
||||
],
|
||||
[
|
||||
new Scalar\MagicConst\Dir,
|
||||
new Scalar\MagicConst\Dir
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
@ -36,6 +36,11 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase
|
||||
];
|
||||
}
|
||||
|
||||
public function testFactoryClassConst() {
|
||||
$factory = new BuilderFactory;
|
||||
$this->assertInstanceOf(Builder\ClassConst::class, $factory->classConst('TEST',1));
|
||||
}
|
||||
|
||||
public function testVal() {
|
||||
// This method is a wrapper around BuilderHelpers::normalizeValue(),
|
||||
// which is already tested elsewhere
|
||||
@ -276,7 +281,11 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase
|
||||
->addStmt($factory->property('someProperty')->makeProtected())
|
||||
->addStmt($factory->property('anotherProperty')
|
||||
->makePrivate()
|
||||
->setDefault([1, 2, 3])))
|
||||
->setDefault([1, 2, 3]))
|
||||
|
||||
->addStmt($factory->classConst("FIRST_CLASS_CONST", 1)
|
||||
->addConst("SECOND_CLASS_CONST",2)
|
||||
->makePrivate()))
|
||||
->getNode()
|
||||
;
|
||||
|
||||
@ -297,6 +306,7 @@ abstract class SomeClass extends SomeOtherClass implements A\Few, \Interfaces
|
||||
AnotherTrait::baz as test;
|
||||
AnotherTrait::func insteadof SecondTrait;
|
||||
}
|
||||
private const FIRST_CLASS_CONST = 1, SECOND_CLASS_CONST = 2;
|
||||
protected $someProperty;
|
||||
private $anotherProperty = array(1, 2, 3);
|
||||
function firstMethod()
|
||||
|
@ -121,9 +121,11 @@ class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase
|
||||
'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,
|
||||
'Division by zero'
|
||||
\PHP_VERSION_ID >= 80000
|
||||
? 'A non-numeric value encountered'
|
||||
: 'A non well formed numeric value encountered'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -24,6 +24,17 @@ class EmulativeTest extends LexerTest
|
||||
$this->assertSame(0, $lexer->getNextToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
public function testReplaceKeywordsUppercase($keyword, $expectedToken) {
|
||||
$lexer = $this->getLexer();
|
||||
$lexer->startLexing('<?php ' . strtoupper($keyword));
|
||||
|
||||
$this->assertSame($expectedToken, $lexer->getNextToken());
|
||||
$this->assertSame(0, $lexer->getNextToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
@ -275,6 +286,62 @@ class EmulativeTest extends LexerTest
|
||||
['?->', [
|
||||
[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(','), ','],
|
||||
]],
|
||||
// Enums use a contextual keyword
|
||||
['enum Foo {}', [
|
||||
[Tokens::T_ENUM, 'enum'],
|
||||
[Tokens::T_STRING, 'Foo'],
|
||||
[ord('{'), '{'],
|
||||
[ord('}'), '}'],
|
||||
]],
|
||||
['class Enum {}', [
|
||||
[Tokens::T_CLASS, 'class'],
|
||||
[Tokens::T_STRING, 'Enum'],
|
||||
[ord('{'), '{'],
|
||||
[ord('}'), '}'],
|
||||
]],
|
||||
['class Enum extends X {}', [
|
||||
[Tokens::T_CLASS, 'class'],
|
||||
[Tokens::T_STRING, 'Enum'],
|
||||
[Tokens::T_EXTENDS, 'extends'],
|
||||
[Tokens::T_STRING, 'X'],
|
||||
[ord('{'), '{'],
|
||||
[ord('}'), '}'],
|
||||
]],
|
||||
['class Enum implements X {}', [
|
||||
[Tokens::T_CLASS, 'class'],
|
||||
[Tokens::T_STRING, 'Enum'],
|
||||
[Tokens::T_IMPLEMENTS, 'implements'],
|
||||
[Tokens::T_STRING, 'X'],
|
||||
[ord('{'), '{'],
|
||||
[ord('}'), '}'],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
@ -291,8 +358,27 @@ class EmulativeTest extends LexerTest
|
||||
return [
|
||||
['8.0', 'match', [[Tokens::T_MATCH, 'match']]],
|
||||
['7.4', 'match', [[Tokens::T_STRING, 'match']]],
|
||||
// Keywords are not case-sensitive.
|
||||
['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('"'), '"'],
|
||||
]],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -248,6 +248,7 @@ PHP;
|
||||
}
|
||||
},
|
||||
"flags": 0,
|
||||
"attrGroups": [],
|
||||
"attributes": {
|
||||
"startLine": 4,
|
||||
"endLine": 4
|
||||
@ -275,6 +276,7 @@ PHP;
|
||||
}
|
||||
},
|
||||
"flags": 0,
|
||||
"attrGroups": [],
|
||||
"attributes": {
|
||||
"startLine": 4,
|
||||
"endLine": 4
|
||||
@ -302,6 +304,7 @@ PHP;
|
||||
}
|
||||
}
|
||||
],
|
||||
"attrGroups": [],
|
||||
"attributes": {
|
||||
"startLine": 4,
|
||||
"comments": [
|
||||
|
@ -165,13 +165,8 @@ namespace Baz {
|
||||
}
|
||||
EOC;
|
||||
|
||||
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$traverser = new PhpParser\NodeTraverser;
|
||||
$traverser->addVisitor(new NameResolver);
|
||||
|
||||
$stmts = $parser->parse($code);
|
||||
$stmts = $traverser->traverse($stmts);
|
||||
$stmts = $this->parseAndResolve($code);
|
||||
|
||||
$this->assertSame(
|
||||
$this->canonicalize($expectedCode),
|
||||
@ -187,33 +182,43 @@ EOC;
|
||||
<?php
|
||||
namespace NS;
|
||||
|
||||
#[X]
|
||||
class A extends B implements C, D {
|
||||
use E, F, G {
|
||||
f as private g;
|
||||
E::h as i;
|
||||
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 {
|
||||
public function a(A $a) : A;
|
||||
public function b(A|B|int $a): A|B|int;
|
||||
}
|
||||
|
||||
class ClassWithTypeProperties {
|
||||
public float $php = 7.4;
|
||||
public ?Foo $person;
|
||||
protected static ?bool $probability;
|
||||
public A|B|int $prop;
|
||||
}
|
||||
#[X]
|
||||
trait A {}
|
||||
|
||||
function f(A $a) : A {}
|
||||
#[X]
|
||||
function f(#[X] A $a) : A {}
|
||||
function f2(array $a) : array {}
|
||||
function(A $a) : A {};
|
||||
|
||||
function fn3(?A $a) : ?A {}
|
||||
function fn4(?array $a) : ?array {}
|
||||
|
||||
#[X]
|
||||
function(A $a) : A {};
|
||||
|
||||
#[X]
|
||||
fn(array $a): array => $a;
|
||||
fn(A $a): A => $a;
|
||||
fn(?A $a): ?A => $a;
|
||||
@ -236,6 +241,7 @@ EOC;
|
||||
$expectedCode = <<<'EOC'
|
||||
namespace NS;
|
||||
|
||||
#[\NS\X]
|
||||
class A extends \NS\B implements \NS\C, \NS\D
|
||||
{
|
||||
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::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
|
||||
{
|
||||
public function a(\NS\A $a) : \NS\A;
|
||||
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 (\NS\A $a) : \NS\A {
|
||||
};
|
||||
function fn3(?\NS\A $a) : ?\NS\A
|
||||
{
|
||||
}
|
||||
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;
|
||||
\NS\A::b();
|
||||
@ -287,13 +299,8 @@ try {
|
||||
}
|
||||
EOC;
|
||||
|
||||
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$traverser = new PhpParser\NodeTraverser;
|
||||
$traverser->addVisitor(new NameResolver);
|
||||
|
||||
$stmts = $parser->parse($code);
|
||||
$stmts = $traverser->traverse($stmts);
|
||||
$stmts = $this->parseAndResolve($code);
|
||||
|
||||
$this->assertSame(
|
||||
$this->canonicalize($expectedCode),
|
||||
@ -512,4 +519,14 @@ EOC;
|
||||
$this->assertEquals(
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
179
test/code/formatPreservation/attributes.test
Normal file
179
test/code/formatPreservation/attributes.test
Normal 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 {};
|
100
test/code/formatPreservation/enum.test
Normal file
100
test/code/formatPreservation/enum.test
Normal file
@ -0,0 +1,100 @@
|
||||
Enum formatting preservation
|
||||
-----
|
||||
<?php
|
||||
enum X: int
|
||||
{}
|
||||
-----
|
||||
$stmts[0]->scalarType = null;
|
||||
-----
|
||||
<?php
|
||||
enum X
|
||||
{}
|
||||
-----
|
||||
<?php
|
||||
enum X {
|
||||
case
|
||||
Y = 1;
|
||||
}
|
||||
-----
|
||||
$stmts[0]->stmts[0]->expr = null;
|
||||
-----
|
||||
<?php
|
||||
enum X {
|
||||
case
|
||||
Y;
|
||||
}
|
||||
-----
|
||||
<?php
|
||||
enum X
|
||||
{}
|
||||
-----
|
||||
$stmts[0]->scalarType = new Node\Identifier('int');
|
||||
-----
|
||||
<?php
|
||||
enum X : int
|
||||
{}
|
||||
-----
|
||||
<?php
|
||||
enum X
|
||||
implements Y
|
||||
{}
|
||||
-----
|
||||
$stmts[0]->scalarType = new Node\Identifier('int');
|
||||
-----
|
||||
<?php
|
||||
enum X : int
|
||||
implements Y
|
||||
{}
|
||||
-----
|
||||
<?php
|
||||
enum X {
|
||||
case
|
||||
Y;
|
||||
}
|
||||
-----
|
||||
$stmts[0]->stmts[0]->expr = new Scalar\LNumber(1);
|
||||
-----
|
||||
<?php
|
||||
enum X {
|
||||
case
|
||||
Y = 1;
|
||||
}
|
||||
-----
|
||||
<?php
|
||||
enum X {
|
||||
case A;
|
||||
|
||||
case B;
|
||||
}
|
||||
-----
|
||||
$stmts[0]->stmts[] = new Node\Stmt\EnumCase('C');
|
||||
-----
|
||||
<?php
|
||||
enum X {
|
||||
case A;
|
||||
|
||||
case B;
|
||||
case C;
|
||||
}
|
||||
-----
|
||||
<?php
|
||||
enum X
|
||||
implements Y
|
||||
{}
|
||||
-----
|
||||
$stmts[0]->implements[] = new Node\Name('Z');
|
||||
-----
|
||||
<?php
|
||||
enum X
|
||||
implements Y, Z
|
||||
{}
|
||||
-----
|
||||
<?php
|
||||
enum X
|
||||
{}
|
||||
-----
|
||||
$stmts[0]->implements[] = new Node\Name('Y');
|
||||
-----
|
||||
<?php
|
||||
enum X implements Y
|
||||
{}
|
@ -316,4 +316,23 @@ $stmts[0]->returnType->types[] = new Node\Name('C');
|
||||
-----
|
||||
<?php
|
||||
function test(): A
|
||||
|B|C {}
|
||||
|B|C {}
|
||||
-----
|
||||
<?php
|
||||
function test() {
|
||||
if ($x) {
|
||||
$a;
|
||||
$b;
|
||||
}
|
||||
$z;
|
||||
}
|
||||
-----
|
||||
$fnStmts =& $stmts[0]->stmts;
|
||||
array_splice($fnStmts, 0, 1, $fnStmts[0]->stmts);
|
||||
-----
|
||||
<?php
|
||||
function test() {
|
||||
$a;
|
||||
$b;
|
||||
$z;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
Rewrite string with variable interpolation
|
||||
-----
|
||||
<?php
|
||||
"{$e}";
|
||||
-----
|
||||
$stmts[0]->expr->parts[0]->setAttribute('origNode', null);
|
||||
-----
|
||||
<?php
|
||||
"{$e}";
|
@ -8,6 +8,8 @@ class MyClass {
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: MyClass
|
||||
@ -17,6 +19,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PROTECTED (2)
|
||||
type: null
|
||||
props: array(
|
||||
|
@ -161,6 +161,8 @@ function test() {
|
||||
Syntax error, unexpected '}' from 4:1 to 4:1
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
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
|
||||
array(
|
||||
0: Stmt_Function[2:1 - 4:1](
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier[2:10 - 2:12](
|
||||
name: foo
|
||||
@ -683,6 +687,8 @@ array(
|
||||
)
|
||||
)
|
||||
4: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: X
|
||||
@ -736,6 +742,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -749,6 +757,8 @@ array(
|
||||
)
|
||||
)
|
||||
3: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: null
|
||||
props: array(
|
||||
@ -763,6 +773,8 @@ array(
|
||||
)
|
||||
)
|
||||
5: Stmt_Interface(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: I
|
||||
)
|
||||
@ -895,6 +907,8 @@ class Foo {
|
||||
Syntax error, unexpected T_STRING from 4:5 to 4:9
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: Foo
|
||||
@ -904,6 +918,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: null
|
||||
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
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: foo
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Name(
|
||||
parts: array(
|
||||
@ -1027,12 +1047,16 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: foo
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Name(
|
||||
parts: array(
|
||||
@ -1047,6 +1071,8 @@ array(
|
||||
default: null
|
||||
)
|
||||
1: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Name(
|
||||
parts: array(
|
||||
@ -1070,12 +1096,16 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: foo
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -1095,12 +1125,16 @@ array(
|
||||
)
|
||||
)
|
||||
3: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: foo
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: true
|
||||
@ -1120,12 +1154,16 @@ array(
|
||||
)
|
||||
)
|
||||
4: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: foo
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Name(
|
||||
parts: array(
|
||||
@ -1144,6 +1182,8 @@ array(
|
||||
)
|
||||
)
|
||||
5: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: Bar
|
||||
@ -1153,6 +1193,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -1160,6 +1202,8 @@ array(
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Name(
|
||||
parts: array(
|
||||
@ -1181,10 +1225,14 @@ array(
|
||||
)
|
||||
6: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Name(
|
||||
parts: array(
|
||||
@ -1219,9 +1267,9 @@ $array = [
|
||||
];
|
||||
-----
|
||||
!!php7
|
||||
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 9:21 to 9:37
|
||||
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 9:21 to 9:37
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_Assign(
|
||||
@ -1330,6 +1378,8 @@ function foo() :
|
||||
Syntax error, unexpected '{' from 3:1 to 3:1
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: foo
|
||||
@ -1351,4 +1401,124 @@ array(
|
||||
$a = ["a "thing"];
|
||||
-----
|
||||
!!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
|
||||
-----
|
||||
<?php
|
||||
class A {
|
||||
/** @var ?string */
|
||||
private $foo
|
||||
|
||||
public function __construct(string $s) {
|
||||
$this->foo = $s;
|
||||
}
|
||||
}
|
||||
class B {
|
||||
const X = 1
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
Syntax error, unexpected T_PUBLIC, expecting ';' from 6:5 to 6:10
|
||||
Syntax error, unexpected '}', expecting ';' from 12:1 to 12:1
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PRIVATE (4)
|
||||
type: null
|
||||
props: array(
|
||||
0: Stmt_PropertyProperty(
|
||||
name: VarLikeIdentifier(
|
||||
name: foo
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: /** @var ?string */
|
||||
)
|
||||
)
|
||||
1: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: __construct
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: string
|
||||
)
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: s
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
stmts: array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_Assign(
|
||||
var: Expr_PropertyFetch(
|
||||
var: Expr_Variable(
|
||||
name: this
|
||||
)
|
||||
name: Identifier(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
expr: Expr_Variable(
|
||||
name: s
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
1: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
consts: array(
|
||||
0: Const(
|
||||
name: Identifier(
|
||||
name: X
|
||||
)
|
||||
value: Scalar_LNumber(
|
||||
value: 1
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -59,6 +59,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: getArr
|
||||
@ -92,6 +94,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: arrGen
|
||||
|
@ -7,15 +7,22 @@ static fn(&$x) => $x;
|
||||
fn&($x) => $x;
|
||||
fn($x, ...$rest) => $rest;
|
||||
fn(): int => $x;
|
||||
|
||||
fn($a, $b) => $a and $b;
|
||||
fn($a, $b) => $a && $b;
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: bool
|
||||
@ -36,10 +43,14 @@ array(
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -60,10 +71,14 @@ array(
|
||||
)
|
||||
2: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: true
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: true
|
||||
@ -82,10 +97,14 @@ array(
|
||||
)
|
||||
3: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: true
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -104,10 +123,14 @@ array(
|
||||
)
|
||||
4: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -118,6 +141,8 @@ array(
|
||||
default: null
|
||||
)
|
||||
1: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -136,6 +161,8 @@ array(
|
||||
)
|
||||
5: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
@ -148,4 +175,90 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
6: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
default: null
|
||||
)
|
||||
1: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
expr: Expr_BinaryOp_LogicalAnd(
|
||||
left: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
7: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
default: null
|
||||
)
|
||||
1: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
expr: Expr_BinaryOp_BooleanAnd(
|
||||
left: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -12,10 +12,14 @@ function() use($a) : \Foo\Bar {};
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -40,10 +44,14 @@ array(
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -69,6 +77,8 @@ array(
|
||||
)
|
||||
2: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
@ -94,10 +104,14 @@ array(
|
||||
)
|
||||
3: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: true
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -117,6 +131,8 @@ array(
|
||||
)
|
||||
4: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: true
|
||||
byRef: false
|
||||
params: array(
|
||||
@ -130,10 +146,14 @@ array(
|
||||
)
|
||||
5: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -155,6 +175,8 @@ array(
|
||||
)
|
||||
6: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
|
@ -7,6 +7,8 @@ function() use($a,) {};
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
|
@ -71,11 +71,13 @@ array(
|
||||
4: Stmt_Expression(
|
||||
expr: Scalar_Encapsed(
|
||||
parts: array(
|
||||
0: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
1: Scalar_EncapsedStringPart(
|
||||
value: ?->b
|
||||
0: Expr_NullsafePropertyFetch(
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
name: Identifier(
|
||||
name: b
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -204,10 +204,14 @@ array(
|
||||
name: Expr_FuncCall(
|
||||
name: Expr_FuncCall(
|
||||
name: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -274,10 +278,14 @@ array(
|
||||
name: f
|
||||
)
|
||||
expr: Expr_Closure(
|
||||
attrGroups: array(
|
||||
)
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
|
17
test/code/parser/expr/varVarPos.test
Normal file
17
test/code/parser/expr/varVarPos.test
Normal file
@ -0,0 +1,17 @@
|
||||
Variable variable positions
|
||||
-----
|
||||
<?php
|
||||
$$$x;
|
||||
-----
|
||||
!!positions
|
||||
array(
|
||||
0: Stmt_Expression[2:1 - 2:5](
|
||||
expr: Expr_Variable[2:1 - 2:4](
|
||||
name: Expr_Variable[2:2 - 2:4](
|
||||
name: Expr_Variable[2:3 - 2:4](
|
||||
name: x
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -54,6 +54,8 @@ class Foo {
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: Test
|
||||
@ -63,6 +65,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -75,6 +79,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -87,6 +93,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -99,6 +107,8 @@ array(
|
||||
)
|
||||
)
|
||||
3: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -111,6 +121,8 @@ array(
|
||||
)
|
||||
)
|
||||
4: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: null
|
||||
props: array(
|
||||
@ -123,6 +135,8 @@ array(
|
||||
)
|
||||
)
|
||||
5: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: null
|
||||
props: array(
|
||||
@ -135,6 +149,8 @@ array(
|
||||
)
|
||||
)
|
||||
6: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -156,6 +172,8 @@ array(
|
||||
)
|
||||
)
|
||||
7: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -344,6 +362,8 @@ array(
|
||||
)
|
||||
)
|
||||
10: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: Foo
|
||||
|
388
test/code/parser/stmt/attributes.test
Normal file
388
test/code/parser/stmt/attributes.test
Normal 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
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -9,6 +9,8 @@ abstract class A {
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT (16)
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -18,6 +20,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -30,6 +34,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC | MODIFIER_ABSTRACT (17)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
|
@ -25,6 +25,8 @@ array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_New(
|
||||
class: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: null
|
||||
extends: null
|
||||
@ -32,6 +34,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -52,6 +56,8 @@ array(
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_New(
|
||||
class: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: null
|
||||
extends: Name(
|
||||
@ -81,6 +87,8 @@ array(
|
||||
2: Stmt_Expression(
|
||||
expr: Expr_New(
|
||||
class: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: null
|
||||
extends: null
|
||||
@ -88,6 +96,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: null
|
||||
props: array(
|
||||
@ -108,6 +118,8 @@ array(
|
||||
3: Stmt_Expression(
|
||||
expr: Expr_New(
|
||||
class: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: null
|
||||
extends: Name(
|
||||
@ -152,6 +164,8 @@ array(
|
||||
)
|
||||
)
|
||||
4: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -161,6 +175,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -173,6 +189,8 @@ array(
|
||||
0: Stmt_Return(
|
||||
expr: Expr_New(
|
||||
class: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: null
|
||||
extends: Name(
|
||||
@ -184,6 +202,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
consts: array(
|
||||
0: Const(
|
||||
|
94
test/code/parser/stmt/class/class_position.test
Normal file
94
test/code/parser/stmt/class/class_position.test
Normal file
@ -0,0 +1,94 @@
|
||||
Class position
|
||||
-----
|
||||
<?php
|
||||
|
||||
if (1);
|
||||
|
||||
class C {}
|
||||
-----
|
||||
!!positions
|
||||
array(
|
||||
0: Stmt_If[3:1 - 3:7](
|
||||
cond: Scalar_LNumber[3:5 - 3:5](
|
||||
value: 1
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
elseifs: array(
|
||||
)
|
||||
else: null
|
||||
)
|
||||
1: Stmt_Class[5:1 - 5:10](
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier[5:7 - 5:7](
|
||||
name: C
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php
|
||||
|
||||
if (1);
|
||||
|
||||
trait X {}
|
||||
-----
|
||||
!!positions
|
||||
array(
|
||||
0: Stmt_If[3:1 - 3:7](
|
||||
cond: Scalar_LNumber[3:5 - 3:5](
|
||||
value: 1
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
elseifs: array(
|
||||
)
|
||||
else: null
|
||||
)
|
||||
1: Stmt_Trait[5:1 - 5:10](
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier[5:7 - 5:7](
|
||||
name: X
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php
|
||||
|
||||
if (1);
|
||||
|
||||
interface X {}
|
||||
-----
|
||||
!!positions
|
||||
array(
|
||||
0: Stmt_If[3:1 - 3:7](
|
||||
cond: Scalar_LNumber[3:5 - 3:5](
|
||||
value: 1
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
elseifs: array(
|
||||
)
|
||||
else: null
|
||||
)
|
||||
1: Stmt_Interface[5:1 - 5:14](
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier[5:11 - 5:11](
|
||||
name: X
|
||||
)
|
||||
extends: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
@ -17,6 +17,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
|
@ -9,6 +9,8 @@ class A {
|
||||
Cannot use 'static' as constant modifier from 3:5 to 3:10
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -18,6 +20,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -43,6 +47,8 @@ class A {
|
||||
Cannot use 'abstract' as constant modifier from 3:5 to 3:12
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -52,6 +58,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT (16)
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -77,6 +85,8 @@ class A {
|
||||
Cannot use 'final' as constant modifier from 3:5 to 3:9
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -86,6 +96,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL (32)
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -111,6 +123,8 @@ class A {
|
||||
Multiple access type modifiers are not allowed from 3:12 to 3:17
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -120,6 +134,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
consts: array(
|
||||
0: Const(
|
||||
|
@ -12,6 +12,8 @@ class Foo {
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: Foo
|
||||
@ -21,6 +23,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -34,6 +38,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -47,6 +53,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PROTECTED (2)
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -60,6 +68,8 @@ array(
|
||||
)
|
||||
)
|
||||
3: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PRIVATE (4)
|
||||
consts: array(
|
||||
0: Const(
|
||||
|
65
test/code/parser/stmt/class/enum.test
Normal file
65
test/code/parser/stmt/class/enum.test
Normal file
@ -0,0 +1,65 @@
|
||||
Enum
|
||||
-----
|
||||
<?php
|
||||
|
||||
enum A {}
|
||||
enum B implements Bar, Baz {
|
||||
}
|
||||
enum C: int implements Bar {}
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Enum(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
scalarType: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Enum(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
scalarType: null
|
||||
implements: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: Bar
|
||||
)
|
||||
)
|
||||
1: Name(
|
||||
parts: array(
|
||||
0: Baz
|
||||
)
|
||||
)
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
2: Stmt_Enum(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: C
|
||||
)
|
||||
scalarType: Identifier(
|
||||
name: int
|
||||
)
|
||||
implements: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: Bar
|
||||
)
|
||||
)
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
67
test/code/parser/stmt/class/enum_with_string.test
Normal file
67
test/code/parser/stmt/class/enum_with_string.test
Normal file
@ -0,0 +1,67 @@
|
||||
Enum
|
||||
-----
|
||||
<?php
|
||||
|
||||
enum Suit: string
|
||||
{
|
||||
case Hearts = 'H';
|
||||
case Diamonds;
|
||||
case Clubs = 'C';
|
||||
case Spades = 'S';
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Enum(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: Suit
|
||||
)
|
||||
scalarType: Identifier(
|
||||
name: string
|
||||
)
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_EnumCase(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: Hearts
|
||||
)
|
||||
expr: Scalar_String(
|
||||
value: H
|
||||
)
|
||||
)
|
||||
1: Stmt_EnumCase(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: Diamonds
|
||||
)
|
||||
expr: null
|
||||
)
|
||||
2: Stmt_EnumCase(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: Clubs
|
||||
)
|
||||
expr: Scalar_String(
|
||||
value: C
|
||||
)
|
||||
)
|
||||
3: Stmt_EnumCase(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: Spades
|
||||
)
|
||||
expr: Scalar_String(
|
||||
value: S
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -6,6 +6,8 @@ final class A {}
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL (32)
|
||||
name: Identifier(
|
||||
name: A
|
||||
|
@ -14,6 +14,8 @@ abstract class A {
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT (16)
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -23,6 +25,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
props: array(
|
||||
@ -35,6 +39,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
type: null
|
||||
props: array(
|
||||
@ -47,6 +53,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT (16)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -58,6 +66,8 @@ array(
|
||||
stmts: null
|
||||
)
|
||||
3: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL (32)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -70,6 +80,8 @@ array(
|
||||
)
|
||||
)
|
||||
4: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -82,6 +94,8 @@ array(
|
||||
)
|
||||
)
|
||||
5: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC | MODIFIER_FINAL (40)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -94,6 +108,8 @@ array(
|
||||
)
|
||||
)
|
||||
6: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
|
@ -8,6 +8,8 @@ interface A extends C, D {
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Interface(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
@ -25,6 +27,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
|
@ -5,6 +5,8 @@ Invalid modifier combination
|
||||
Multiple access type modifiers are not allowed from 1:24 to 1:29
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -14,6 +16,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: null
|
||||
props: array(
|
||||
@ -34,6 +38,8 @@ array(
|
||||
Multiple access type modifiers are not allowed from 1:24 to 1:32
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -43,6 +49,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC | MODIFIER_PROTECTED (3)
|
||||
type: null
|
||||
props: array(
|
||||
@ -63,6 +71,8 @@ array(
|
||||
Multiple abstract modifiers are not allowed from 1:26 to 1:33
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -72,6 +82,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT (16)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -91,6 +103,8 @@ array(
|
||||
Multiple static modifiers are not allowed from 1:24 to 1:29
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -100,6 +114,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
type: null
|
||||
props: array(
|
||||
@ -120,6 +136,8 @@ array(
|
||||
Multiple final modifiers are not allowed from 1:23 to 1:27
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -129,6 +147,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL (32)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -149,6 +169,8 @@ array(
|
||||
Cannot use the final modifier on an abstract class member from 1:26 to 1:30
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -158,6 +180,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT | MODIFIER_FINAL (48)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -178,6 +202,8 @@ array(
|
||||
Syntax error, unexpected T_FINAL, expecting T_CLASS from 1:16 to 1:20
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL (32)
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -200,6 +226,8 @@ array(
|
||||
Properties cannot be declared abstract from 1:17 to 1:24
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -209,6 +237,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT (16)
|
||||
type: null
|
||||
props: array(
|
||||
@ -229,6 +259,8 @@ array(
|
||||
Properties cannot be declared final from 1:17 to 1:21
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -238,6 +270,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL (32)
|
||||
type: null
|
||||
props: array(
|
||||
|
@ -5,6 +5,8 @@ Invalid class name
|
||||
Cannot use 'self' as class name as it is reserved from 1:13 to 1:16
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: self
|
||||
@ -22,6 +24,8 @@ array(
|
||||
Cannot use 'PARENT' as class name as it is reserved from 1:13 to 1:18
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: PARENT
|
||||
@ -45,6 +49,8 @@ array(
|
||||
Cannot use 'self' as class name as it is reserved from 1:23 to 1:26
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -66,6 +72,8 @@ array(
|
||||
Cannot use 'PARENT' as class name as it is reserved from 1:23 to 1:28
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -87,6 +95,8 @@ array(
|
||||
Cannot use 'static' as class name as it is reserved from 1:23 to 1:28
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -108,6 +118,8 @@ array(
|
||||
Cannot use 'self' as interface name as it is reserved from 1:26 to 1:29
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -130,6 +142,8 @@ array(
|
||||
Cannot use 'PARENT' as interface name as it is reserved from 1:26 to 1:31
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -152,6 +166,8 @@ array(
|
||||
Cannot use 'static' as interface name as it is reserved from 1:26 to 1:31
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -174,6 +190,8 @@ array(
|
||||
Cannot use 'self' as class name as it is reserved from 1:17 to 1:20
|
||||
array(
|
||||
0: Stmt_Interface(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: self
|
||||
)
|
||||
@ -189,6 +207,8 @@ array(
|
||||
Cannot use 'PARENT' as class name as it is reserved from 1:17 to 1:22
|
||||
array(
|
||||
0: Stmt_Interface(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: PARENT
|
||||
)
|
||||
@ -210,6 +230,8 @@ array(
|
||||
Cannot use 'self' as interface name as it is reserved from 1:27 to 1:30
|
||||
array(
|
||||
0: Stmt_Interface(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
@ -230,6 +252,8 @@ array(
|
||||
Cannot use 'PARENT' as interface name as it is reserved from 1:27 to 1:32
|
||||
array(
|
||||
0: Stmt_Interface(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
@ -250,6 +274,8 @@ array(
|
||||
Cannot use 'static' as interface name as it is reserved from 1:27 to 1:32
|
||||
array(
|
||||
0: Stmt_Interface(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
|
@ -10,6 +10,8 @@ class A {
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -19,6 +21,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
props: array(
|
||||
@ -31,6 +35,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -43,6 +49,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT | MODIFIER_STATIC (24)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
|
@ -11,6 +11,8 @@ class A {
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -20,6 +22,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: Identifier(
|
||||
name: string
|
||||
@ -34,6 +38,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PROTECTED | MODIFIER_STATIC (10)
|
||||
type: Name(
|
||||
parts: array(
|
||||
@ -50,6 +56,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PRIVATE (4)
|
||||
type: NullableType(
|
||||
type: Identifier(
|
||||
|
@ -13,6 +13,8 @@ class Point {
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: Point
|
||||
@ -22,6 +24,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -29,6 +33,8 @@ array(
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: Identifier(
|
||||
name: float
|
||||
@ -43,6 +49,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PROTECTED (2)
|
||||
type: Identifier(
|
||||
name: array
|
||||
@ -58,6 +66,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PRIVATE (4)
|
||||
type: Identifier(
|
||||
name: string
|
||||
|
@ -18,6 +18,8 @@ class A extends B implements C, D {
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -41,6 +43,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
consts: array(
|
||||
0: Const(
|
||||
@ -62,6 +66,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: null
|
||||
props: array(
|
||||
@ -84,6 +90,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PROTECTED (2)
|
||||
type: null
|
||||
props: array(
|
||||
@ -96,6 +104,8 @@ array(
|
||||
)
|
||||
)
|
||||
3: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PRIVATE (4)
|
||||
type: null
|
||||
props: array(
|
||||
@ -108,6 +118,8 @@ array(
|
||||
)
|
||||
)
|
||||
4: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -120,6 +132,8 @@ array(
|
||||
)
|
||||
)
|
||||
5: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC | MODIFIER_STATIC (9)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -127,6 +141,8 @@ array(
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -142,6 +158,8 @@ array(
|
||||
)
|
||||
)
|
||||
6: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC | MODIFIER_FINAL (33)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -158,6 +176,8 @@ array(
|
||||
)
|
||||
)
|
||||
7: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PROTECTED (2)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -170,6 +190,8 @@ array(
|
||||
)
|
||||
)
|
||||
8: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PRIVATE (4)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
|
@ -5,6 +5,8 @@ Some special methods cannot be static
|
||||
Constructor __construct() cannot be static from 1:17 to 1:22
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -14,6 +16,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -34,6 +38,8 @@ array(
|
||||
Destructor __destruct() cannot be static from 1:17 to 1:22
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -43,6 +49,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -63,6 +71,8 @@ array(
|
||||
Clone method __clone() cannot be static from 1:17 to 1:22
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -72,6 +82,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -92,6 +104,8 @@ array(
|
||||
Constructor __CONSTRUCT() cannot be static from 1:17 to 1:22
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -101,6 +115,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -121,6 +137,8 @@ array(
|
||||
Destructor __Destruct() cannot be static from 1:17 to 1:22
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -130,6 +148,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -150,6 +170,8 @@ array(
|
||||
Clone method __cLoNe() cannot be static from 1:17 to 1:22
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
@ -159,6 +181,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_STATIC (8)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
|
@ -8,6 +8,8 @@ class Test {
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: Test
|
||||
@ -17,6 +19,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC | MODIFIER_STATIC (9)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
|
@ -23,11 +23,15 @@ class B {
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Trait(
|
||||
attrGroups: array(
|
||||
)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
@ -42,6 +46,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: B
|
||||
|
@ -6,12 +6,16 @@ function test(bool $a, Int $b, FLOAT $c, StRiNg $d, iterable $e, object $f, mixe
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: bool
|
||||
@ -24,6 +28,8 @@ array(
|
||||
default: null
|
||||
)
|
||||
1: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: int
|
||||
@ -36,6 +42,8 @@ array(
|
||||
default: null
|
||||
)
|
||||
2: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: float
|
||||
@ -48,6 +56,8 @@ array(
|
||||
default: null
|
||||
)
|
||||
3: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: string
|
||||
@ -60,6 +70,8 @@ array(
|
||||
default: null
|
||||
)
|
||||
4: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: iterable
|
||||
@ -72,6 +84,8 @@ array(
|
||||
default: null
|
||||
)
|
||||
5: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: object
|
||||
@ -84,6 +98,8 @@ array(
|
||||
default: null
|
||||
)
|
||||
6: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: Identifier(
|
||||
name: mixed
|
||||
|
@ -7,12 +7,16 @@ function &a($b) {}
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: a
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: true
|
||||
@ -28,12 +32,16 @@ array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: true
|
||||
name: Identifier(
|
||||
name: a
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
|
@ -17,6 +17,8 @@ array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: A
|
||||
|
@ -16,12 +16,16 @@ function a(
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: a
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -38,6 +42,8 @@ array(
|
||||
)
|
||||
)
|
||||
1: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -50,6 +56,8 @@ array(
|
||||
)
|
||||
)
|
||||
2: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -69,6 +77,8 @@ array(
|
||||
)
|
||||
)
|
||||
3: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -83,6 +93,8 @@ array(
|
||||
)
|
||||
)
|
||||
4: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -97,6 +109,8 @@ array(
|
||||
)
|
||||
)
|
||||
5: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -110,6 +124,8 @@ array(
|
||||
)
|
||||
)
|
||||
6: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -123,6 +139,8 @@ array(
|
||||
)
|
||||
)
|
||||
7: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
@ -144,6 +162,8 @@ array(
|
||||
)
|
||||
)
|
||||
8: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user