mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-18 12:51:39 +02:00
Compare commits
153 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
220f5b3661 | ||
|
ad38d47247 | ||
|
6de6ede1cb | ||
|
20412455fc | ||
|
103f95d560 | ||
|
78f289c51f | ||
|
0f585f0e95 | ||
|
0ed4c8949a | ||
|
8a21ec3182 | ||
|
4d36e9c16f | ||
|
4e1b88d21c | ||
|
d4d4e3e155 | ||
|
32cdab9a03 | ||
|
c2403aa729 | ||
|
051ad218f8 | ||
|
1bcbb2179f | ||
|
05c01865ea | ||
|
2a5e81f7ca | ||
|
e453389866 | ||
|
402b6cf345 | ||
|
54103d8387 | ||
|
a6303e50c9 | ||
|
44fc92194b | ||
|
844c228bf2 | ||
|
6d2584bdf1 | ||
|
21a61ece15 | ||
|
8f8e47b6c1 | ||
|
0aad06bce3 | ||
|
80a680bf59 | ||
|
cfc54e30a4 | ||
|
05e84f7201 | ||
|
73ccbabbe7 | ||
|
19526a33fb | ||
|
1d0748ad35 | ||
|
c9e5a13d68 | ||
|
ba788aa98b | ||
|
11e2663a5b | ||
|
11e2dcd96c | ||
|
0ffddce52d | ||
|
6bb5176bc4 | ||
|
cad49f8ed3 | ||
|
570e980a20 | ||
|
a50b4310f7 | ||
|
8863f92b58 | ||
|
3182d12b55 | ||
|
1df465cd90 | ||
|
f59bbe44bf | ||
|
2e11deec46 | ||
|
a4fe65bf60 | ||
|
e072fd2c30 | ||
|
7027899d7f | ||
|
2f1fd784fe | ||
|
0ef6c55a3f | ||
|
8216e878be | ||
|
617d0220b9 | ||
|
a951e9e24d | ||
|
b30e7e73d5 | ||
|
ff24d1d61a | ||
|
e55f8c6b30 | ||
|
3ee592b6aa | ||
|
3fe2422e34 | ||
|
2d589921f2 | ||
|
cdb731fa8b | ||
|
e727475d08 | ||
|
34bea19b6e | ||
|
678ccbe072 | ||
|
5d83adcc0e | ||
|
3bf0082455 | ||
|
d3eb10aca1 | ||
|
a6e34665fd | ||
|
f4b835f7d8 | ||
|
210577fe3c | ||
|
f09f22760e | ||
|
d4cb98ae38 | ||
|
68d2a52b42 | ||
|
63f8699143 | ||
|
4122ff3a91 | ||
|
6f1f206862 | ||
|
99a24b6a55 | ||
|
63a79e8daa | ||
|
6a21234e58 | ||
|
7064539974 | ||
|
4bfc4595ed | ||
|
54f19a0a66 | ||
|
8da6d7ac62 | ||
|
f6e1fbf3a2 | ||
|
50953a2691 | ||
|
0a20979a62 | ||
|
a45fb2a621 | ||
|
08501991d4 | ||
|
b5234eacd0 | ||
|
632ead3a82 | ||
|
13549aa794 | ||
|
d2c645f163 | ||
|
def24f2224 | ||
|
cfeb195205 | ||
|
ace6c67a8a | ||
|
0483391aca | ||
|
9aebf377fc | ||
|
5a43015499 | ||
|
6608f01670 | ||
|
55c4269232 | ||
|
c4304c76bd | ||
|
b099e8fc76 | ||
|
acf16edc8e | ||
|
a8b5ed4306 | ||
|
c758510a37 | ||
|
feed91cf0f | ||
|
fe14cf3672 | ||
|
3fb73520c1 | ||
|
c35cc4b2cb | ||
|
e69ebbbfd9 | ||
|
0b258d9a9e | ||
|
2d193bb0e4 | ||
|
49e9951f2c | ||
|
eccf1bf464 | ||
|
b67560d388 | ||
|
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 |
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
|
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,9 +1,11 @@
|
|||||||
|
/.github export-ignore
|
||||||
/doc export-ignore
|
/doc export-ignore
|
||||||
|
/grammar export-ignore
|
||||||
/test export-ignore
|
/test export-ignore
|
||||||
/test_old export-ignore
|
/test_old export-ignore
|
||||||
|
.editorconfig export-ignore
|
||||||
.gitattributes export-ignore
|
.gitattributes export-ignore
|
||||||
.gitignore export-ignore
|
.gitignore export-ignore
|
||||||
.travis.yml export-ignore
|
|
||||||
CHANGELOG.md export-ignore
|
CHANGELOG.md export-ignore
|
||||||
phpunit.xml.dist export-ignore
|
phpunit.xml.dist export-ignore
|
||||||
UPGRADE-*.md export-ignore
|
UPGRADE-*.md export-ignore
|
||||||
|
91
.github/workflows/main.yml
vendored
Normal file
91
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
|
||||||
|
name: Main
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests_71:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
name: "PHP 7.1 Unit Tests"
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "actions/checkout@v3"
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: "shivammathur/setup-php@v2"
|
||||||
|
with:
|
||||||
|
coverage: "xdebug"
|
||||||
|
php-version: "7.1"
|
||||||
|
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.2"
|
||||||
|
- "7.3"
|
||||||
|
- "7.4"
|
||||||
|
- "8.0"
|
||||||
|
- "8.1"
|
||||||
|
- "8.2"
|
||||||
|
- "8.3"
|
||||||
|
- "8.4"
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "actions/checkout@v3"
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: "shivammathur/setup-php@v2"
|
||||||
|
with:
|
||||||
|
coverage: "none"
|
||||||
|
php-version: "${{ matrix.php-version }}"
|
||||||
|
ini-file: "development"
|
||||||
|
tools: composer:v2
|
||||||
|
- name: "Install dependencies"
|
||||||
|
run: "composer update --no-progress --prefer-dist ${{ matrix.flags }}"
|
||||||
|
- 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@v3"
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: "shivammathur/setup-php@v2"
|
||||||
|
with:
|
||||||
|
coverage: "none"
|
||||||
|
php-version: "8.0"
|
||||||
|
ini-file: "development"
|
||||||
|
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_71:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
name: "PHP 8.1 Code on PHP 7.1 Integration Tests"
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "actions/checkout@v3"
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: "shivammathur/setup-php@v2"
|
||||||
|
with:
|
||||||
|
coverage: "none"
|
||||||
|
php-version: "7.1"
|
||||||
|
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.1.6"
|
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.0beta4
|
|
||||||
allow_failures:
|
|
||||||
- name: PHP 8.0 Code on PHP 7.0 Integration Tests
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
script: vendor/bin/phpunit
|
|
261
CHANGELOG.md
261
CHANGELOG.md
@@ -1,7 +1,262 @@
|
|||||||
Version 4.10.3-dev
|
Version 4.19.3 (2024-09-29)
|
||||||
------------------
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Include the trailing semicolon inside `Stmt\GroupUse` nodes, making them consistent with
|
||||||
|
`Stmt\Use_` nodes.
|
||||||
|
* Fixed indentation sometimes becoming negative in formatting-preserving pretty printer, resulting
|
||||||
|
in `ValueError`s.
|
||||||
|
|
||||||
|
Version 4.19.2 (2024-09-17)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Added support for passing enum values to various builder methods, like `BuilderFactory::val()`.
|
||||||
|
|
||||||
|
Version 4.19.1 (2024-03-17)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed "Optional parameter before required parameter" deprecation warning introduced in
|
||||||
|
previous version.
|
||||||
|
|
||||||
|
Version 4.19.0 (2024-03-16)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Do not use implicitly nullable parameters, which are deprecated in PHP 8.4.
|
||||||
|
* Remove support for running on PHP 7.0, which does not support explicitly nullable parameters.
|
||||||
|
|
||||||
|
Version 4.18.0 (2023-12-10)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Added methods `ParserFactory::createForNewestSupportedVersion()` and
|
||||||
|
`ParserFactory::createForHostVersion()` for forward-compatibility with PHP-Parser 5.0.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed missing name resolution of class constant types.
|
||||||
|
* Fixed class members being dropped if an error is encountered while parsing a later class member
|
||||||
|
(when error recovery is enabeld).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* The `grammar/` directory has been excluded from exported git archives.
|
||||||
|
|
||||||
|
Version 4.17.1 (2023-08-13)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed phpdoc mismatches for `ClassConst::$type` introduced in previous release.
|
||||||
|
|
||||||
|
Version 4.17.0 (2023-08-13)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [PHP 8.3] Added support for typed class constants.
|
||||||
|
* [PHP 8.3] Added supprot for dynamic class constant fetch.
|
||||||
|
* [PHP 8.3] Added support for readonly anonymous classes.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed missing required parentheses when pretty printing new with an expression class name.
|
||||||
|
* Fixed missing required parentheses when pretty printing `(CONST)::$x` and similar.
|
||||||
|
|
||||||
|
Version 4.16.0 (2023-06-25)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Added `Name::getParts()` method for forward-compatibility with PHP-Parser 5.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
* Deprecated direct access to `Name::$parts`, which will be removed in PHP-Parser 5.
|
||||||
|
|
||||||
|
Version 4.15.5 (2023-05-19)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Added `makePrivate()`, `makeProtected()`, `makePublic()` and `makeReadonly()` methods to
|
||||||
|
`Builder\Param` to allow the creation of promoted parameters.
|
||||||
|
|
||||||
|
Version 4.15.4 (2023-03-05)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed formatting-preservation for alternative if syntax with trailing comments.
|
||||||
|
|
||||||
|
Version 4.15.3 (2023-01-16)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Support readonly property with PHP 8.2 DNF type.
|
||||||
|
* Fixed PHP attribute group and PHP-Parser attribute mixup in EnumCase builder.
|
||||||
|
|
||||||
|
Version 4.15.2 (2022-11-12)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed parsing of large hex float literals that contain an "e" character.
|
||||||
|
* Fixed tests to pass on 32-bit.
|
||||||
|
* Fixed generation of invalid code when using formatting-preserving pretty printer with code that
|
||||||
|
uses inline HTML.
|
||||||
|
|
||||||
|
Version 4.15.1 (2022-09-04)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed formatting preservation when adding *multiple* attributes to a class/method/etc that
|
||||||
|
previously had none. This fixes a regression in the 4.15.0 release.
|
||||||
|
|
||||||
|
Version 4.15.0 (2022-09-03)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* PHP 8.2: Added support for `true` type.
|
||||||
|
* PHP 8.2: Added support for DNF types.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Support `readonly` as a function name.
|
||||||
|
* Added `__serialize` and `__unserialize` to magic method list.
|
||||||
|
* Fixed bounds check in `Name::slice()`.
|
||||||
|
* Fixed formatting preservation when adding attributes to a class/method/etc that previously had none.
|
||||||
|
|
||||||
|
Version 4.14.0 (2022-05-31)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Added support for readonly classes.
|
||||||
|
* Added `rawValue` attribute to `LNumber`, `DNumber` and `String_` nodes, which stores the unparsed
|
||||||
|
value of the literal (e.g. `"1_000"` rather than `1000`).
|
||||||
|
|
||||||
|
Version 4.13.2 (2021-11-30)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Added builders for enums and enum cases.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* NullsafeMethodCall now extends from CallLike.
|
||||||
|
* The `namespacedName` property populated by the `NameResolver` is now declared on relevant nodes,
|
||||||
|
to avoid a dynamic property deprecation warning with PHP 8.2.
|
||||||
|
|
||||||
|
Version 4.13.1 (2021-11-03)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Support reserved keywords as enum cases.
|
||||||
|
* Support array unpacking in constant expression evaluator.
|
||||||
|
|
||||||
|
Version 4.13.0 (2021-09-20)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [PHP 8.1] Added support for intersection types using a new `IntersectionType` node. Additionally
|
||||||
|
a `ComplexType` parent class for `NullableType`, `UnionType` and `IntersectionType` has been
|
||||||
|
added.
|
||||||
|
* [PHP 8.1] Added support for explicit octal literals.
|
||||||
|
* [PHP 8.1] Added support for first-class callables. These are represented using a call whose first
|
||||||
|
argument is a `VariadicPlaceholder`. The representation is intended to be forward-compatible with
|
||||||
|
partial function application, just like the PHP feature itself. Call nodes now extend from
|
||||||
|
`Expr\CallLike`, which provides an `isFirstClassCallable()` method to determine whether a
|
||||||
|
placeholder id present. `getArgs()` can be used to assert that the call is not a first-class
|
||||||
|
callable and returns `Arg[]` rather than `array<Arg|VariadicPlaceholder>`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Multiple modifiers for promoted properties are now accepted. In particular this allows something
|
||||||
|
like `public readonly` for promoted properties.
|
||||||
|
* Formatting-preserving pretty printing for comments in array literals has been fixed.
|
||||||
|
|
||||||
|
Version 4.12.0 (2021-07-21)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [PHP 8.1] Added support for readonly properties (through a new `MODIFIER_READONLY`).
|
||||||
|
* [PHP 8.1] Added support for final class constants.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed compatibility with PHP 8.1. `&` tokens are now canonicalized to the
|
||||||
|
`T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG` and `T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG` tokens
|
||||||
|
used in PHP 8.1. This happens unconditionally, regardless of whether the emulative lexer is used.
|
||||||
|
|
||||||
|
Version 4.11.0 (2021-07-03)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* `BuilderFactory::args()` now accepts named arguments.
|
||||||
|
* `BuilderFactory::attribute()` has been added.
|
||||||
|
* An `addAttribute()` method accepting an `Attribute` or `AttributeGroup` has been adde to all
|
||||||
|
builders that accept attributes, such as `Builder\Class_`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* `NameResolver` now handles enums.
|
||||||
|
* `PrettyPrinter` now prints backing enum type.
|
||||||
|
* Builder methods for types now property handle `never` type.
|
||||||
|
|
||||||
|
Version 4.10.5 (2021-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.
|
||||||
|
|
||||||
Nothing yet.
|
|
||||||
|
|
||||||
Version 4.10.2 (2020-09-26)
|
Version 4.10.2 (2020-09-26)
|
||||||
------------------
|
------------------
|
||||||
|
12
README.md
12
README.md
@@ -1,12 +1,12 @@
|
|||||||
PHP Parser
|
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.2 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||||
manipulation.
|
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_4_x] (stable; for running on PHP >= 7.1; for parsing PHP 5.2 to PHP 8.2).
|
||||||
|
|
||||||
[Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).
|
[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:
|
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.
|
* Invalid code can be parsed into a partial AST.
|
||||||
* The AST contains accurate location information.
|
* The AST contains accurate location information.
|
||||||
* Dumping the AST in human-readable form.
|
* Dumping the AST in human-readable form.
|
||||||
@@ -215,11 +215,11 @@ Component documentation:
|
|||||||
* [JSON representation](doc/component/JSON_representation.markdown)
|
* [JSON representation](doc/component/JSON_representation.markdown)
|
||||||
* JSON encoding and decoding of ASTs
|
* JSON encoding and decoding of ASTs
|
||||||
* [Performance](doc/component/Performance.markdown)
|
* [Performance](doc/component/Performance.markdown)
|
||||||
* Disabling XDebug
|
* Disabling Xdebug
|
||||||
* Reusing objects
|
* Reusing objects
|
||||||
* Garbage collection impact
|
* Garbage collection impact
|
||||||
* [Frequently asked questions](doc/component/FAQ.markdown)
|
* [Frequently asked questions](doc/component/FAQ.markdown)
|
||||||
* Parent and sibling references
|
* Parent and sibling references
|
||||||
|
|
||||||
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
|
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
|
||||||
[doc_master]: https://github.com/nikic/PHP-Parser/tree/master/doc
|
[doc_4_x]: https://github.com/nikic/PHP-Parser/tree/4.x/doc
|
||||||
|
@@ -10,7 +10,7 @@ foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php
|
|||||||
|
|
||||||
ini_set('xdebug.max_nesting_level', 3000);
|
ini_set('xdebug.max_nesting_level', 3000);
|
||||||
|
|
||||||
// Disable XDebug var_dump() output truncation
|
// Disable Xdebug var_dump() output truncation
|
||||||
ini_set('xdebug.var_display_max_children', -1);
|
ini_set('xdebug.var_display_max_children', -1);
|
||||||
ini_set('xdebug.var_display_max_data', -1);
|
ini_set('xdebug.var_display_max_data', -1);
|
||||||
ini_set('xdebug.var_display_max_depth', -1);
|
ini_set('xdebug.var_display_max_depth', -1);
|
||||||
|
@@ -13,11 +13,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.0",
|
"php": ">=7.1",
|
||||||
"ext-tokenizer": "*"
|
"ext-tokenizer": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0",
|
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||||
"ircmaxell/php-yacc": "^0.0.7"
|
"ircmaxell/php-yacc": "^0.0.7"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
Introduction
|
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?
|
What is this for?
|
||||||
-----------------
|
-----------------
|
||||||
@@ -26,11 +26,15 @@ programmatic PHP code analysis are incidentally PHP developers, not C developers
|
|||||||
What can it parse?
|
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
|
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.
|
version it runs on), additionally a wrapper for emulating tokens from newer versions is provided.
|
||||||
This allows to parse PHP 7.4 source code running on PHP 7.0, for example. This emulation is somewhat
|
This allows to parse PHP 7.4 source code running on PHP 7.1, for example. This emulation is somewhat
|
||||||
hacky and not perfect, but it should work well on any sane code.
|
hacky and not perfect, but it should work well on any sane code.
|
||||||
|
|
||||||
What output does it produce?
|
What output does it produce?
|
||||||
|
@@ -19,7 +19,7 @@ ini_set('xdebug.max_nesting_level', 3000);
|
|||||||
```
|
```
|
||||||
|
|
||||||
This ensures that there will be no errors when traversing highly nested node trees. However, it is
|
This ensures that there will be no errors when traversing highly nested node trees. However, it is
|
||||||
preferable to disable XDebug completely, as it can easily make this library more than five times
|
preferable to disable Xdebug completely, as it can easily make this library more than five times
|
||||||
slower.
|
slower.
|
||||||
|
|
||||||
Parsing
|
Parsing
|
||||||
|
@@ -39,7 +39,7 @@ Component documentation
|
|||||||
* [JSON representation](component/JSON_representation.markdown)
|
* [JSON representation](component/JSON_representation.markdown)
|
||||||
* JSON encoding and decoding of ASTs
|
* JSON encoding and decoding of ASTs
|
||||||
* [Performance](component/Performance.markdown)
|
* [Performance](component/Performance.markdown)
|
||||||
* Disabling XDebug
|
* Disabling Xdebug
|
||||||
* Reusing objects
|
* Reusing objects
|
||||||
* Garbage collection impact
|
* Garbage collection impact
|
||||||
* [Frequently asked questions](component/FAQ.markdown)
|
* [Frequently asked questions](component/FAQ.markdown)
|
||||||
|
@@ -36,7 +36,7 @@ if ($e->hasColumnInfo()) {
|
|||||||
echo $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code)
|
echo $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code)
|
||||||
. ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code);
|
. ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code);
|
||||||
// or:
|
// or:
|
||||||
echo $e->getMessageWithColumnInfo();
|
echo $e->getMessageWithColumnInfo($code);
|
||||||
} else {
|
} else {
|
||||||
echo $e->getMessage();
|
echo $e->getMessage();
|
||||||
}
|
}
|
||||||
|
@@ -8,20 +8,20 @@ described in the following.
|
|||||||
Xdebug
|
Xdebug
|
||||||
------
|
------
|
||||||
|
|
||||||
Running PHP with XDebug adds a lot of overhead, especially for code that performs many method calls.
|
Running PHP with Xdebug adds a lot of overhead, especially for code that performs many method calls.
|
||||||
Just by loading XDebug (without enabling profiling or other more intrusive XDebug features), you
|
Just by loading Xdebug (without enabling profiling or other more intrusive Xdebug features), you
|
||||||
can expect that code using PHP-Parser will be approximately *five times slower*.
|
can expect that code using PHP-Parser will be approximately *five times slower*.
|
||||||
|
|
||||||
As such, you should make sure that XDebug is not loaded when using this library. Note that setting
|
As such, you should make sure that Xdebug is not loaded when using this library. Note that setting
|
||||||
the `xdebug.default_enable=0` ini option does *not* disable XDebug. The *only* way to disable
|
the `xdebug.default_enable=0` ini option does *not* disable Xdebug. The *only* way to disable
|
||||||
XDebug is to not load the extension in the first place.
|
Xdebug is to not load the extension in the first place.
|
||||||
|
|
||||||
If you are building a command-line utility for use by developers (who often have XDebug enabled),
|
If you are building a command-line utility for use by developers (who often have Xdebug enabled),
|
||||||
you may want to consider automatically restarting PHP with XDebug unloaded. The
|
you may want to consider automatically restarting PHP with Xdebug unloaded. The
|
||||||
[composer/xdebug-handler](https://github.com/composer/xdebug-handler) package can be used to do
|
[composer/xdebug-handler](https://github.com/composer/xdebug-handler) package can be used to do
|
||||||
this.
|
this.
|
||||||
|
|
||||||
If you do run with XDebug, you may need to increase the `xdebug.max_nesting_level` option to a
|
If you do run with Xdebug, you may need to increase the `xdebug.max_nesting_level` option to a
|
||||||
higher level, such as 3000. While the parser itself is recursion free, most other code working on
|
higher level, such as 3000. While the parser itself is recursion free, most other code working on
|
||||||
the AST uses recursion and will generate an error if the value of this option is too low.
|
the AST uses recursion and will generate an error if the value of this option is too low.
|
||||||
|
|
||||||
|
@@ -193,7 +193,7 @@ anonymous classes), you know that once you've seen a class declaration, there is
|
|||||||
checking all it's child nodes, because PHP does not allow nesting classes. In this case, you can
|
checking all it's child nodes, because PHP does not allow nesting classes. In this case, you can
|
||||||
instruct the traverser to not recurse into the class node:
|
instruct the traverser to not recurse into the class node:
|
||||||
|
|
||||||
```
|
```php
|
||||||
private $classes = [];
|
private $classes = [];
|
||||||
public function enterNode(Node $node) {
|
public function enterNode(Node $node) {
|
||||||
if ($node instanceof Node\Stmt\Class_) {
|
if ($node instanceof Node\Stmt\Class_) {
|
||||||
@@ -211,7 +211,7 @@ after finding it. For example, if you are looking for the node of a class with a
|
|||||||
discounting exotic cases like conditionally defining a class two times), you can stop traversal
|
discounting exotic cases like conditionally defining a class two times), you can stop traversal
|
||||||
once you found it:
|
once you found it:
|
||||||
|
|
||||||
```
|
```php
|
||||||
private $class = null;
|
private $class = null;
|
||||||
public function enterNode(Node $node) {
|
public function enterNode(Node $node) {
|
||||||
if ($node instanceof Node\Stmt\Class_ &&
|
if ($node instanceof Node\Stmt\Class_ &&
|
||||||
@@ -251,7 +251,7 @@ Stmt_Return(
|
|||||||
|
|
||||||
the following method calls will be performed:
|
the following method calls will be performed:
|
||||||
|
|
||||||
```
|
```php
|
||||||
$visitorA->enterNode(Stmt_Return)
|
$visitorA->enterNode(Stmt_Return)
|
||||||
$visitorB->enterNode(Stmt_Return)
|
$visitorB->enterNode(Stmt_Return)
|
||||||
$visitorA->enterNode(Expr_Variable)
|
$visitorA->enterNode(Expr_Variable)
|
||||||
|
@@ -20,6 +20,11 @@ top_statement_list:
|
|||||||
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
|
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
ampersand:
|
||||||
|
T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
|
||||||
|
| T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG
|
||||||
|
;
|
||||||
|
|
||||||
reserved_non_modifiers:
|
reserved_non_modifiers:
|
||||||
T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
|
T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
|
||||||
| T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
|
| T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
|
||||||
@@ -83,7 +88,7 @@ top_statement:
|
|||||||
$this->checkNamespace($$); }
|
$this->checkNamespace($$); }
|
||||||
| T_USE use_declarations ';' { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
| T_USE use_declarations ';' { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
||||||
| T_USE use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; }
|
| T_USE use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; }
|
||||||
| group_use_declaration ';' { $$ = $1; }
|
| group_use_declaration { $$ = $1; }
|
||||||
| T_CONST constant_declaration_list ';' { $$ = Stmt\Const_[$2]; }
|
| T_CONST constant_declaration_list ';' { $$ = Stmt\Const_[$2]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -93,9 +98,9 @@ use_type:
|
|||||||
;
|
;
|
||||||
|
|
||||||
group_use_declaration:
|
group_use_declaration:
|
||||||
T_USE use_type legacy_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
|
T_USE use_type legacy_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}' ';'
|
||||||
{ $$ = Stmt\GroupUse[$3, $6, $2]; }
|
{ $$ = Stmt\GroupUse[$3, $6, $2]; }
|
||||||
| T_USE legacy_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}'
|
| T_USE legacy_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}' ';'
|
||||||
{ $$ = Stmt\GroupUse[$2, $5, Stmt\Use_::TYPE_UNKNOWN]; }
|
{ $$ = Stmt\GroupUse[$2, $5, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -246,7 +251,12 @@ variables_list:
|
|||||||
|
|
||||||
optional_ref:
|
optional_ref:
|
||||||
/* empty */ { $$ = false; }
|
/* empty */ { $$ = false; }
|
||||||
| '&' { $$ = true; }
|
| ampersand { $$ = true; }
|
||||||
|
;
|
||||||
|
|
||||||
|
optional_arg_ref:
|
||||||
|
/* empty */ { $$ = false; }
|
||||||
|
| T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG { $$ = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
optional_ellipsis:
|
optional_ellipsis:
|
||||||
@@ -254,8 +264,13 @@ optional_ellipsis:
|
|||||||
| T_ELLIPSIS { $$ = true; }
|
| T_ELLIPSIS { $$ = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
identifier_maybe_readonly:
|
||||||
|
identifier { $$ = $1; }
|
||||||
|
| T_READONLY { $$ = Node\Identifier[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
function_declaration_statement:
|
function_declaration_statement:
|
||||||
T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
T_FUNCTION optional_ref identifier_maybe_readonly '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
||||||
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; }
|
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -378,7 +393,7 @@ new_else_single:
|
|||||||
|
|
||||||
foreach_variable:
|
foreach_variable:
|
||||||
variable { $$ = array($1, false); }
|
variable { $$ = array($1, false); }
|
||||||
| '&' variable { $$ = array($2, true); }
|
| ampersand variable { $$ = array($2, true); }
|
||||||
| list_expr { $$ = array($1, false); }
|
| list_expr { $$ = array($1, false); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -393,9 +408,9 @@ non_empty_parameter_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
parameter:
|
parameter:
|
||||||
optional_param_type optional_ref optional_ellipsis plain_variable
|
optional_param_type optional_arg_ref optional_ellipsis plain_variable
|
||||||
{ $$ = Node\Param[$4, null, $1, $2, $3]; $this->checkParam($$); }
|
{ $$ = Node\Param[$4, null, $1, $2, $3]; $this->checkParam($$); }
|
||||||
| optional_param_type optional_ref optional_ellipsis plain_variable '=' static_scalar
|
| optional_param_type optional_arg_ref optional_ellipsis plain_variable '=' static_scalar
|
||||||
{ $$ = Node\Param[$4, $6, $1, $2, $3]; $this->checkParam($$); }
|
{ $$ = Node\Param[$4, $6, $1, $2, $3]; $this->checkParam($$); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -428,7 +443,7 @@ non_empty_argument_list:
|
|||||||
|
|
||||||
argument:
|
argument:
|
||||||
expr { $$ = Node\Arg[$1, false, false]; }
|
expr { $$ = Node\Arg[$1, false, false]; }
|
||||||
| '&' variable { $$ = Node\Arg[$2, true, false]; }
|
| ampersand variable { $$ = Node\Arg[$2, true, false]; }
|
||||||
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -454,7 +469,7 @@ static_var:
|
|||||||
;
|
;
|
||||||
|
|
||||||
class_statement_list_ex:
|
class_statement_list_ex:
|
||||||
class_statement_list_ex class_statement { if ($2 !== null) { push($1, $2); } }
|
class_statement_list_ex class_statement { if ($2 !== null) { push($1, $2); } else { $$ = $1; } }
|
||||||
| /* empty */ { init(); }
|
| /* empty */ { init(); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -562,8 +577,8 @@ expr:
|
|||||||
variable { $$ = $1; }
|
variable { $$ = $1; }
|
||||||
| list_expr '=' expr { $$ = Expr\Assign[$1, $3]; }
|
| list_expr '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||||
| variable '=' expr { $$ = Expr\Assign[$1, $3]; }
|
| variable '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||||
| variable '=' '&' variable { $$ = Expr\AssignRef[$1, $4]; }
|
| variable '=' ampersand variable { $$ = Expr\AssignRef[$1, $4]; }
|
||||||
| variable '=' '&' new_expr { $$ = Expr\AssignRef[$1, $4]; }
|
| variable '=' ampersand new_expr { $$ = Expr\AssignRef[$1, $4]; }
|
||||||
| new_expr { $$ = $1; }
|
| new_expr { $$ = $1; }
|
||||||
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
||||||
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
|
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
|
||||||
@@ -589,7 +604,8 @@ expr:
|
|||||||
| expr T_LOGICAL_AND expr { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
|
| expr T_LOGICAL_AND expr { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
|
||||||
| expr T_LOGICAL_XOR expr { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
|
| expr T_LOGICAL_XOR expr { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
|
||||||
| expr '|' expr { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
|
| expr '|' expr { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
|
||||||
| expr '&' expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
| expr T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
||||||
|
| expr T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
||||||
| expr '^' expr { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
|
| expr '^' expr { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
|
||||||
| expr '.' expr { $$ = Expr\BinaryOp\Concat [$1, $3]; }
|
| expr '.' expr { $$ = Expr\BinaryOp\Concat [$1, $3]; }
|
||||||
| expr '+' expr { $$ = Expr\BinaryOp\Plus [$1, $3]; }
|
| expr '+' expr { $$ = Expr\BinaryOp\Plus [$1, $3]; }
|
||||||
@@ -678,9 +694,7 @@ array_expr:
|
|||||||
|
|
||||||
scalar_dereference:
|
scalar_dereference:
|
||||||
array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']'
|
| T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[Scalar\String_::fromString($1, attributes()), $3]; }
|
||||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
|
||||||
$$ = Expr\ArrayDimFetch[new Scalar\String_(Scalar\String_::parse($1), $attrs), $3]; }
|
|
||||||
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
/* alternative array syntax missing intentionally */
|
/* alternative array syntax missing intentionally */
|
||||||
@@ -712,8 +726,13 @@ lexical_var:
|
|||||||
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
|
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
name_readonly:
|
||||||
|
T_READONLY { $$ = Name[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
function_call:
|
function_call:
|
||||||
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
|
| name_readonly argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex argument_list
|
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex argument_list
|
||||||
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
||||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list
|
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list
|
||||||
@@ -782,10 +801,8 @@ ctor_arguments:
|
|||||||
|
|
||||||
common_scalar:
|
common_scalar:
|
||||||
T_LNUMBER { $$ = $this->parseLNumber($1, attributes(), true); }
|
T_LNUMBER { $$ = $this->parseLNumber($1, attributes(), true); }
|
||||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
| T_DNUMBER { $$ = Scalar\DNumber::fromString($1, attributes()); }
|
||||||
| T_CONSTANT_ENCAPSED_STRING
|
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_::fromString($1, attributes(), false); }
|
||||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
|
||||||
$$ = new Scalar\String_(Scalar\String_::parse($1, false), $attrs); }
|
|
||||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||||
@@ -816,7 +833,10 @@ static_operation:
|
|||||||
| static_scalar T_LOGICAL_AND static_scalar { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
|
| static_scalar T_LOGICAL_AND static_scalar { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
|
||||||
| static_scalar T_LOGICAL_XOR static_scalar { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
|
| static_scalar T_LOGICAL_XOR static_scalar { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
|
||||||
| static_scalar '|' static_scalar { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
|
| static_scalar '|' static_scalar { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
|
||||||
| static_scalar '&' static_scalar { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
| static_scalar T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG static_scalar
|
||||||
|
{ $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
||||||
|
| static_scalar T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG static_scalar
|
||||||
|
{ $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
||||||
| static_scalar '^' static_scalar { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
|
| static_scalar '^' static_scalar { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
|
||||||
| static_scalar '.' static_scalar { $$ = Expr\BinaryOp\Concat [$1, $3]; }
|
| static_scalar '.' static_scalar { $$ = Expr\BinaryOp\Concat [$1, $3]; }
|
||||||
| static_scalar '+' static_scalar { $$ = Expr\BinaryOp\Plus [$1, $3]; }
|
| static_scalar '+' static_scalar { $$ = Expr\BinaryOp\Plus [$1, $3]; }
|
||||||
@@ -986,9 +1006,9 @@ non_empty_array_pair_list:
|
|||||||
array_pair:
|
array_pair:
|
||||||
expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||||
| expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
| expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||||
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
|
| expr T_DOUBLE_ARROW ampersand variable { $$ = Expr\ArrayItem[$4, $1, true]; }
|
||||||
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
|
| ampersand variable { $$ = Expr\ArrayItem[$2, null, true]; }
|
||||||
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
|
| T_ELLIPSIS expr { $$ = new Expr\ArrayItem($2, null, false, attributes(), true); }
|
||||||
;
|
;
|
||||||
|
|
||||||
encaps_list:
|
encaps_list:
|
||||||
|
248
grammar/php7.y
248
grammar/php7.y
@@ -20,6 +20,11 @@ top_statement_list:
|
|||||||
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
|
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
ampersand:
|
||||||
|
T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
|
||||||
|
| T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG
|
||||||
|
;
|
||||||
|
|
||||||
reserved_non_modifiers:
|
reserved_non_modifiers:
|
||||||
T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
|
T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
|
||||||
| T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
|
| T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
|
||||||
@@ -28,20 +33,20 @@ reserved_non_modifiers:
|
|||||||
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
|
| 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_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_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:
|
semi_reserved:
|
||||||
reserved_non_modifiers
|
reserved_non_modifiers
|
||||||
| T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC
|
| T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY
|
||||||
;
|
;
|
||||||
|
|
||||||
identifier_ex:
|
identifier_maybe_reserved:
|
||||||
T_STRING { $$ = Node\Identifier[$1]; }
|
T_STRING { $$ = Node\Identifier[$1]; }
|
||||||
| semi_reserved { $$ = Node\Identifier[$1]; }
|
| semi_reserved { $$ = Node\Identifier[$1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
identifier:
|
identifier_not_reserved:
|
||||||
T_STRING { $$ = Node\Identifier[$1]; }
|
T_STRING { $$ = Node\Identifier[$1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -128,7 +133,7 @@ top_statement:
|
|||||||
$this->checkNamespace($$); }
|
$this->checkNamespace($$); }
|
||||||
| T_USE use_declarations semi { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
| T_USE use_declarations semi { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
||||||
| T_USE use_type use_declarations semi { $$ = Stmt\Use_[$3, $2]; }
|
| T_USE use_type use_declarations semi { $$ = Stmt\Use_[$3, $2]; }
|
||||||
| group_use_declaration semi { $$ = $1; }
|
| group_use_declaration { $$ = $1; }
|
||||||
| T_CONST constant_declaration_list semi { $$ = Stmt\Const_[$2]; }
|
| T_CONST constant_declaration_list semi { $$ = Stmt\Const_[$2]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -138,9 +143,9 @@ use_type:
|
|||||||
;
|
;
|
||||||
|
|
||||||
group_use_declaration:
|
group_use_declaration:
|
||||||
T_USE use_type legacy_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
|
T_USE use_type legacy_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}' semi
|
||||||
{ $$ = Stmt\GroupUse[$3, $6, $2]; }
|
{ $$ = Stmt\GroupUse[$3, $6, $2]; }
|
||||||
| T_USE legacy_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}'
|
| T_USE legacy_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}' semi
|
||||||
{ $$ = Stmt\GroupUse[$2, $5, Stmt\Use_::TYPE_UNKNOWN]; }
|
{ $$ = Stmt\GroupUse[$2, $5, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -176,14 +181,14 @@ non_empty_inline_use_declarations:
|
|||||||
unprefixed_use_declaration:
|
unprefixed_use_declaration:
|
||||||
namespace_name
|
namespace_name
|
||||||
{ $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
|
{ $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
|
||||||
| namespace_name T_AS identifier
|
| namespace_name T_AS identifier_not_reserved
|
||||||
{ $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
|
{ $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
use_declaration:
|
use_declaration:
|
||||||
legacy_namespace_name
|
legacy_namespace_name
|
||||||
{ $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
|
{ $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
|
||||||
| legacy_namespace_name T_AS identifier
|
| legacy_namespace_name T_AS identifier_not_reserved
|
||||||
{ $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
|
{ $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -203,7 +208,7 @@ non_empty_constant_declaration_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
constant_declaration:
|
constant_declaration:
|
||||||
identifier '=' expr { $$ = Node\Const_[$1, $3]; }
|
identifier_not_reserved '=' expr { $$ = Node\Const_[$1, $3]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
class_const_list:
|
class_const_list:
|
||||||
@@ -216,7 +221,10 @@ non_empty_class_const_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
class_const:
|
class_const:
|
||||||
identifier_ex '=' expr { $$ = Node\Const_[$1, $3]; }
|
T_STRING '=' expr
|
||||||
|
{ $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
|
||||||
|
| semi_reserved '=' expr
|
||||||
|
{ $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
inner_statement_list_ex:
|
inner_statement_list_ex:
|
||||||
@@ -284,8 +292,8 @@ non_empty_statement:
|
|||||||
| T_DECLARE '(' declare_list ')' declare_statement { $$ = Stmt\Declare_[$3, $5]; }
|
| T_DECLARE '(' declare_list ')' declare_statement { $$ = Stmt\Declare_[$3, $5]; }
|
||||||
| T_TRY '{' inner_statement_list '}' catches optional_finally
|
| T_TRY '{' inner_statement_list '}' catches optional_finally
|
||||||
{ $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); }
|
{ $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); }
|
||||||
| T_GOTO identifier semi { $$ = Stmt\Goto_[$2]; }
|
| T_GOTO identifier_not_reserved semi { $$ = Stmt\Goto_[$2]; }
|
||||||
| identifier ':' { $$ = Stmt\Label[$1]; }
|
| identifier_not_reserved ':' { $$ = Stmt\Label[$1]; }
|
||||||
| error { $$ = array(); /* means: no statement */ }
|
| error { $$ = array(); /* means: no statement */ }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -327,7 +335,12 @@ non_empty_variables_list:
|
|||||||
|
|
||||||
optional_ref:
|
optional_ref:
|
||||||
/* empty */ { $$ = false; }
|
/* empty */ { $$ = false; }
|
||||||
| '&' { $$ = true; }
|
| ampersand { $$ = true; }
|
||||||
|
;
|
||||||
|
|
||||||
|
optional_arg_ref:
|
||||||
|
/* empty */ { $$ = false; }
|
||||||
|
| T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG { $$ = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
optional_ellipsis:
|
optional_ellipsis:
|
||||||
@@ -340,31 +353,58 @@ block_or_error:
|
|||||||
| error { $$ = []; }
|
| error { $$ = []; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
identifier_maybe_readonly:
|
||||||
|
identifier_not_reserved { $$ = $1; }
|
||||||
|
| T_READONLY { $$ = Node\Identifier[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
function_declaration_statement:
|
function_declaration_statement:
|
||||||
T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type block_or_error
|
T_FUNCTION optional_ref identifier_maybe_readonly '(' parameter_list ')' optional_return_type block_or_error
|
||||||
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8, 'attrGroups' => []]]; }
|
{ $$ = 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
|
| attributes T_FUNCTION optional_ref identifier_maybe_readonly '(' parameter_list ')' optional_return_type block_or_error
|
||||||
{ $$ = Stmt\Function_[$4, ['byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
|
{ $$ = Stmt\Function_[$4, ['byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
class_declaration_statement:
|
class_declaration_statement:
|
||||||
class_entry_type identifier extends_from implements_list '{' class_statement_list '}'
|
class_entry_type identifier_not_reserved extends_from implements_list '{' class_statement_list '}'
|
||||||
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6, 'attrGroups' => []]];
|
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6, 'attrGroups' => []]];
|
||||||
$this->checkClass($$, #2); }
|
$this->checkClass($$, #2); }
|
||||||
| attributes class_entry_type identifier extends_from implements_list '{' class_statement_list '}'
|
| attributes class_entry_type identifier_not_reserved extends_from implements_list '{' class_statement_list '}'
|
||||||
{ $$ = Stmt\Class_[$3, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
|
{ $$ = Stmt\Class_[$3, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
|
||||||
$this->checkClass($$, #3); }
|
$this->checkClass($$, #3); }
|
||||||
| optional_attributes T_INTERFACE identifier interface_extends_list '{' class_statement_list '}'
|
| optional_attributes T_INTERFACE identifier_not_reserved interface_extends_list '{' class_statement_list '}'
|
||||||
{ $$ = Stmt\Interface_[$3, ['extends' => $4, 'stmts' => $6, 'attrGroups' => $1]];
|
{ $$ = Stmt\Interface_[$3, ['extends' => $4, 'stmts' => $6, 'attrGroups' => $1]];
|
||||||
$this->checkInterface($$, #3); }
|
$this->checkInterface($$, #3); }
|
||||||
| optional_attributes T_TRAIT identifier '{' class_statement_list '}'
|
| optional_attributes T_TRAIT identifier_not_reserved '{' class_statement_list '}'
|
||||||
{ $$ = Stmt\Trait_[$3, ['stmts' => $5, 'attrGroups' => $1]]; }
|
{ $$ = Stmt\Trait_[$3, ['stmts' => $5, 'attrGroups' => $1]]; }
|
||||||
|
| optional_attributes T_ENUM identifier_not_reserved 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:
|
class_entry_type:
|
||||||
T_CLASS { $$ = 0; }
|
T_CLASS { $$ = 0; }
|
||||||
| T_ABSTRACT T_CLASS { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
| class_modifiers T_CLASS { $$ = $1; }
|
||||||
| T_FINAL T_CLASS { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
;
|
||||||
|
|
||||||
|
class_modifiers:
|
||||||
|
class_modifier { $$ = $1; }
|
||||||
|
| class_modifiers class_modifier { $this->checkClassModifier($1, $2, #2); $$ = $1 | $2; }
|
||||||
|
;
|
||||||
|
|
||||||
|
class_modifier:
|
||||||
|
T_ABSTRACT { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
||||||
|
| T_FINAL { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
||||||
|
| T_READONLY { $$ = Stmt\Class_::MODIFIER_READONLY; }
|
||||||
;
|
;
|
||||||
|
|
||||||
extends_from:
|
extends_from:
|
||||||
@@ -417,7 +457,7 @@ non_empty_declare_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
declare_list_element:
|
declare_list_element:
|
||||||
identifier '=' expr { $$ = Stmt\DeclareDeclare[$1, $3]; }
|
identifier_not_reserved '=' expr { $$ = Stmt\DeclareDeclare[$1, $3]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
switch_case_list:
|
switch_case_list:
|
||||||
@@ -481,7 +521,8 @@ new_elseif_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
new_elseif:
|
new_elseif:
|
||||||
T_ELSEIF '(' expr ')' ':' inner_statement_list { $$ = Stmt\ElseIf_[$3, $6]; }
|
T_ELSEIF '(' expr ')' ':' inner_statement_list
|
||||||
|
{ $$ = Stmt\ElseIf_[$3, $6]; $this->fixupAlternativeElse($$); }
|
||||||
;
|
;
|
||||||
|
|
||||||
else_single:
|
else_single:
|
||||||
@@ -491,12 +532,13 @@ else_single:
|
|||||||
|
|
||||||
new_else_single:
|
new_else_single:
|
||||||
/* empty */ { $$ = null; }
|
/* empty */ { $$ = null; }
|
||||||
| T_ELSE ':' inner_statement_list { $$ = Stmt\Else_[$3]; }
|
| T_ELSE ':' inner_statement_list
|
||||||
|
{ $$ = Stmt\Else_[$3]; $this->fixupAlternativeElse($$); }
|
||||||
;
|
;
|
||||||
|
|
||||||
foreach_variable:
|
foreach_variable:
|
||||||
variable { $$ = array($1, false); }
|
variable { $$ = array($1, false); }
|
||||||
| '&' variable { $$ = array($2, true); }
|
| ampersand variable { $$ = array($2, true); }
|
||||||
| list_expr { $$ = array($1, false); }
|
| list_expr { $$ = array($1, false); }
|
||||||
| array_short_syntax { $$ = array($1, false); }
|
| array_short_syntax { $$ = array($1, false); }
|
||||||
;
|
;
|
||||||
@@ -511,21 +553,30 @@ non_empty_parameter_list:
|
|||||||
| non_empty_parameter_list ',' parameter { push($1, $3); }
|
| non_empty_parameter_list ',' parameter { push($1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
optional_visibility_modifier:
|
optional_property_modifiers:
|
||||||
/* empty */ { $$ = 0; }
|
/* empty */ { $$ = 0; }
|
||||||
| T_PUBLIC { $$ = Stmt\Class_::MODIFIER_PUBLIC; }
|
| optional_property_modifiers property_modifier
|
||||||
|
{ $this->checkModifier($1, $2, #2); $$ = $1 | $2; }
|
||||||
|
;
|
||||||
|
|
||||||
|
property_modifier:
|
||||||
|
T_PUBLIC { $$ = Stmt\Class_::MODIFIER_PUBLIC; }
|
||||||
| T_PROTECTED { $$ = Stmt\Class_::MODIFIER_PROTECTED; }
|
| T_PROTECTED { $$ = Stmt\Class_::MODIFIER_PROTECTED; }
|
||||||
| T_PRIVATE { $$ = Stmt\Class_::MODIFIER_PRIVATE; }
|
| T_PRIVATE { $$ = Stmt\Class_::MODIFIER_PRIVATE; }
|
||||||
|
| T_READONLY { $$ = Stmt\Class_::MODIFIER_READONLY; }
|
||||||
;
|
;
|
||||||
|
|
||||||
parameter:
|
parameter:
|
||||||
optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable
|
optional_attributes optional_property_modifiers optional_type_without_static
|
||||||
|
optional_arg_ref optional_ellipsis plain_variable
|
||||||
{ $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1);
|
{ $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1);
|
||||||
$this->checkParam($$); }
|
$this->checkParam($$); }
|
||||||
| optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable '=' expr
|
| optional_attributes optional_property_modifiers optional_type_without_static
|
||||||
|
optional_arg_ref optional_ellipsis plain_variable '=' expr
|
||||||
{ $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1);
|
{ $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1);
|
||||||
$this->checkParam($$); }
|
$this->checkParam($$); }
|
||||||
| optional_attributes optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis error
|
| optional_attributes optional_property_modifiers optional_type_without_static
|
||||||
|
optional_arg_ref optional_ellipsis error
|
||||||
{ $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
|
{ $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -533,6 +584,7 @@ type_expr:
|
|||||||
type { $$ = $1; }
|
type { $$ = $1; }
|
||||||
| '?' type { $$ = Node\NullableType[$2]; }
|
| '?' type { $$ = Node\NullableType[$2]; }
|
||||||
| union_type { $$ = Node\UnionType[$1]; }
|
| union_type { $$ = Node\UnionType[$1]; }
|
||||||
|
| intersection_type { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
type:
|
type:
|
||||||
@@ -546,20 +598,52 @@ type_without_static:
|
|||||||
| T_CALLABLE { $$ = Node\Identifier['callable']; }
|
| T_CALLABLE { $$ = Node\Identifier['callable']; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
union_type_element:
|
||||||
|
type { $$ = $1; }
|
||||||
|
| '(' intersection_type ')' { $$ = $2; }
|
||||||
|
;
|
||||||
|
|
||||||
union_type:
|
union_type:
|
||||||
type '|' type { init($1, $3); }
|
union_type_element '|' union_type_element { init($1, $3); }
|
||||||
| union_type '|' type { push($1, $3); }
|
| union_type '|' union_type_element { push($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
|
union_type_without_static_element:
|
||||||
|
type_without_static { $$ = $1; }
|
||||||
|
| '(' intersection_type_without_static ')' { $$ = $2; }
|
||||||
;
|
;
|
||||||
|
|
||||||
union_type_without_static:
|
union_type_without_static:
|
||||||
type_without_static '|' type_without_static { init($1, $3); }
|
union_type_without_static_element '|' union_type_without_static_element { init($1, $3); }
|
||||||
| union_type_without_static '|' type_without_static { push($1, $3); }
|
| union_type_without_static '|' union_type_without_static_element { push($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
|
intersection_type_list:
|
||||||
|
type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type { init($1, $3); }
|
||||||
|
| intersection_type_list T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type
|
||||||
|
{ push($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
|
intersection_type:
|
||||||
|
intersection_type_list { $$ = Node\IntersectionType[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
|
intersection_type_without_static_list:
|
||||||
|
type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
|
||||||
|
{ init($1, $3); }
|
||||||
|
| intersection_type_without_static_list T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
|
||||||
|
{ push($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
|
intersection_type_without_static:
|
||||||
|
intersection_type_without_static_list { $$ = Node\IntersectionType[$1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
type_expr_without_static:
|
type_expr_without_static:
|
||||||
type_without_static { $$ = $1; }
|
type_without_static { $$ = $1; }
|
||||||
| '?' type_without_static { $$ = Node\NullableType[$2]; }
|
| '?' type_without_static { $$ = Node\NullableType[$2]; }
|
||||||
| union_type_without_static { $$ = Node\UnionType[$1]; }
|
| union_type_without_static { $$ = Node\UnionType[$1]; }
|
||||||
|
| intersection_type_without_static { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
optional_type_without_static:
|
optional_type_without_static:
|
||||||
@@ -576,6 +660,11 @@ optional_return_type:
|
|||||||
argument_list:
|
argument_list:
|
||||||
'(' ')' { $$ = array(); }
|
'(' ')' { $$ = array(); }
|
||||||
| '(' non_empty_argument_list optional_comma ')' { $$ = $2; }
|
| '(' non_empty_argument_list optional_comma ')' { $$ = $2; }
|
||||||
|
| '(' variadic_placeholder ')' { init($2); }
|
||||||
|
;
|
||||||
|
|
||||||
|
variadic_placeholder:
|
||||||
|
T_ELLIPSIS { $$ = Node\VariadicPlaceholder[]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
non_empty_argument_list:
|
non_empty_argument_list:
|
||||||
@@ -585,9 +674,9 @@ non_empty_argument_list:
|
|||||||
|
|
||||||
argument:
|
argument:
|
||||||
expr { $$ = Node\Arg[$1, false, false]; }
|
expr { $$ = Node\Arg[$1, false, false]; }
|
||||||
| '&' variable { $$ = Node\Arg[$2, true, false]; }
|
| ampersand variable { $$ = Node\Arg[$2, true, false]; }
|
||||||
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
||||||
| identifier_ex ':' expr
|
| identifier_maybe_reserved ':' expr
|
||||||
{ $$ = new Node\Arg($3, false, false, attributes(), $1); }
|
{ $$ = new Node\Arg($3, false, false, attributes(), $1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -601,7 +690,7 @@ non_empty_global_var_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
global_var:
|
global_var:
|
||||||
simple_variable { $$ = Expr\Variable[$1]; }
|
simple_variable { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
static_var_list:
|
static_var_list:
|
||||||
@@ -619,7 +708,7 @@ static_var:
|
|||||||
;
|
;
|
||||||
|
|
||||||
class_statement_list_ex:
|
class_statement_list_ex:
|
||||||
class_statement_list_ex class_statement { if ($2 !== null) { push($1, $2); } }
|
class_statement_list_ex class_statement { if ($2 !== null) { push($1, $2); } else { $$ = $1; } }
|
||||||
| /* empty */ { init(); }
|
| /* empty */ { init(); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -636,10 +725,16 @@ class_statement:
|
|||||||
| optional_attributes method_modifiers T_CONST class_const_list semi
|
| optional_attributes method_modifiers T_CONST class_const_list semi
|
||||||
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
|
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
|
||||||
$this->checkClassConst($$, #2); }
|
$this->checkClassConst($$, #2); }
|
||||||
| optional_attributes method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body
|
| optional_attributes method_modifiers T_CONST type_expr class_const_list semi
|
||||||
|
{ $$ = new Stmt\ClassConst($5, $2, attributes(), $1, $4);
|
||||||
|
$this->checkClassConst($$, #2); }
|
||||||
|
| optional_attributes method_modifiers T_FUNCTION optional_ref identifier_maybe_reserved '(' parameter_list ')'
|
||||||
|
optional_return_type method_body
|
||||||
{ $$ = Stmt\ClassMethod[$5, ['type' => $2, 'byRef' => $4, 'params' => $7, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]];
|
{ $$ = Stmt\ClassMethod[$5, ['type' => $2, 'byRef' => $4, 'params' => $7, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]];
|
||||||
$this->checkClassMethod($$, #2); }
|
$this->checkClassMethod($$, #2); }
|
||||||
| T_USE class_name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; }
|
| T_USE class_name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; }
|
||||||
|
| optional_attributes T_CASE identifier_maybe_reserved enum_case_expr semi
|
||||||
|
{ $$ = Stmt\EnumCase[$3, $4, $1]; }
|
||||||
| error { $$ = null; /* will be skipped */ }
|
| error { $$ = null; /* will be skipped */ }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -656,22 +751,22 @@ trait_adaptation_list:
|
|||||||
trait_adaptation:
|
trait_adaptation:
|
||||||
trait_method_reference_fully_qualified T_INSTEADOF class_name_list ';'
|
trait_method_reference_fully_qualified T_INSTEADOF class_name_list ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; }
|
{ $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; }
|
||||||
| trait_method_reference T_AS member_modifier identifier_ex ';'
|
| trait_method_reference T_AS member_modifier identifier_maybe_reserved ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; }
|
||||||
| trait_method_reference T_AS member_modifier ';'
|
| trait_method_reference T_AS member_modifier ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
|
||||||
| trait_method_reference T_AS identifier ';'
|
| trait_method_reference T_AS identifier_not_reserved ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||||
| trait_method_reference T_AS reserved_non_modifiers_identifier ';'
|
| trait_method_reference T_AS reserved_non_modifiers_identifier ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
trait_method_reference_fully_qualified:
|
trait_method_reference_fully_qualified:
|
||||||
name T_PAAMAYIM_NEKUDOTAYIM identifier_ex { $$ = array($1, $3); }
|
name T_PAAMAYIM_NEKUDOTAYIM identifier_maybe_reserved { $$ = array($1, $3); }
|
||||||
;
|
;
|
||||||
trait_method_reference:
|
trait_method_reference:
|
||||||
trait_method_reference_fully_qualified { $$ = $1; }
|
trait_method_reference_fully_qualified { $$ = $1; }
|
||||||
| identifier_ex { $$ = array(null, $1); }
|
| identifier_maybe_reserved { $$ = array(null, $1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
method_body:
|
method_body:
|
||||||
@@ -701,6 +796,7 @@ member_modifier:
|
|||||||
| T_STATIC { $$ = Stmt\Class_::MODIFIER_STATIC; }
|
| T_STATIC { $$ = Stmt\Class_::MODIFIER_STATIC; }
|
||||||
| T_ABSTRACT { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
| T_ABSTRACT { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
||||||
| T_FINAL { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
| T_FINAL { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
||||||
|
| T_READONLY { $$ = Stmt\Class_::MODIFIER_READONLY; }
|
||||||
;
|
;
|
||||||
|
|
||||||
property_declaration_list:
|
property_declaration_list:
|
||||||
@@ -745,7 +841,7 @@ expr:
|
|||||||
| list_expr '=' expr { $$ = Expr\Assign[$1, $3]; }
|
| list_expr '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||||
| array_short_syntax '=' expr { $$ = Expr\Assign[$1, $3]; }
|
| array_short_syntax '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||||
| variable '=' expr { $$ = Expr\Assign[$1, $3]; }
|
| variable '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||||
| variable '=' '&' variable { $$ = Expr\AssignRef[$1, $4]; }
|
| variable '=' ampersand variable { $$ = Expr\AssignRef[$1, $4]; }
|
||||||
| new_expr { $$ = $1; }
|
| new_expr { $$ = $1; }
|
||||||
| match { $$ = $1; }
|
| match { $$ = $1; }
|
||||||
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
||||||
@@ -772,7 +868,8 @@ expr:
|
|||||||
| expr T_LOGICAL_AND expr { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
|
| expr T_LOGICAL_AND expr { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
|
||||||
| expr T_LOGICAL_XOR expr { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
|
| expr T_LOGICAL_XOR expr { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
|
||||||
| expr '|' expr { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
|
| expr '|' expr { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
|
||||||
| expr '&' expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
| expr T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
||||||
|
| expr T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
||||||
| expr '^' expr { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
|
| expr '^' expr { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
|
||||||
| expr '.' expr { $$ = Expr\BinaryOp\Concat [$1, $3]; }
|
| expr '.' expr { $$ = Expr\BinaryOp\Concat [$1, $3]; }
|
||||||
| expr '+' expr { $$ = Expr\BinaryOp\Plus [$1, $3]; }
|
| expr '+' expr { $$ = Expr\BinaryOp\Plus [$1, $3]; }
|
||||||
@@ -832,18 +929,18 @@ expr:
|
|||||||
| T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; }
|
| T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; }
|
||||||
| T_THROW expr { $$ = Expr\Throw_[$2]; }
|
| T_THROW expr { $$ = Expr\Throw_[$2]; }
|
||||||
|
|
||||||
| T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
|
| T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
|
||||||
{ $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8, 'attrGroups' => []]]; }
|
{ $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8, 'attrGroups' => []]]; }
|
||||||
| T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
|
| T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
|
||||||
{ $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9, 'attrGroups' => []]]; }
|
{ $$ = 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
|
| 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' => []]]; }
|
{ $$ = 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
|
| 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' => []]]; }
|
{ $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => []]]; }
|
||||||
|
|
||||||
| attributes T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
|
| 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]]; }
|
{ $$ = 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
|
| 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]]; }
|
{ $$ = 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
|
| 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]]; }
|
{ $$ = Expr\Closure[['static' => false, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
|
||||||
@@ -852,8 +949,8 @@ expr:
|
|||||||
;
|
;
|
||||||
|
|
||||||
anonymous_class:
|
anonymous_class:
|
||||||
optional_attributes T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
|
optional_attributes class_entry_type ctor_arguments extends_from implements_list '{' class_statement_list '}'
|
||||||
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
|
{ $$ = array(Stmt\Class_[null, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
|
||||||
$this->checkClass($$[0], -1); }
|
$this->checkClass($$[0], -1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -881,8 +978,13 @@ lexical_var:
|
|||||||
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
|
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
name_readonly:
|
||||||
|
T_READONLY { $$ = Name[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
function_call:
|
function_call:
|
||||||
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
|
| name_readonly argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
| callable_expr argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
| callable_expr argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
|
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
|
||||||
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
||||||
@@ -942,8 +1044,10 @@ constant:
|
|||||||
;
|
;
|
||||||
|
|
||||||
class_constant:
|
class_constant:
|
||||||
class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex
|
class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_maybe_reserved
|
||||||
{ $$ = Expr\ClassConstFetch[$1, $3]; }
|
{ $$ = Expr\ClassConstFetch[$1, $3]; }
|
||||||
|
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}'
|
||||||
|
{ $$ = Expr\ClassConstFetch[$1, $4]; }
|
||||||
/* We interpret an isolated FOO:: as an unfinished class constant fetch. It could also be
|
/* We interpret an isolated FOO:: as an unfinished class constant fetch. It could also be
|
||||||
an unfinished static property fetch or unfinished scoped call. */
|
an unfinished static property fetch or unfinished scoped call. */
|
||||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error
|
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error
|
||||||
@@ -961,9 +1065,7 @@ dereferencable_scalar:
|
|||||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
|
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
|
||||||
$$ = new Expr\Array_($3, $attrs); }
|
$$ = new Expr\Array_($3, $attrs); }
|
||||||
| array_short_syntax { $$ = $1; }
|
| array_short_syntax { $$ = $1; }
|
||||||
| T_CONSTANT_ENCAPSED_STRING
|
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_::fromString($1, attributes()); }
|
||||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
|
||||||
$$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); }
|
|
||||||
| '"' encaps_list '"'
|
| '"' encaps_list '"'
|
||||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||||
@@ -971,7 +1073,7 @@ dereferencable_scalar:
|
|||||||
|
|
||||||
scalar:
|
scalar:
|
||||||
T_LNUMBER { $$ = $this->parseLNumber($1, attributes()); }
|
T_LNUMBER { $$ = $this->parseLNumber($1, attributes()); }
|
||||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
| T_DNUMBER { $$ = Scalar\DNumber::fromString($1, attributes()); }
|
||||||
| dereferencable_scalar { $$ = $1; }
|
| dereferencable_scalar { $$ = $1; }
|
||||||
| constant { $$ = $1; }
|
| constant { $$ = $1; }
|
||||||
| class_constant { $$ = $1; }
|
| class_constant { $$ = $1; }
|
||||||
@@ -1007,7 +1109,7 @@ callable_expr:
|
|||||||
;
|
;
|
||||||
|
|
||||||
callable_variable:
|
callable_variable:
|
||||||
simple_variable { $$ = Expr\Variable[$1]; }
|
simple_variable { $$ = $1; }
|
||||||
| array_object_dereferencable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| array_object_dereferencable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| array_object_dereferencable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| array_object_dereferencable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| function_call { $$ = $1; }
|
| function_call { $$ = $1; }
|
||||||
@@ -1032,15 +1134,15 @@ variable:
|
|||||||
;
|
;
|
||||||
|
|
||||||
simple_variable:
|
simple_variable:
|
||||||
T_VARIABLE { $$ = parseVar($1); }
|
plain_variable { $$ = $1; }
|
||||||
| '$' '{' expr '}' { $$ = $3; }
|
| '$' '{' expr '}' { $$ = Expr\Variable[$3]; }
|
||||||
| '$' simple_variable { $$ = Expr\Variable[$2]; }
|
| '$' simple_variable { $$ = Expr\Variable[$2]; }
|
||||||
| '$' error { $$ = Expr\Error[]; $this->errorState = 2; }
|
| '$' error { $$ = Expr\Variable[Expr\Error[]]; $this->errorState = 2; }
|
||||||
;
|
;
|
||||||
|
|
||||||
static_member_prop_name:
|
static_member_prop_name:
|
||||||
simple_variable
|
simple_variable
|
||||||
{ $var = $1; $$ = \is_string($var) ? Node\VarLikeIdentifier[$var] : $var; }
|
{ $var = $1->name; $$ = \is_string($var) ? Node\VarLikeIdentifier[$var] : $var; }
|
||||||
;
|
;
|
||||||
|
|
||||||
static_member:
|
static_member:
|
||||||
@@ -1049,7 +1151,7 @@ static_member:
|
|||||||
;
|
;
|
||||||
|
|
||||||
new_variable:
|
new_variable:
|
||||||
simple_variable { $$ = Expr\Variable[$1]; }
|
simple_variable { $$ = $1; }
|
||||||
| new_variable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| new_variable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| new_variable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| new_variable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| new_variable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; }
|
| new_variable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; }
|
||||||
@@ -1061,15 +1163,15 @@ new_variable:
|
|||||||
;
|
;
|
||||||
|
|
||||||
member_name:
|
member_name:
|
||||||
identifier_ex { $$ = $1; }
|
identifier_maybe_reserved { $$ = $1; }
|
||||||
| '{' expr '}' { $$ = $2; }
|
| '{' expr '}' { $$ = $2; }
|
||||||
| simple_variable { $$ = Expr\Variable[$1]; }
|
| simple_variable { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
property_name:
|
property_name:
|
||||||
identifier { $$ = $1; }
|
identifier_not_reserved { $$ = $1; }
|
||||||
| '{' expr '}' { $$ = $2; }
|
| '{' expr '}' { $$ = $2; }
|
||||||
| simple_variable { $$ = Expr\Variable[$1]; }
|
| simple_variable { $$ = $1; }
|
||||||
| error { $$ = Expr\Error[]; $this->errorState = 2; }
|
| error { $$ = Expr\Error[]; $this->errorState = 2; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -1095,12 +1197,12 @@ inner_array_pair_list:
|
|||||||
|
|
||||||
array_pair:
|
array_pair:
|
||||||
expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||||
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
|
| ampersand variable { $$ = Expr\ArrayItem[$2, null, true]; }
|
||||||
| list_expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
| list_expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||||
| expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
| expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||||
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
|
| expr T_DOUBLE_ARROW ampersand variable { $$ = Expr\ArrayItem[$4, $1, true]; }
|
||||||
| expr T_DOUBLE_ARROW list_expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
| expr T_DOUBLE_ARROW list_expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||||
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
|
| T_ELLIPSIS expr { $$ = new Expr\ArrayItem($2, null, false, attributes(), true); }
|
||||||
| /* empty */ { $$ = null; }
|
| /* empty */ { $$ = null; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -1122,8 +1224,10 @@ encaps_str_varname:
|
|||||||
encaps_var:
|
encaps_var:
|
||||||
plain_variable { $$ = $1; }
|
plain_variable { $$ = $1; }
|
||||||
| plain_variable '[' encaps_var_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| plain_variable '[' encaps_var_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| plain_variable T_OBJECT_OPERATOR identifier { $$ = Expr\PropertyFetch[$1, $3]; }
|
| plain_variable T_OBJECT_OPERATOR identifier_not_reserved
|
||||||
| plain_variable T_NULLSAFE_OBJECT_OPERATOR identifier { $$ = Expr\NullsafePropertyFetch[$1, $3]; }
|
{ $$ = Expr\PropertyFetch[$1, $3]; }
|
||||||
|
| plain_variable T_NULLSAFE_OBJECT_OPERATOR identifier_not_reserved
|
||||||
|
{ $$ = Expr\NullsafePropertyFetch[$1, $3]; }
|
||||||
| T_DOLLAR_OPEN_CURLY_BRACES expr '}' { $$ = Expr\Variable[$2]; }
|
| T_DOLLAR_OPEN_CURLY_BRACES expr '}' { $$ = Expr\Variable[$2]; }
|
||||||
| T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}' { $$ = Expr\Variable[$2]; }
|
| T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}' { $$ = Expr\Variable[$2]; }
|
||||||
| T_DOLLAR_OPEN_CURLY_BRACES encaps_str_varname '[' expr ']' '}'
|
| T_DOLLAR_OPEN_CURLY_BRACES encaps_str_varname '[' expr ']' '}'
|
||||||
|
184
grammar/phpyLang.php
Normal file
184
grammar/phpyLang.php
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<?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 ('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
|
<?php
|
||||||
|
|
||||||
|
require __DIR__ . '/phpyLang.php';
|
||||||
|
|
||||||
$grammarFileToName = [
|
$grammarFileToName = [
|
||||||
__DIR__ . '/php5.y' => 'Php5',
|
__DIR__ . '/php5.y' => 'Php5',
|
||||||
__DIR__ . '/php7.y' => 'Php7',
|
__DIR__ . '/php7.y' => 'Php7',
|
||||||
@@ -23,21 +25,6 @@ $options = array_flip($argv);
|
|||||||
$optionDebug = isset($options['--debug']);
|
$optionDebug = isset($options['--debug']);
|
||||||
$optionKeepTmpGrammar = isset($options['--keep-tmp-grammar']);
|
$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 ///
|
/// Main script ///
|
||||||
///////////////////
|
///////////////////
|
||||||
@@ -49,10 +36,7 @@ foreach ($grammarFileToName as $grammarFile => $name) {
|
|||||||
|
|
||||||
$grammarCode = file_get_contents($grammarFile);
|
$grammarCode = file_get_contents($grammarFile);
|
||||||
$grammarCode = str_replace('%tokens', $tokens, $grammarCode);
|
$grammarCode = str_replace('%tokens', $tokens, $grammarCode);
|
||||||
|
$grammarCode = preprocessGrammar($grammarCode);
|
||||||
$grammarCode = resolveNodes($grammarCode);
|
|
||||||
$grammarCode = resolveMacros($grammarCode);
|
|
||||||
$grammarCode = resolveStackAccess($grammarCode);
|
|
||||||
|
|
||||||
file_put_contents($tmpGrammarFile, $grammarCode);
|
file_put_contents($tmpGrammarFile, $grammarCode);
|
||||||
|
|
||||||
@@ -77,151 +61,9 @@ foreach ($grammarFileToName as $grammarFile => $name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////
|
////////////////////////////////
|
||||||
/// Preprocessing functions ///
|
/// Utility helper 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureDirExists($dir) {
|
function ensureDirExists($dir) {
|
||||||
if (!is_dir($dir)) {
|
if (!is_dir($dir)) {
|
||||||
@@ -237,25 +79,3 @@ function execCmd($cmd) {
|
|||||||
}
|
}
|
||||||
return $output;
|
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;
|
|
||||||
}
|
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
%left T_BOOLEAN_AND
|
%left T_BOOLEAN_AND
|
||||||
%left '|'
|
%left '|'
|
||||||
%left '^'
|
%left '^'
|
||||||
%left '&'
|
%left T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
|
||||||
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
|
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
|
||||||
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
|
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
|
||||||
%left T_SL T_SR
|
%left T_SL T_SR
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
%token T_USE
|
%token T_USE
|
||||||
%token T_INSTEADOF
|
%token T_INSTEADOF
|
||||||
%token T_GLOBAL
|
%token T_GLOBAL
|
||||||
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
|
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
|
||||||
%token T_VAR
|
%token T_VAR
|
||||||
%token T_UNSET
|
%token T_UNSET
|
||||||
%token T_ISSET
|
%token T_ISSET
|
||||||
@@ -83,6 +83,7 @@
|
|||||||
%token T_CLASS
|
%token T_CLASS
|
||||||
%token T_TRAIT
|
%token T_TRAIT
|
||||||
%token T_INTERFACE
|
%token T_INTERFACE
|
||||||
|
%token T_ENUM
|
||||||
%token T_EXTENDS
|
%token T_EXTENDS
|
||||||
%token T_IMPLEMENTS
|
%token T_IMPLEMENTS
|
||||||
%token T_OBJECT_OPERATOR
|
%token T_OBJECT_OPERATOR
|
||||||
@@ -111,3 +112,4 @@
|
|||||||
%token T_NAME_QUALIFIED
|
%token T_NAME_QUALIFIED
|
||||||
%token T_NAME_RELATIVE
|
%token T_NAME_RELATIVE
|
||||||
%token T_ATTRIBUTE
|
%token T_ATTRIBUTE
|
||||||
|
%token T_ENUM
|
||||||
|
148
lib/PhpParser/Builder/ClassConst.php
Normal file
148
lib/PhpParser/Builder/ClassConst.php
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Builder;
|
||||||
|
|
||||||
|
use PhpParser;
|
||||||
|
use PhpParser\BuilderHelpers;
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Const_;
|
||||||
|
use PhpParser\Node\Identifier;
|
||||||
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
|
class ClassConst implements PhpParser\Builder
|
||||||
|
{
|
||||||
|
protected $flags = 0;
|
||||||
|
protected $attributes = [];
|
||||||
|
protected $constants = [];
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
/** @var Identifier|Node\Name|Node\ComplexType */
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a class constant builder
|
||||||
|
*
|
||||||
|
* @param string|Identifier $name Name
|
||||||
|
* @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $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|\UnitEnum $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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the constant final.
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function makeFinal() {
|
||||||
|
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the constant type.
|
||||||
|
*
|
||||||
|
* @param string|Node\Name|Identifier|Node\ComplexType $type
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setType($type) {
|
||||||
|
$this->type = BuilderHelpers::normalizeType($type);
|
||||||
|
|
||||||
|
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,
|
||||||
|
$this->attributeGroups,
|
||||||
|
$this->type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,7 @@ namespace PhpParser\Builder;
|
|||||||
|
|
||||||
use PhpParser;
|
use PhpParser;
|
||||||
use PhpParser\BuilderHelpers;
|
use PhpParser\BuilderHelpers;
|
||||||
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Name;
|
use PhpParser\Node\Name;
|
||||||
use PhpParser\Node\Stmt;
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
@@ -20,6 +21,9 @@ class Class_ extends Declaration
|
|||||||
protected $properties = [];
|
protected $properties = [];
|
||||||
protected $methods = [];
|
protected $methods = [];
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a class builder.
|
* Creates a class builder.
|
||||||
*
|
*
|
||||||
@@ -63,7 +67,7 @@ class Class_ extends Declaration
|
|||||||
* @return $this The builder instance (for fluid interface)
|
* @return $this The builder instance (for fluid interface)
|
||||||
*/
|
*/
|
||||||
public function makeAbstract() {
|
public function makeAbstract() {
|
||||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT);
|
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -74,7 +78,13 @@ class Class_ extends Declaration
|
|||||||
* @return $this The builder instance (for fluid interface)
|
* @return $this The builder instance (for fluid interface)
|
||||||
*/
|
*/
|
||||||
public function makeFinal() {
|
public function makeFinal() {
|
||||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeReadonly() {
|
||||||
|
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_READONLY);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -106,6 +116,19 @@ class Class_ extends Declaration
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the built class node.
|
* Returns the built class node.
|
||||||
*
|
*
|
||||||
@@ -117,6 +140,7 @@ class Class_ extends Declaration
|
|||||||
'extends' => $this->extends,
|
'extends' => $this->extends,
|
||||||
'implements' => $this->implements,
|
'implements' => $this->implements,
|
||||||
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
|
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
|
||||||
|
'attrGroups' => $this->attributeGroups,
|
||||||
], $this->attributes);
|
], $this->attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
85
lib/PhpParser/Builder/EnumCase.php
Normal file
85
lib/PhpParser/Builder/EnumCase.php
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Builder;
|
||||||
|
|
||||||
|
use PhpParser;
|
||||||
|
use PhpParser\BuilderHelpers;
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Identifier;
|
||||||
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
|
class EnumCase implements PhpParser\Builder
|
||||||
|
{
|
||||||
|
protected $name;
|
||||||
|
protected $value = null;
|
||||||
|
protected $attributes = [];
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an enum case builder.
|
||||||
|
*
|
||||||
|
* @param string|Identifier $name Name
|
||||||
|
*/
|
||||||
|
public function __construct($name) {
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value.
|
||||||
|
*
|
||||||
|
* @param Node\Expr|string|int $value
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setValue($value) {
|
||||||
|
$this->value = BuilderHelpers::normalizeValue($value);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the built enum case node.
|
||||||
|
*
|
||||||
|
* @return Stmt\EnumCase The built constant node
|
||||||
|
*/
|
||||||
|
public function getNode(): PhpParser\Node {
|
||||||
|
return new Stmt\EnumCase(
|
||||||
|
$this->name,
|
||||||
|
$this->value,
|
||||||
|
$this->attributeGroups,
|
||||||
|
$this->attributes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
117
lib/PhpParser/Builder/Enum_.php
Normal file
117
lib/PhpParser/Builder/Enum_.php
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Builder;
|
||||||
|
|
||||||
|
use PhpParser;
|
||||||
|
use PhpParser\BuilderHelpers;
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Identifier;
|
||||||
|
use PhpParser\Node\Name;
|
||||||
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
|
class Enum_ extends Declaration
|
||||||
|
{
|
||||||
|
protected $name;
|
||||||
|
protected $scalarType = null;
|
||||||
|
|
||||||
|
protected $implements = [];
|
||||||
|
|
||||||
|
protected $uses = [];
|
||||||
|
protected $enumCases = [];
|
||||||
|
protected $constants = [];
|
||||||
|
protected $methods = [];
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an enum builder.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the enum
|
||||||
|
*/
|
||||||
|
public function __construct(string $name) {
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scalar type.
|
||||||
|
*
|
||||||
|
* @param string|Identifier $type
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setScalarType($scalarType) {
|
||||||
|
$this->scalarType = BuilderHelpers::normalizeType($scalarType);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements one or more interfaces.
|
||||||
|
*
|
||||||
|
* @param Name|string ...$interfaces Names of interfaces to implement
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function implement(...$interfaces) {
|
||||||
|
foreach ($interfaces as $interface) {
|
||||||
|
$this->implements[] = BuilderHelpers::normalizeName($interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a statement.
|
||||||
|
*
|
||||||
|
* @param Stmt|PhpParser\Builder $stmt The statement to add
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addStmt($stmt) {
|
||||||
|
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||||
|
|
||||||
|
$targets = [
|
||||||
|
Stmt\TraitUse::class => &$this->uses,
|
||||||
|
Stmt\EnumCase::class => &$this->enumCases,
|
||||||
|
Stmt\ClassConst::class => &$this->constants,
|
||||||
|
Stmt\ClassMethod::class => &$this->methods,
|
||||||
|
];
|
||||||
|
|
||||||
|
$class = \get_class($stmt);
|
||||||
|
if (!isset($targets[$class])) {
|
||||||
|
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$targets[$class][] = $stmt;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the built class node.
|
||||||
|
*
|
||||||
|
* @return Stmt\Enum_ The built enum node
|
||||||
|
*/
|
||||||
|
public function getNode() : PhpParser\Node {
|
||||||
|
return new Stmt\Enum_($this->name, [
|
||||||
|
'scalarType' => $this->scalarType,
|
||||||
|
'implements' => $this->implements,
|
||||||
|
'stmts' => array_merge($this->uses, $this->enumCases, $this->constants, $this->methods),
|
||||||
|
'attrGroups' => $this->attributeGroups,
|
||||||
|
], $this->attributes);
|
||||||
|
}
|
||||||
|
}
|
@@ -61,8 +61,7 @@ abstract class FunctionLike extends Declaration
|
|||||||
/**
|
/**
|
||||||
* Sets the return type for PHP 7.
|
* Sets the return type for PHP 7.
|
||||||
*
|
*
|
||||||
* @param string|Node\Name|Node\NullableType $type One of array, callable, string, int, float,
|
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type
|
||||||
* bool, iterable, or a class/interface name.
|
|
||||||
*
|
*
|
||||||
* @return $this The builder instance (for fluid interface)
|
* @return $this The builder instance (for fluid interface)
|
||||||
*/
|
*/
|
||||||
|
@@ -12,6 +12,9 @@ class Function_ extends FunctionLike
|
|||||||
protected $name;
|
protected $name;
|
||||||
protected $stmts = [];
|
protected $stmts = [];
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a function builder.
|
* Creates a function builder.
|
||||||
*
|
*
|
||||||
@@ -34,6 +37,19 @@ class Function_ extends FunctionLike
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the built function node.
|
* Returns the built function node.
|
||||||
*
|
*
|
||||||
@@ -45,6 +61,7 @@ class Function_ extends FunctionLike
|
|||||||
'params' => $this->params,
|
'params' => $this->params,
|
||||||
'returnType' => $this->returnType,
|
'returnType' => $this->returnType,
|
||||||
'stmts' => $this->stmts,
|
'stmts' => $this->stmts,
|
||||||
|
'attrGroups' => $this->attributeGroups,
|
||||||
], $this->attributes);
|
], $this->attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ namespace PhpParser\Builder;
|
|||||||
|
|
||||||
use PhpParser;
|
use PhpParser;
|
||||||
use PhpParser\BuilderHelpers;
|
use PhpParser\BuilderHelpers;
|
||||||
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Name;
|
use PhpParser\Node\Name;
|
||||||
use PhpParser\Node\Stmt;
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
@@ -14,6 +15,9 @@ class Interface_ extends Declaration
|
|||||||
protected $constants = [];
|
protected $constants = [];
|
||||||
protected $methods = [];
|
protected $methods = [];
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an interface builder.
|
* Creates an interface builder.
|
||||||
*
|
*
|
||||||
@@ -61,6 +65,19 @@ class Interface_ extends Declaration
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the built interface node.
|
* Returns the built interface node.
|
||||||
*
|
*
|
||||||
@@ -70,6 +87,7 @@ class Interface_ extends Declaration
|
|||||||
return new Stmt\Interface_($this->name, [
|
return new Stmt\Interface_($this->name, [
|
||||||
'extends' => $this->extends,
|
'extends' => $this->extends,
|
||||||
'stmts' => array_merge($this->constants, $this->methods),
|
'stmts' => array_merge($this->constants, $this->methods),
|
||||||
|
'attrGroups' => $this->attributeGroups,
|
||||||
], $this->attributes);
|
], $this->attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,9 @@ class Method extends FunctionLike
|
|||||||
/** @var array|null */
|
/** @var array|null */
|
||||||
protected $stmts = [];
|
protected $stmts = [];
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a method builder.
|
* Creates a method builder.
|
||||||
*
|
*
|
||||||
@@ -112,6 +115,19 @@ class Method extends FunctionLike
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the built method node.
|
* Returns the built method node.
|
||||||
*
|
*
|
||||||
@@ -124,6 +140,7 @@ class Method extends FunctionLike
|
|||||||
'params' => $this->params,
|
'params' => $this->params,
|
||||||
'returnType' => $this->returnType,
|
'returnType' => $this->returnType,
|
||||||
'stmts' => $this->stmts,
|
'stmts' => $this->stmts,
|
||||||
|
'attrGroups' => $this->attributeGroups,
|
||||||
], $this->attributes);
|
], $this->attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,7 @@ class Namespace_ extends Declaration
|
|||||||
/**
|
/**
|
||||||
* Returns the built node.
|
* Returns the built node.
|
||||||
*
|
*
|
||||||
* @return Node The built node
|
* @return Stmt\Namespace_ The built node
|
||||||
*/
|
*/
|
||||||
public function getNode() : Node {
|
public function getNode() : Node {
|
||||||
return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
|
return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
|
||||||
|
@@ -19,6 +19,11 @@ class Param implements PhpParser\Builder
|
|||||||
|
|
||||||
protected $variadic = false;
|
protected $variadic = false;
|
||||||
|
|
||||||
|
protected $flags = 0;
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a parameter builder.
|
* Creates a parameter builder.
|
||||||
*
|
*
|
||||||
@@ -44,7 +49,7 @@ class Param implements PhpParser\Builder
|
|||||||
/**
|
/**
|
||||||
* Sets type for the parameter.
|
* Sets type for the parameter.
|
||||||
*
|
*
|
||||||
* @param string|Node\Name|Node\NullableType|Node\UnionType $type Parameter type
|
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type
|
||||||
*
|
*
|
||||||
* @return $this The builder instance (for fluid interface)
|
* @return $this The builder instance (for fluid interface)
|
||||||
*/
|
*/
|
||||||
@@ -60,7 +65,7 @@ class Param implements PhpParser\Builder
|
|||||||
/**
|
/**
|
||||||
* Sets type for the parameter.
|
* Sets type for the parameter.
|
||||||
*
|
*
|
||||||
* @param string|Node\Name|Node\NullableType|Node\UnionType $type Parameter type
|
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type
|
||||||
*
|
*
|
||||||
* @return $this The builder instance (for fluid interface)
|
* @return $this The builder instance (for fluid interface)
|
||||||
*
|
*
|
||||||
@@ -92,6 +97,63 @@ class Param implements PhpParser\Builder
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the (promoted) parameter public.
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function makePublic() {
|
||||||
|
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the (promoted) parameter protected.
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function makeProtected() {
|
||||||
|
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the (promoted) parameter private.
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function makePrivate() {
|
||||||
|
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the (promoted) parameter readonly.
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function makeReadonly() {
|
||||||
|
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the built parameter node.
|
* Returns the built parameter node.
|
||||||
*
|
*
|
||||||
@@ -100,7 +162,7 @@ class Param implements PhpParser\Builder
|
|||||||
public function getNode() : Node {
|
public function getNode() : Node {
|
||||||
return new Node\Param(
|
return new Node\Param(
|
||||||
new Node\Expr\Variable($this->name),
|
new Node\Expr\Variable($this->name),
|
||||||
$this->default, $this->type, $this->byRef, $this->variadic
|
$this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,10 +4,11 @@ namespace PhpParser\Builder;
|
|||||||
|
|
||||||
use PhpParser;
|
use PhpParser;
|
||||||
use PhpParser\BuilderHelpers;
|
use PhpParser\BuilderHelpers;
|
||||||
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Identifier;
|
use PhpParser\Node\Identifier;
|
||||||
use PhpParser\Node\Name;
|
use PhpParser\Node\Name;
|
||||||
use PhpParser\Node\NullableType;
|
|
||||||
use PhpParser\Node\Stmt;
|
use PhpParser\Node\Stmt;
|
||||||
|
use PhpParser\Node\ComplexType;
|
||||||
|
|
||||||
class Property implements PhpParser\Builder
|
class Property implements PhpParser\Builder
|
||||||
{
|
{
|
||||||
@@ -20,6 +21,9 @@ class Property implements PhpParser\Builder
|
|||||||
/** @var null|Identifier|Name|NullableType */
|
/** @var null|Identifier|Name|NullableType */
|
||||||
protected $type;
|
protected $type;
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a property builder.
|
* Creates a property builder.
|
||||||
*
|
*
|
||||||
@@ -73,6 +77,17 @@ class Property implements PhpParser\Builder
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the property readonly.
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function makeReadonly() {
|
||||||
|
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets default value for the property.
|
* Sets default value for the property.
|
||||||
*
|
*
|
||||||
@@ -104,7 +119,7 @@ class Property implements PhpParser\Builder
|
|||||||
/**
|
/**
|
||||||
* Sets the property type for PHP 7.4+.
|
* Sets the property type for PHP 7.4+.
|
||||||
*
|
*
|
||||||
* @param string|Name|NullableType|Identifier $type
|
* @param string|Name|Identifier|ComplexType $type
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
@@ -114,6 +129,19 @@ class Property implements PhpParser\Builder
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the built class node.
|
* Returns the built class node.
|
||||||
*
|
*
|
||||||
@@ -126,7 +154,8 @@ class Property implements PhpParser\Builder
|
|||||||
new Stmt\PropertyProperty($this->name, $this->default)
|
new Stmt\PropertyProperty($this->name, $this->default)
|
||||||
],
|
],
|
||||||
$this->attributes,
|
$this->attributes,
|
||||||
$this->type
|
$this->type,
|
||||||
|
$this->attributeGroups
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ namespace PhpParser\Builder;
|
|||||||
|
|
||||||
use PhpParser;
|
use PhpParser;
|
||||||
use PhpParser\BuilderHelpers;
|
use PhpParser\BuilderHelpers;
|
||||||
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Stmt;
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
class Trait_ extends Declaration
|
class Trait_ extends Declaration
|
||||||
@@ -13,6 +14,9 @@ class Trait_ extends Declaration
|
|||||||
protected $properties = [];
|
protected $properties = [];
|
||||||
protected $methods = [];
|
protected $methods = [];
|
||||||
|
|
||||||
|
/** @var Node\AttributeGroup[] */
|
||||||
|
protected $attributeGroups = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an interface builder.
|
* Creates an interface builder.
|
||||||
*
|
*
|
||||||
@@ -45,6 +49,19 @@ class Trait_ extends Declaration
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute group.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return $this The builder instance (for fluid interface)
|
||||||
|
*/
|
||||||
|
public function addAttribute($attribute) {
|
||||||
|
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the built trait node.
|
* Returns the built trait node.
|
||||||
*
|
*
|
||||||
@@ -53,7 +70,8 @@ class Trait_ extends Declaration
|
|||||||
public function getNode() : PhpParser\Node {
|
public function getNode() : PhpParser\Node {
|
||||||
return new Stmt\Trait_(
|
return new Stmt\Trait_(
|
||||||
$this->name, [
|
$this->name, [
|
||||||
'stmts' => array_merge($this->uses, $this->properties, $this->methods)
|
'stmts' => array_merge($this->uses, $this->properties, $this->methods),
|
||||||
|
'attrGroups' => $this->attributeGroups,
|
||||||
], $this->attributes
|
], $this->attributes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,7 @@ class Use_ implements Builder
|
|||||||
/**
|
/**
|
||||||
* Returns the built node.
|
* Returns the built node.
|
||||||
*
|
*
|
||||||
* @return Node The built node
|
* @return Stmt\Use_ The built node
|
||||||
*/
|
*/
|
||||||
public function getNode() : Node {
|
public function getNode() : Node {
|
||||||
return new Stmt\Use_([
|
return new Stmt\Use_([
|
||||||
|
@@ -12,6 +12,21 @@ use PhpParser\Node\Stmt\Use_;
|
|||||||
|
|
||||||
class BuilderFactory
|
class BuilderFactory
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Creates an attribute node.
|
||||||
|
*
|
||||||
|
* @param string|Name $name Name of the attribute
|
||||||
|
* @param array $args Attribute named arguments
|
||||||
|
*
|
||||||
|
* @return Node\Attribute
|
||||||
|
*/
|
||||||
|
public function attribute($name, array $args = []) : Node\Attribute {
|
||||||
|
return new Node\Attribute(
|
||||||
|
BuilderHelpers::normalizeName($name),
|
||||||
|
$this->args($args)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a namespace builder.
|
* Creates a namespace builder.
|
||||||
*
|
*
|
||||||
@@ -56,6 +71,17 @@ class BuilderFactory
|
|||||||
return new Builder\Trait_($name);
|
return new Builder\Trait_($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an enum builder.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the enum
|
||||||
|
*
|
||||||
|
* @return Builder\Enum_ The created enum builder
|
||||||
|
*/
|
||||||
|
public function enum(string $name) : Builder\Enum_ {
|
||||||
|
return new Builder\Enum_($name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a trait use builder.
|
* Creates a trait use builder.
|
||||||
*
|
*
|
||||||
@@ -161,10 +187,33 @@ class BuilderFactory
|
|||||||
return new Builder\Use_($name, Use_::TYPE_CONSTANT);
|
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 an enum case builder.
|
||||||
|
*
|
||||||
|
* @param string|Identifier $name Name
|
||||||
|
*
|
||||||
|
* @return Builder\EnumCase The created use const builder
|
||||||
|
*/
|
||||||
|
public function enumCase($name) : Builder\EnumCase {
|
||||||
|
return new Builder\EnumCase($name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates node a for a literal value.
|
* Creates node a for a literal value.
|
||||||
*
|
*
|
||||||
* @param Expr|bool|null|int|float|string|array $value $value
|
* @param Expr|bool|null|int|float|string|array|\UnitEnum $value $value
|
||||||
*
|
*
|
||||||
* @return Expr
|
* @return Expr
|
||||||
*/
|
*/
|
||||||
@@ -198,12 +247,14 @@ class BuilderFactory
|
|||||||
*/
|
*/
|
||||||
public function args(array $args) : array {
|
public function args(array $args) : array {
|
||||||
$normalizedArgs = [];
|
$normalizedArgs = [];
|
||||||
foreach ($args as $arg) {
|
foreach ($args as $key => $arg) {
|
||||||
if ($arg instanceof Arg) {
|
if (!($arg instanceof Arg)) {
|
||||||
$normalizedArgs[] = $arg;
|
$arg = new Arg(BuilderHelpers::normalizeValue($arg));
|
||||||
} else {
|
|
||||||
$normalizedArgs[] = new Arg(BuilderHelpers::normalizeValue($arg));
|
|
||||||
}
|
}
|
||||||
|
if (\is_string($key)) {
|
||||||
|
$arg->name = BuilderHelpers::normalizeIdentifier($key);
|
||||||
|
}
|
||||||
|
$normalizedArgs[] = $arg;
|
||||||
}
|
}
|
||||||
return $normalizedArgs;
|
return $normalizedArgs;
|
||||||
}
|
}
|
||||||
@@ -299,14 +350,14 @@ class BuilderFactory
|
|||||||
* Creates a class constant fetch node.
|
* Creates a class constant fetch node.
|
||||||
*
|
*
|
||||||
* @param string|Name|Expr $class Class name
|
* @param string|Name|Expr $class Class name
|
||||||
* @param string|Identifier $name Constant name
|
* @param string|Identifier|Expr $name Constant name
|
||||||
*
|
*
|
||||||
* @return Expr\ClassConstFetch
|
* @return Expr\ClassConstFetch
|
||||||
*/
|
*/
|
||||||
public function classConstFetch($class, $name): Expr\ClassConstFetch {
|
public function classConstFetch($class, $name): Expr\ClassConstFetch {
|
||||||
return new Expr\ClassConstFetch(
|
return new Expr\ClassConstFetch(
|
||||||
BuilderHelpers::normalizeNameOrExpr($class),
|
BuilderHelpers::normalizeNameOrExpr($class),
|
||||||
BuilderHelpers::normalizeIdentifier($name)
|
BuilderHelpers::normalizeIdentifierOrExpr($name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace PhpParser;
|
namespace PhpParser;
|
||||||
|
|
||||||
|
use PhpParser\Node\ComplexType;
|
||||||
use PhpParser\Node\Expr;
|
use PhpParser\Node\Expr;
|
||||||
use PhpParser\Node\Identifier;
|
use PhpParser\Node\Identifier;
|
||||||
use PhpParser\Node\Name;
|
use PhpParser\Node\Name;
|
||||||
|
use PhpParser\Node\Name\FullyQualified;
|
||||||
use PhpParser\Node\NullableType;
|
use PhpParser\Node\NullableType;
|
||||||
use PhpParser\Node\Scalar;
|
use PhpParser\Node\Scalar;
|
||||||
use PhpParser\Node\Stmt;
|
use PhpParser\Node\Stmt;
|
||||||
use PhpParser\Node\UnionType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class defines helpers used in the implementation of builders. Don't use it directly.
|
* This class defines helpers used in the implementation of builders. Don't use it directly.
|
||||||
@@ -27,7 +28,9 @@ final class BuilderHelpers
|
|||||||
public static function normalizeNode($node) : Node {
|
public static function normalizeNode($node) : Node {
|
||||||
if ($node instanceof Builder) {
|
if ($node instanceof Builder) {
|
||||||
return $node->getNode();
|
return $node->getNode();
|
||||||
} elseif ($node instanceof Node) {
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node) {
|
||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +105,27 @@ final class BuilderHelpers
|
|||||||
* @return Name The normalized name
|
* @return Name The normalized name
|
||||||
*/
|
*/
|
||||||
public static function normalizeName($name) : Name {
|
public static function normalizeName($name) : Name {
|
||||||
return self::normalizeNameCommon($name, false);
|
if ($name instanceof Name) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($name)) {
|
||||||
|
if (!$name) {
|
||||||
|
throw new \LogicException('Name cannot be empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($name[0] === '\\') {
|
||||||
|
return new Name\FullyQualified(substr($name, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === strpos($name, 'namespace\\')) {
|
||||||
|
return new Name\Relative(substr($name, strlen('namespace\\')));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Name($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \LogicException('Name must be a string or an instance of Node\Name');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,44 +136,17 @@ final class BuilderHelpers
|
|||||||
* @return Name|Expr The normalized name or expression
|
* @return Name|Expr The normalized name or expression
|
||||||
*/
|
*/
|
||||||
public static function normalizeNameOrExpr($name) {
|
public static function normalizeNameOrExpr($name) {
|
||||||
return self::normalizeNameCommon($name, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalizes a name: Converts string names to Name nodes, optionally allowing expressions.
|
|
||||||
*
|
|
||||||
* @param Expr|Name|string $name The name to normalize
|
|
||||||
* @param bool $allowExpr Whether to also allow expressions
|
|
||||||
*
|
|
||||||
* @return Name|Expr The normalized name, or expression (if allowed)
|
|
||||||
*/
|
|
||||||
private static function normalizeNameCommon($name, bool $allowExpr) {
|
|
||||||
if ($name instanceof Name) {
|
|
||||||
return $name;
|
|
||||||
} elseif (is_string($name)) {
|
|
||||||
if (!$name) {
|
|
||||||
throw new \LogicException('Name cannot be empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($name[0] === '\\') {
|
|
||||||
return new Name\FullyQualified(substr($name, 1));
|
|
||||||
} elseif (0 === strpos($name, 'namespace\\')) {
|
|
||||||
return new Name\Relative(substr($name, strlen('namespace\\')));
|
|
||||||
} else {
|
|
||||||
return new Name($name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($allowExpr) {
|
|
||||||
if ($name instanceof Expr) {
|
if ($name instanceof Expr) {
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_string($name) && !($name instanceof Name)) {
|
||||||
throw new \LogicException(
|
throw new \LogicException(
|
||||||
'Name must be a string or an instance of Node\Name or Node\Expr'
|
'Name must be a string or an instance of Node\Name or Node\Expr'
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
throw new \LogicException('Name must be a string or an instance of Node\Name');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return self::normalizeName($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,18 +155,18 @@ final class BuilderHelpers
|
|||||||
* In particular, builtin types become Identifiers, custom types become Names and nullables
|
* In particular, builtin types become Identifiers, custom types become Names and nullables
|
||||||
* are wrapped in NullableType nodes.
|
* are wrapped in NullableType nodes.
|
||||||
*
|
*
|
||||||
* @param string|Name|Identifier|NullableType|UnionType $type The type to normalize
|
* @param string|Name|Identifier|ComplexType $type The type to normalize
|
||||||
*
|
*
|
||||||
* @return Name|Identifier|NullableType|UnionType The normalized type
|
* @return Name|Identifier|ComplexType The normalized type
|
||||||
*/
|
*/
|
||||||
public static function normalizeType($type) {
|
public static function normalizeType($type) {
|
||||||
if (!is_string($type)) {
|
if (!is_string($type)) {
|
||||||
if (
|
if (
|
||||||
!$type instanceof Name && !$type instanceof Identifier &&
|
!$type instanceof Name && !$type instanceof Identifier &&
|
||||||
!$type instanceof NullableType && !$type instanceof UnionType
|
!$type instanceof ComplexType
|
||||||
) {
|
) {
|
||||||
throw new \LogicException(
|
throw new \LogicException(
|
||||||
'Type must be a string, or an instance of Name, Identifier, NullableType or UnionType'
|
'Type must be a string, or an instance of Name, Identifier or ComplexType'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return $type;
|
return $type;
|
||||||
@@ -183,7 +179,20 @@ final class BuilderHelpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
$builtinTypes = [
|
$builtinTypes = [
|
||||||
'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object', 'mixed'
|
'array',
|
||||||
|
'callable',
|
||||||
|
'bool',
|
||||||
|
'int',
|
||||||
|
'float',
|
||||||
|
'string',
|
||||||
|
'iterable',
|
||||||
|
'void',
|
||||||
|
'object',
|
||||||
|
'null',
|
||||||
|
'false',
|
||||||
|
'mixed',
|
||||||
|
'never',
|
||||||
|
'true',
|
||||||
];
|
];
|
||||||
|
|
||||||
$lowerType = strtolower($type);
|
$lowerType = strtolower($type);
|
||||||
@@ -193,12 +202,11 @@ final class BuilderHelpers
|
|||||||
$type = self::normalizeName($type);
|
$type = self::normalizeName($type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($nullable && (string) $type === 'void') {
|
$notNullableTypes = [
|
||||||
throw new \LogicException('void type cannot be nullable');
|
'void', 'mixed', 'never',
|
||||||
}
|
];
|
||||||
|
if ($nullable && in_array((string) $type, $notNullableTypes)) {
|
||||||
if ($nullable && (string) $type === 'mixed') {
|
throw new \LogicException(sprintf('%s type cannot be nullable', $type));
|
||||||
throw new \LogicException('mixed type cannot be nullable');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $nullable ? new NullableType($type) : $type;
|
return $nullable ? new NullableType($type) : $type;
|
||||||
@@ -208,28 +216,40 @@ final class BuilderHelpers
|
|||||||
* Normalizes a value: Converts nulls, booleans, integers,
|
* Normalizes a value: Converts nulls, booleans, integers,
|
||||||
* floats, strings and arrays into their respective nodes
|
* floats, strings and arrays into their respective nodes
|
||||||
*
|
*
|
||||||
* @param Node\Expr|bool|null|int|float|string|array $value The value to normalize
|
* @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value The value to normalize
|
||||||
*
|
*
|
||||||
* @return Expr The normalized value
|
* @return Expr The normalized value
|
||||||
*/
|
*/
|
||||||
public static function normalizeValue($value) : Expr {
|
public static function normalizeValue($value) : Expr {
|
||||||
if ($value instanceof Node\Expr) {
|
if ($value instanceof Node\Expr) {
|
||||||
return $value;
|
return $value;
|
||||||
} elseif (is_null($value)) {
|
}
|
||||||
|
|
||||||
|
if (is_null($value)) {
|
||||||
return new Expr\ConstFetch(
|
return new Expr\ConstFetch(
|
||||||
new Name('null')
|
new Name('null')
|
||||||
);
|
);
|
||||||
} elseif (is_bool($value)) {
|
}
|
||||||
|
|
||||||
|
if (is_bool($value)) {
|
||||||
return new Expr\ConstFetch(
|
return new Expr\ConstFetch(
|
||||||
new Name($value ? 'true' : 'false')
|
new Name($value ? 'true' : 'false')
|
||||||
);
|
);
|
||||||
} elseif (is_int($value)) {
|
}
|
||||||
|
|
||||||
|
if (is_int($value)) {
|
||||||
return new Scalar\LNumber($value);
|
return new Scalar\LNumber($value);
|
||||||
} elseif (is_float($value)) {
|
}
|
||||||
|
|
||||||
|
if (is_float($value)) {
|
||||||
return new Scalar\DNumber($value);
|
return new Scalar\DNumber($value);
|
||||||
} elseif (is_string($value)) {
|
}
|
||||||
|
|
||||||
|
if (is_string($value)) {
|
||||||
return new Scalar\String_($value);
|
return new Scalar\String_($value);
|
||||||
} elseif (is_array($value)) {
|
}
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
$items = [];
|
$items = [];
|
||||||
$lastKey = -1;
|
$lastKey = -1;
|
||||||
foreach ($value as $itemKey => $itemValue) {
|
foreach ($value as $itemKey => $itemValue) {
|
||||||
@@ -248,9 +268,13 @@ final class BuilderHelpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Expr\Array_($items);
|
return new Expr\Array_($items);
|
||||||
} else {
|
|
||||||
throw new \LogicException('Invalid value');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($value instanceof \UnitEnum) {
|
||||||
|
return new Expr\ClassConstFetch(new FullyQualified(\get_class($value)), new Identifier($value->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \LogicException('Invalid value');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -263,11 +287,33 @@ final class BuilderHelpers
|
|||||||
public static function normalizeDocComment($docComment) : Comment\Doc {
|
public static function normalizeDocComment($docComment) : Comment\Doc {
|
||||||
if ($docComment instanceof Comment\Doc) {
|
if ($docComment instanceof Comment\Doc) {
|
||||||
return $docComment;
|
return $docComment;
|
||||||
} elseif (is_string($docComment)) {
|
}
|
||||||
|
|
||||||
|
if (is_string($docComment)) {
|
||||||
return new Comment\Doc($docComment);
|
return new Comment\Doc($docComment);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
|
throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes a attribute: Converts attribute to the Attribute Group if needed.
|
||||||
|
*
|
||||||
|
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||||
|
*
|
||||||
|
* @return Node\AttributeGroup The Attribute Group
|
||||||
|
*/
|
||||||
|
public static function normalizeAttribute($attribute) : Node\AttributeGroup
|
||||||
|
{
|
||||||
|
if ($attribute instanceof Node\AttributeGroup) {
|
||||||
|
return $attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($attribute instanceof Node\Attribute)) {
|
||||||
|
throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Node\AttributeGroup([$attribute]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -282,4 +328,13 @@ final class BuilderHelpers
|
|||||||
Stmt\Class_::verifyModifier($modifiers, $modifier);
|
Stmt\Class_::verifyModifier($modifiers, $modifier);
|
||||||
return $modifiers | $modifier;
|
return $modifiers | $modifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a modifier and returns new modifier bitmask.
|
||||||
|
* @return int New modifiers
|
||||||
|
*/
|
||||||
|
public static function addClassModifier(int $existingModifiers, int $modifierToSet) : int {
|
||||||
|
Stmt\Class_::verifyClassModifier($existingModifiers, $modifierToSet);
|
||||||
|
return $existingModifiers | $modifierToSet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace PhpParser;
|
namespace PhpParser;
|
||||||
|
|
||||||
|
use function array_merge;
|
||||||
use PhpParser\Node\Expr;
|
use PhpParser\Node\Expr;
|
||||||
use PhpParser\Node\Scalar;
|
use PhpParser\Node\Scalar;
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ class ConstExprEvaluator
|
|||||||
*
|
*
|
||||||
* @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
|
* @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
|
||||||
*/
|
*/
|
||||||
public function __construct(callable $fallbackEvaluator = null) {
|
public function __construct(?callable $fallbackEvaluator = null) {
|
||||||
$this->fallbackEvaluator = $fallbackEvaluator ?? function(Expr $expr) {
|
$this->fallbackEvaluator = $fallbackEvaluator ?? function(Expr $expr) {
|
||||||
throw new ConstExprEvaluationException(
|
throw new ConstExprEvaluationException(
|
||||||
"Expression of type {$expr->getType()} cannot be evaluated"
|
"Expression of type {$expr->getType()} cannot be evaluated"
|
||||||
@@ -150,6 +151,8 @@ class ConstExprEvaluator
|
|||||||
foreach ($expr->items as $item) {
|
foreach ($expr->items as $item) {
|
||||||
if (null !== $item->key) {
|
if (null !== $item->key) {
|
||||||
$array[$this->evaluate($item->key)] = $this->evaluate($item->value);
|
$array[$this->evaluate($item->key)] = $this->evaluate($item->value);
|
||||||
|
} elseif ($item->unpack) {
|
||||||
|
$array = array_merge($array, $this->evaluate($item->value));
|
||||||
} else {
|
} else {
|
||||||
$array[] = $this->evaluate($item->value);
|
$array[] = $this->evaluate($item->value);
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,8 @@ class PrintableNewAnonClassNode extends Expr
|
|||||||
{
|
{
|
||||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||||
public $attrGroups;
|
public $attrGroups;
|
||||||
|
/** @var int Modifiers */
|
||||||
|
public $flags;
|
||||||
/** @var Node\Arg[] Arguments */
|
/** @var Node\Arg[] Arguments */
|
||||||
public $args;
|
public $args;
|
||||||
/** @var null|Node\Name Name of extended class */
|
/** @var null|Node\Name Name of extended class */
|
||||||
@@ -29,11 +31,12 @@ class PrintableNewAnonClassNode extends Expr
|
|||||||
public $stmts;
|
public $stmts;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $attrGroups, array $args, Node\Name $extends = null, array $implements,
|
array $attrGroups, int $flags, array $args, ?Node\Name $extends, array $implements,
|
||||||
array $stmts, array $attributes
|
array $stmts, array $attributes
|
||||||
) {
|
) {
|
||||||
parent::__construct($attributes);
|
parent::__construct($attributes);
|
||||||
$this->attrGroups = $attrGroups;
|
$this->attrGroups = $attrGroups;
|
||||||
|
$this->flags = $flags;
|
||||||
$this->args = $args;
|
$this->args = $args;
|
||||||
$this->extends = $extends;
|
$this->extends = $extends;
|
||||||
$this->implements = $implements;
|
$this->implements = $implements;
|
||||||
@@ -46,7 +49,7 @@ class PrintableNewAnonClassNode extends Expr
|
|||||||
// We don't assert that $class->name is null here, to allow consumers to assign unique names
|
// We don't assert that $class->name is null here, to allow consumers to assign unique names
|
||||||
// to anonymous classes for their own purposes. We simplify ignore the name here.
|
// to anonymous classes for their own purposes. We simplify ignore the name here.
|
||||||
return new self(
|
return new self(
|
||||||
$class->attrGroups, $newNode->args, $class->extends, $class->implements,
|
$class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements,
|
||||||
$class->stmts, $newNode->getAttributes()
|
$class->stmts, $newNode->getAttributes()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -56,6 +59,6 @@ class PrintableNewAnonClassNode extends Expr
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['attrGroups', 'args', 'extends', 'implements', 'stmts'];
|
return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,8 @@ class TokenStream
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function haveBraces(int $startPos, int $endPos) : 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, '}');
|
&& $this->haveTokenImmediatelyAfter($endPos, '}');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,9 +202,15 @@ class TokenStream
|
|||||||
|
|
||||||
public function haveBracesInRange(int $startPos, int $endPos) {
|
public function haveBracesInRange(int $startPos, int $endPos) {
|
||||||
return $this->haveTokenInRange($startPos, $endPos, '{')
|
return $this->haveTokenInRange($startPos, $endPos, '{')
|
||||||
|
|| $this->haveTokenInRange($startPos, $endPos, T_CURLY_OPEN)
|
||||||
|| $this->haveTokenInRange($startPos, $endPos, '}');
|
|| $this->haveTokenInRange($startPos, $endPos, '}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function haveTagInRange(int $startPos, int $endPos): bool {
|
||||||
|
return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
|
||||||
|
|| $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get indentation before token position.
|
* Get indentation before token position.
|
||||||
*
|
*
|
||||||
|
@@ -69,7 +69,7 @@ class Lexer
|
|||||||
* @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
|
* @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
|
||||||
* ErrorHandler\Throwing
|
* ErrorHandler\Throwing
|
||||||
*/
|
*/
|
||||||
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
|
public function startLexing(string $code, ?ErrorHandler $errorHandler = null) {
|
||||||
if (null === $errorHandler) {
|
if (null === $errorHandler) {
|
||||||
$errorHandler = new ErrorHandler\Throwing();
|
$errorHandler = new ErrorHandler\Throwing();
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,6 @@ class Lexer
|
|||||||
|
|
||||||
$scream = ini_set('xdebug.scream', '0');
|
$scream = ini_set('xdebug.scream', '0');
|
||||||
|
|
||||||
error_clear_last();
|
|
||||||
$this->tokens = @token_get_all($code);
|
$this->tokens = @token_get_all($code);
|
||||||
$this->postprocessTokens($errorHandler);
|
$this->postprocessTokens($errorHandler);
|
||||||
|
|
||||||
@@ -135,10 +134,11 @@ class Lexer
|
|||||||
// detected by finding "gaps" in the token array. Unterminated comments are detected
|
// detected by finding "gaps" in the token array. Unterminated comments are detected
|
||||||
// by checking if a trailing comment has a "*/" at the end.
|
// by checking if a trailing comment has a "*/" at the end.
|
||||||
//
|
//
|
||||||
// Additionally, we canonicalize to the PHP 8 comment format here, which does not include
|
// Additionally, we perform a number of canonicalizations here:
|
||||||
// the trailing whitespace anymore.
|
// * Use the PHP 8.0 comment format, which does not include trailing whitespace anymore.
|
||||||
//
|
// * Use PHP 8.0 T_NAME_* tokens.
|
||||||
// We also canonicalize to the PHP 8 T_NAME_* tokens.
|
// * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and
|
||||||
|
// T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types.
|
||||||
|
|
||||||
$filePos = 0;
|
$filePos = 0;
|
||||||
$line = 1;
|
$line = 1;
|
||||||
@@ -209,6 +209,22 @@ class Lexer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($token === '&') {
|
||||||
|
$next = $i + 1;
|
||||||
|
while (isset($this->tokens[$next]) && $this->tokens[$next][0] === \T_WHITESPACE) {
|
||||||
|
$next++;
|
||||||
|
}
|
||||||
|
$followedByVarOrVarArg = isset($this->tokens[$next]) &&
|
||||||
|
($this->tokens[$next][0] === \T_VARIABLE || $this->tokens[$next][0] === \T_ELLIPSIS);
|
||||||
|
$this->tokens[$i] = $token = [
|
||||||
|
$followedByVarOrVarArg
|
||||||
|
? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
|
||||||
|
: \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||||
|
'&',
|
||||||
|
$line,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$tokenValue = \is_string($token) ? $token : $token[1];
|
$tokenValue = \is_string($token) ? $token : $token[1];
|
||||||
$tokenLen = \strlen($tokenValue);
|
$tokenLen = \strlen($tokenValue);
|
||||||
|
|
||||||
@@ -323,7 +339,8 @@ class Lexer
|
|||||||
$value = $token[1];
|
$value = $token[1];
|
||||||
$id = $this->tokenMap[$token[0]];
|
$id = $this->tokenMap[$token[0]];
|
||||||
if (\T_CLOSE_TAG === $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]) {
|
} elseif (\T_INLINE_HTML === $token[0]) {
|
||||||
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
|
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
|
||||||
}
|
}
|
||||||
@@ -422,6 +439,11 @@ class Lexer
|
|||||||
'T_MATCH',
|
'T_MATCH',
|
||||||
'T_NULLSAFE_OBJECT_OPERATOR',
|
'T_NULLSAFE_OBJECT_OPERATOR',
|
||||||
'T_ATTRIBUTE',
|
'T_ATTRIBUTE',
|
||||||
|
// PHP 8.1
|
||||||
|
'T_ENUM',
|
||||||
|
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
|
||||||
|
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
|
||||||
|
'T_READONLY',
|
||||||
];
|
];
|
||||||
|
|
||||||
// PHP-Parser might be used together with another library that also emulates some or all
|
// PHP-Parser might be used together with another library that also emulates some or all
|
||||||
@@ -512,6 +534,10 @@ class Lexer
|
|||||||
$tokenMap[\T_MATCH] = Tokens::T_MATCH;
|
$tokenMap[\T_MATCH] = Tokens::T_MATCH;
|
||||||
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR;
|
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR;
|
||||||
$tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE;
|
$tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE;
|
||||||
|
$tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
|
||||||
|
$tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
|
||||||
|
$tokenMap[\T_ENUM] = Tokens::T_ENUM;
|
||||||
|
$tokenMap[\T_READONLY] = Tokens::T_READONLY;
|
||||||
|
|
||||||
return $tokenMap;
|
return $tokenMap;
|
||||||
}
|
}
|
||||||
@@ -520,7 +546,7 @@ class Lexer
|
|||||||
// Based on semi_reserved production.
|
// Based on semi_reserved production.
|
||||||
return array_fill_keys([
|
return array_fill_keys([
|
||||||
\T_STRING,
|
\T_STRING,
|
||||||
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC,
|
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
|
||||||
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
|
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
|
||||||
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
|
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
|
||||||
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
|
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
|
||||||
|
@@ -6,21 +6,26 @@ use PhpParser\Error;
|
|||||||
use PhpParser\ErrorHandler;
|
use PhpParser\ErrorHandler;
|
||||||
use PhpParser\Lexer;
|
use PhpParser\Lexer;
|
||||||
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
|
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
|
||||||
|
use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
|
||||||
|
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
|
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
||||||
|
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
|
||||||
|
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
||||||
use PhpParser\Parser\Tokens;
|
|
||||||
|
|
||||||
class Emulative extends Lexer
|
class Emulative extends Lexer
|
||||||
{
|
{
|
||||||
const PHP_7_3 = '7.3dev';
|
const PHP_7_3 = '7.3dev';
|
||||||
const PHP_7_4 = '7.4dev';
|
const PHP_7_4 = '7.4dev';
|
||||||
const PHP_8_0 = '8.0dev';
|
const PHP_8_0 = '8.0dev';
|
||||||
|
const PHP_8_1 = '8.1dev';
|
||||||
|
const PHP_8_2 = '8.2dev';
|
||||||
|
|
||||||
/** @var mixed[] Patches used to reverse changes introduced in the code */
|
/** @var mixed[] Patches used to reverse changes introduced in the code */
|
||||||
private $patches = [];
|
private $patches = [];
|
||||||
@@ -34,11 +39,11 @@ class Emulative extends Lexer
|
|||||||
/**
|
/**
|
||||||
* @param mixed[] $options Lexer options. In addition to the usual options,
|
* @param mixed[] $options Lexer options. In addition to the usual options,
|
||||||
* accepts a 'phpVersion' string that specifies the
|
* accepts a 'phpVersion' string that specifies the
|
||||||
* version to emulated. Defaults to newest supported.
|
* version to emulate. Defaults to newest supported.
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = [])
|
public function __construct(array $options = [])
|
||||||
{
|
{
|
||||||
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_0;
|
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_2;
|
||||||
unset($options['phpVersion']);
|
unset($options['phpVersion']);
|
||||||
|
|
||||||
parent::__construct($options);
|
parent::__construct($options);
|
||||||
@@ -51,6 +56,10 @@ class Emulative extends Lexer
|
|||||||
new NumericLiteralSeparatorEmulator(),
|
new NumericLiteralSeparatorEmulator(),
|
||||||
new NullsafeTokenEmulator(),
|
new NullsafeTokenEmulator(),
|
||||||
new AttributeEmulator(),
|
new AttributeEmulator(),
|
||||||
|
new EnumTokenEmulator(),
|
||||||
|
new ReadonlyTokenEmulator(),
|
||||||
|
new ExplicitOctalEmulator(),
|
||||||
|
new ReadonlyFunctionTokenEmulator(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Collect emulators that are relevant for the PHP version we're running
|
// Collect emulators that are relevant for the PHP version we're running
|
||||||
@@ -65,7 +74,7 @@ class Emulative extends Lexer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
|
public function startLexing(string $code, ?ErrorHandler $errorHandler = null) {
|
||||||
$emulators = array_filter($this->emulators, function($emulator) use($code) {
|
$emulators = array_filter($this->emulators, function($emulator) use($code) {
|
||||||
return $emulator->isEmulationNeeded($code);
|
return $emulator->isEmulationNeeded($code);
|
||||||
});
|
});
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
44
lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php
Normal file
44
lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Lexer\TokenEmulator;
|
||||||
|
|
||||||
|
use PhpParser\Lexer\Emulative;
|
||||||
|
|
||||||
|
class ExplicitOctalEmulator extends TokenEmulator {
|
||||||
|
public function getPhpVersion(): string {
|
||||||
|
return Emulative::PHP_8_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmulationNeeded(string $code): bool {
|
||||||
|
return strpos($code, '0o') !== false || strpos($code, '0O') !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emulate(string $code, array $tokens): array {
|
||||||
|
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||||
|
if ($tokens[$i][0] == \T_LNUMBER && $tokens[$i][1] === '0' &&
|
||||||
|
isset($tokens[$i + 1]) && $tokens[$i + 1][0] == \T_STRING &&
|
||||||
|
preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1][1])
|
||||||
|
) {
|
||||||
|
$tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1][1]);
|
||||||
|
array_splice($tokens, $i, 2, [
|
||||||
|
[$tokenKind, '0' . $tokens[$i + 1][1], $tokens[$i][2]],
|
||||||
|
]);
|
||||||
|
$c--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveIntegerOrFloatToken(string $str): int
|
||||||
|
{
|
||||||
|
$str = substr($str, 1);
|
||||||
|
$str = str_replace('_', '', $str);
|
||||||
|
$num = octdec($str);
|
||||||
|
return is_float($num) ? \T_DNUMBER : \T_LNUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseEmulate(string $code, array $tokens): array {
|
||||||
|
// Explicit octals were not legal code previously, don't bother.
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
}
|
@@ -12,16 +12,18 @@ abstract class KeywordEmulator extends TokenEmulator
|
|||||||
return strpos(strtolower($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
|
public function emulate(string $code, array $tokens): array
|
||||||
{
|
{
|
||||||
$keywordString = $this->getKeywordString();
|
$keywordString = $this->getKeywordString();
|
||||||
foreach ($tokens as $i => $token) {
|
foreach ($tokens as $i => $token) {
|
||||||
if ($token[0] === T_STRING && strtolower($token[1]) === $keywordString) {
|
if ($token[0] === T_STRING && strtolower($token[1]) === $keywordString
|
||||||
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $i);
|
&& $this->isKeywordContext($tokens, $i)) {
|
||||||
if ($previousNonSpaceToken !== null && $previousNonSpaceToken[0] === \T_OBJECT_OPERATOR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tokens[$i][0] = $this->getKeywordToken();
|
$tokens[$i][0] = $this->getKeywordToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +33,7 @@ abstract class KeywordEmulator extends TokenEmulator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed[] $tokens
|
* @param mixed[] $tokens
|
||||||
* @return mixed[]|null
|
* @return array|string|null
|
||||||
*/
|
*/
|
||||||
private function getPreviousNonSpaceToken(array $tokens, int $start)
|
private function getPreviousNonSpaceToken(array $tokens, int $start)
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Lexer\TokenEmulator;
|
||||||
|
|
||||||
|
use PhpParser\Lexer\Emulative;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In PHP 8.1, "readonly(" was special cased in the lexer in order to support functions with
|
||||||
|
* name readonly. In PHP 8.2, this may conflict with readonly properties having a DNF type. For
|
||||||
|
* this reason, PHP 8.2 instead treats this as T_READONLY and then handles it specially in the
|
||||||
|
* parser. This emulator only exists to handle this special case, which is skipped by the
|
||||||
|
* PHP 8.1 ReadonlyTokenEmulator.
|
||||||
|
*/
|
||||||
|
class ReadonlyFunctionTokenEmulator extends KeywordEmulator {
|
||||||
|
public function getKeywordString(): string {
|
||||||
|
return 'readonly';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKeywordToken(): int {
|
||||||
|
return \T_READONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPhpVersion(): string {
|
||||||
|
return Emulative::PHP_8_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseEmulate(string $code, array $tokens): array {
|
||||||
|
// Don't bother
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
}
|
36
lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php
Normal file
36
lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Lexer\TokenEmulator;
|
||||||
|
|
||||||
|
use PhpParser\Lexer\Emulative;
|
||||||
|
|
||||||
|
final class ReadonlyTokenEmulator extends KeywordEmulator
|
||||||
|
{
|
||||||
|
public function getPhpVersion(): string
|
||||||
|
{
|
||||||
|
return Emulative::PHP_8_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKeywordString(): string
|
||||||
|
{
|
||||||
|
return 'readonly';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKeywordToken(): int
|
||||||
|
{
|
||||||
|
return \T_READONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isKeywordContext(array $tokens, int $pos): bool
|
||||||
|
{
|
||||||
|
if (!parent::isKeywordContext($tokens, $pos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Support "function readonly("
|
||||||
|
return !(isset($tokens[$pos + 1]) &&
|
||||||
|
($tokens[$pos + 1][0] === '(' ||
|
||||||
|
($tokens[$pos + 1][0] === \T_WHITESPACE &&
|
||||||
|
isset($tokens[$pos + 2]) &&
|
||||||
|
$tokens[$pos + 2][0] === '(')));
|
||||||
|
}
|
||||||
|
}
|
@@ -36,7 +36,7 @@ class NameContext
|
|||||||
*
|
*
|
||||||
* @param Name|null $namespace Null is the global namespace
|
* @param Name|null $namespace Null is the global namespace
|
||||||
*/
|
*/
|
||||||
public function startNamespace(Name $namespace = null) {
|
public function startNamespace(?Name $namespace = null) {
|
||||||
$this->namespace = $namespace;
|
$this->namespace = $namespace;
|
||||||
$this->origAliases = $this->aliases = [
|
$this->origAliases = $this->aliases = [
|
||||||
Stmt\Use_::TYPE_NORMAL => [],
|
Stmt\Use_::TYPE_NORMAL => [],
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace PhpParser\Node;
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
|
use PhpParser\Node\VariadicPlaceholder;
|
||||||
use PhpParser\NodeAbstract;
|
use PhpParser\NodeAbstract;
|
||||||
|
|
||||||
class Arg extends NodeAbstract
|
class Arg extends NodeAbstract
|
||||||
@@ -26,7 +27,7 @@ class Arg extends NodeAbstract
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = [],
|
Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = [],
|
||||||
Identifier $name = null
|
?Identifier $name = null
|
||||||
) {
|
) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
14
lib/PhpParser/Node/ComplexType.php
Normal file
14
lib/PhpParser/Node/ComplexType.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
|
use PhpParser\NodeAbstract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a base class for complex types, including nullable types and union types.
|
||||||
|
*
|
||||||
|
* It does not provide any shared behavior and exists only for type-checking purposes.
|
||||||
|
*/
|
||||||
|
abstract class ComplexType extends NodeAbstract
|
||||||
|
{
|
||||||
|
}
|
@@ -4,9 +4,6 @@ namespace PhpParser\Node;
|
|||||||
|
|
||||||
use PhpParser\NodeAbstract;
|
use PhpParser\NodeAbstract;
|
||||||
|
|
||||||
/**
|
|
||||||
* @property Name $namespacedName Namespaced name (for global constants, if using NameResolver)
|
|
||||||
*/
|
|
||||||
class Const_ extends NodeAbstract
|
class Const_ extends NodeAbstract
|
||||||
{
|
{
|
||||||
/** @var Identifier Name */
|
/** @var Identifier Name */
|
||||||
@@ -14,6 +11,9 @@ class Const_ extends NodeAbstract
|
|||||||
/** @var Expr Value */
|
/** @var Expr Value */
|
||||||
public $value;
|
public $value;
|
||||||
|
|
||||||
|
/** @var Name|null Namespaced name (if using NameResolver) */
|
||||||
|
public $namespacedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a const node for use in class const and const statements.
|
* Constructs a const node for use in class const and const statements.
|
||||||
*
|
*
|
||||||
|
@@ -18,7 +18,7 @@ class ArrayDimFetch extends Expr
|
|||||||
* @param null|Expr $dim Array index / dim
|
* @param null|Expr $dim Array index / dim
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Expr $var, Expr $dim = null, array $attributes = []) {
|
public function __construct(Expr $var, ?Expr $dim = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->var = $var;
|
$this->var = $var;
|
||||||
$this->dim = $dim;
|
$this->dim = $dim;
|
||||||
|
@@ -23,7 +23,7 @@ class ArrayItem extends Expr
|
|||||||
* @param bool $byRef Whether to assign by reference
|
* @param bool $byRef Whether to assign by reference
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Expr $value, Expr $key = null, bool $byRef = false, array $attributes = [], bool $unpack = false) {
|
public function __construct(Expr $value, ?Expr $key = null, bool $byRef = false, array $attributes = [], bool $unpack = false) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
|
@@ -17,7 +17,7 @@ class ArrowFunction extends Expr implements FunctionLike
|
|||||||
/** @var Node\Param[] */
|
/** @var Node\Param[] */
|
||||||
public $params = [];
|
public $params = [];
|
||||||
|
|
||||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType */
|
/** @var null|Node\Identifier|Node\Name|Node\ComplexType */
|
||||||
public $returnType;
|
public $returnType;
|
||||||
|
|
||||||
/** @var Expr */
|
/** @var Expr */
|
||||||
@@ -42,7 +42,7 @@ class ArrowFunction extends Expr implements FunctionLike
|
|||||||
$this->params = $subNodes['params'] ?? [];
|
$this->params = $subNodes['params'] ?? [];
|
||||||
$returnType = $subNodes['returnType'] ?? null;
|
$returnType = $subNodes['returnType'] ?? null;
|
||||||
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
|
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
|
||||||
$this->expr = $subNodes['expr'] ?? null;
|
$this->expr = $subNodes['expr'];
|
||||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
lib/PhpParser/Node/Expr/CallLike.php
Normal file
39
lib/PhpParser/Node/Expr/CallLike.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Expr;
|
||||||
|
|
||||||
|
use PhpParser\Node\Arg;
|
||||||
|
use PhpParser\Node\Expr;
|
||||||
|
use PhpParser\Node\VariadicPlaceholder;
|
||||||
|
|
||||||
|
abstract class CallLike extends Expr {
|
||||||
|
/**
|
||||||
|
* Return raw arguments, which may be actual Args, or VariadicPlaceholders for first-class
|
||||||
|
* callables.
|
||||||
|
*
|
||||||
|
* @return array<Arg|VariadicPlaceholder>
|
||||||
|
*/
|
||||||
|
abstract public function getRawArgs(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this call expression is actually a first class callable.
|
||||||
|
*/
|
||||||
|
public function isFirstClassCallable(): bool {
|
||||||
|
foreach ($this->getRawArgs() as $arg) {
|
||||||
|
if ($arg instanceof VariadicPlaceholder) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that this is not a first-class callable and return only ordinary Args.
|
||||||
|
*
|
||||||
|
* @return Arg[]
|
||||||
|
*/
|
||||||
|
public function getArgs(): array {
|
||||||
|
assert(!$this->isFirstClassCallable());
|
||||||
|
return $this->getRawArgs();
|
||||||
|
}
|
||||||
|
}
|
@@ -10,14 +10,14 @@ class ClassConstFetch extends Expr
|
|||||||
{
|
{
|
||||||
/** @var Name|Expr Class name */
|
/** @var Name|Expr Class name */
|
||||||
public $class;
|
public $class;
|
||||||
/** @var Identifier|Error Constant name */
|
/** @var Identifier|Expr|Error Constant name */
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a class const fetch node.
|
* Constructs a class const fetch node.
|
||||||
*
|
*
|
||||||
* @param Name|Expr $class Class name
|
* @param Name|Expr $class Class name
|
||||||
* @param string|Identifier|Error $name Constant name
|
* @param string|Identifier|Expr|Error $name Constant name
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct($class, $name, array $attributes = []) {
|
public function __construct($class, $name, array $attributes = []) {
|
||||||
|
@@ -16,7 +16,7 @@ class Closure extends Expr implements FunctionLike
|
|||||||
public $params;
|
public $params;
|
||||||
/** @var ClosureUse[] use()s */
|
/** @var ClosureUse[] use()s */
|
||||||
public $uses;
|
public $uses;
|
||||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
|
/** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
|
||||||
public $returnType;
|
public $returnType;
|
||||||
/** @var Node\Stmt[] Statements */
|
/** @var Node\Stmt[] Statements */
|
||||||
public $stmts;
|
public $stmts;
|
||||||
|
@@ -19,7 +19,7 @@ class Exit_ extends Expr
|
|||||||
* @param null|Expr $expr Expression
|
* @param null|Expr $expr Expression
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Expr $expr = null, array $attributes = []) {
|
public function __construct(?Expr $expr = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->expr = $expr;
|
$this->expr = $expr;
|
||||||
}
|
}
|
||||||
|
@@ -5,18 +5,18 @@ namespace PhpParser\Node\Expr;
|
|||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Expr;
|
use PhpParser\Node\Expr;
|
||||||
|
|
||||||
class FuncCall extends Expr
|
class FuncCall extends CallLike
|
||||||
{
|
{
|
||||||
/** @var Node\Name|Expr Function name */
|
/** @var Node\Name|Expr Function name */
|
||||||
public $name;
|
public $name;
|
||||||
/** @var Node\Arg[] Arguments */
|
/** @var array<Node\Arg|Node\VariadicPlaceholder> Arguments */
|
||||||
public $args;
|
public $args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a function call node.
|
* Constructs a function call node.
|
||||||
*
|
*
|
||||||
* @param Node\Name|Expr $name Function name
|
* @param Node\Name|Expr $name Function name
|
||||||
* @param Node\Arg[] $args Arguments
|
* @param array<Node\Arg|Node\VariadicPlaceholder> $args Arguments
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct($name, array $args = [], array $attributes = []) {
|
public function __construct($name, array $args = [], array $attributes = []) {
|
||||||
@@ -32,4 +32,8 @@ class FuncCall extends Expr
|
|||||||
public function getType() : string {
|
public function getType() : string {
|
||||||
return 'Expr_FuncCall';
|
return 'Expr_FuncCall';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRawArgs(): array {
|
||||||
|
return $this->args;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,14 +5,15 @@ namespace PhpParser\Node\Expr;
|
|||||||
use PhpParser\Node\Arg;
|
use PhpParser\Node\Arg;
|
||||||
use PhpParser\Node\Expr;
|
use PhpParser\Node\Expr;
|
||||||
use PhpParser\Node\Identifier;
|
use PhpParser\Node\Identifier;
|
||||||
|
use PhpParser\Node\VariadicPlaceholder;
|
||||||
|
|
||||||
class MethodCall extends Expr
|
class MethodCall extends CallLike
|
||||||
{
|
{
|
||||||
/** @var Expr Variable holding object */
|
/** @var Expr Variable holding object */
|
||||||
public $var;
|
public $var;
|
||||||
/** @var Identifier|Expr Method name */
|
/** @var Identifier|Expr Method name */
|
||||||
public $name;
|
public $name;
|
||||||
/** @var Arg[] Arguments */
|
/** @var array<Arg|VariadicPlaceholder> Arguments */
|
||||||
public $args;
|
public $args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +21,7 @@ class MethodCall extends Expr
|
|||||||
*
|
*
|
||||||
* @param Expr $var Variable holding object
|
* @param Expr $var Variable holding object
|
||||||
* @param string|Identifier|Expr $name Method name
|
* @param string|Identifier|Expr $name Method name
|
||||||
* @param Arg[] $args Arguments
|
* @param array<Arg|VariadicPlaceholder> $args Arguments
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
|
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
|
||||||
@@ -37,4 +38,8 @@ class MethodCall extends Expr
|
|||||||
public function getType() : string {
|
public function getType() : string {
|
||||||
return 'Expr_MethodCall';
|
return 'Expr_MethodCall';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRawArgs(): array {
|
||||||
|
return $this->args;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,20 +3,22 @@
|
|||||||
namespace PhpParser\Node\Expr;
|
namespace PhpParser\Node\Expr;
|
||||||
|
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Arg;
|
||||||
use PhpParser\Node\Expr;
|
use PhpParser\Node\Expr;
|
||||||
|
use PhpParser\Node\VariadicPlaceholder;
|
||||||
|
|
||||||
class New_ extends Expr
|
class New_ extends CallLike
|
||||||
{
|
{
|
||||||
/** @var Node\Name|Expr|Node\Stmt\Class_ Class name */
|
/** @var Node\Name|Expr|Node\Stmt\Class_ Class name */
|
||||||
public $class;
|
public $class;
|
||||||
/** @var Node\Arg[] Arguments */
|
/** @var array<Arg|VariadicPlaceholder> Arguments */
|
||||||
public $args;
|
public $args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a function call node.
|
* Constructs a function call node.
|
||||||
*
|
*
|
||||||
* @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes)
|
* @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes)
|
||||||
* @param Node\Arg[] $args Arguments
|
* @param array<Arg|VariadicPlaceholder> $args Arguments
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct($class, array $args = [], array $attributes = []) {
|
public function __construct($class, array $args = [], array $attributes = []) {
|
||||||
@@ -32,4 +34,8 @@ class New_ extends Expr
|
|||||||
public function getType() : string {
|
public function getType() : string {
|
||||||
return 'Expr_New';
|
return 'Expr_New';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRawArgs(): array {
|
||||||
|
return $this->args;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,14 +5,15 @@ namespace PhpParser\Node\Expr;
|
|||||||
use PhpParser\Node\Arg;
|
use PhpParser\Node\Arg;
|
||||||
use PhpParser\Node\Expr;
|
use PhpParser\Node\Expr;
|
||||||
use PhpParser\Node\Identifier;
|
use PhpParser\Node\Identifier;
|
||||||
|
use PhpParser\Node\VariadicPlaceholder;
|
||||||
|
|
||||||
class NullsafeMethodCall extends Expr
|
class NullsafeMethodCall extends CallLike
|
||||||
{
|
{
|
||||||
/** @var Expr Variable holding object */
|
/** @var Expr Variable holding object */
|
||||||
public $var;
|
public $var;
|
||||||
/** @var Identifier|Expr Method name */
|
/** @var Identifier|Expr Method name */
|
||||||
public $name;
|
public $name;
|
||||||
/** @var Arg[] Arguments */
|
/** @var array<Arg|VariadicPlaceholder> Arguments */
|
||||||
public $args;
|
public $args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +21,7 @@ class NullsafeMethodCall extends Expr
|
|||||||
*
|
*
|
||||||
* @param Expr $var Variable holding object
|
* @param Expr $var Variable holding object
|
||||||
* @param string|Identifier|Expr $name Method name
|
* @param string|Identifier|Expr $name Method name
|
||||||
* @param Arg[] $args Arguments
|
* @param array<Arg|VariadicPlaceholder> $args Arguments
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
|
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
|
||||||
@@ -37,4 +38,8 @@ class NullsafeMethodCall extends Expr
|
|||||||
public function getType() : string {
|
public function getType() : string {
|
||||||
return 'Expr_NullsafeMethodCall';
|
return 'Expr_NullsafeMethodCall';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRawArgs(): array {
|
||||||
|
return $this->args;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,16 +3,18 @@
|
|||||||
namespace PhpParser\Node\Expr;
|
namespace PhpParser\Node\Expr;
|
||||||
|
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Arg;
|
||||||
use PhpParser\Node\Expr;
|
use PhpParser\Node\Expr;
|
||||||
use PhpParser\Node\Identifier;
|
use PhpParser\Node\Identifier;
|
||||||
|
use PhpParser\Node\VariadicPlaceholder;
|
||||||
|
|
||||||
class StaticCall extends Expr
|
class StaticCall extends CallLike
|
||||||
{
|
{
|
||||||
/** @var Node\Name|Expr Class name */
|
/** @var Node\Name|Expr Class name */
|
||||||
public $class;
|
public $class;
|
||||||
/** @var Identifier|Expr Method name */
|
/** @var Identifier|Expr Method name */
|
||||||
public $name;
|
public $name;
|
||||||
/** @var Node\Arg[] Arguments */
|
/** @var array<Arg|VariadicPlaceholder> Arguments */
|
||||||
public $args;
|
public $args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +22,7 @@ class StaticCall extends Expr
|
|||||||
*
|
*
|
||||||
* @param Node\Name|Expr $class Class name
|
* @param Node\Name|Expr $class Class name
|
||||||
* @param string|Identifier|Expr $name Method name
|
* @param string|Identifier|Expr $name Method name
|
||||||
* @param Node\Arg[] $args Arguments
|
* @param array<Arg|VariadicPlaceholder> $args Arguments
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct($class, $name, array $args = [], array $attributes = []) {
|
public function __construct($class, $name, array $args = [], array $attributes = []) {
|
||||||
@@ -37,4 +39,8 @@ class StaticCall extends Expr
|
|||||||
public function getType() : string {
|
public function getType() : string {
|
||||||
return 'Expr_StaticCall';
|
return 'Expr_StaticCall';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRawArgs(): array {
|
||||||
|
return $this->args;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ class Yield_ extends Expr
|
|||||||
* @param null|Expr $key Key expression
|
* @param null|Expr $key Key expression
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Expr $value = null, Expr $key = null, array $attributes = []) {
|
public function __construct(?Expr $value = null, ?Expr $key = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
|
@@ -23,7 +23,7 @@ interface FunctionLike extends Node
|
|||||||
/**
|
/**
|
||||||
* Get the declared return type or null
|
* Get the declared return type or null
|
||||||
*
|
*
|
||||||
* @return null|Identifier|Name|NullableType|UnionType
|
* @return null|Identifier|Name|ComplexType
|
||||||
*/
|
*/
|
||||||
public function getReturnType();
|
public function getReturnType();
|
||||||
|
|
||||||
|
30
lib/PhpParser/Node/IntersectionType.php
Normal file
30
lib/PhpParser/Node/IntersectionType.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
|
use PhpParser\NodeAbstract;
|
||||||
|
|
||||||
|
class IntersectionType extends ComplexType
|
||||||
|
{
|
||||||
|
/** @var (Identifier|Name)[] Types */
|
||||||
|
public $types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an intersection type.
|
||||||
|
*
|
||||||
|
* @param (Identifier|Name)[] $types Types
|
||||||
|
* @param array $attributes Additional attributes
|
||||||
|
*/
|
||||||
|
public function __construct(array $types, array $attributes = []) {
|
||||||
|
$this->attributes = $attributes;
|
||||||
|
$this->types = $types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubNodeNames() : array {
|
||||||
|
return ['types'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'IntersectionType';
|
||||||
|
}
|
||||||
|
}
|
@@ -6,7 +6,10 @@ use PhpParser\NodeAbstract;
|
|||||||
|
|
||||||
class Name extends NodeAbstract
|
class Name extends NodeAbstract
|
||||||
{
|
{
|
||||||
/** @var string[] Parts of the name */
|
/**
|
||||||
|
* @var string[] Parts of the name
|
||||||
|
* @deprecated Use getParts() instead
|
||||||
|
*/
|
||||||
public $parts;
|
public $parts;
|
||||||
|
|
||||||
private static $specialClassNames = [
|
private static $specialClassNames = [
|
||||||
@@ -30,6 +33,15 @@ class Name extends NodeAbstract
|
|||||||
return ['parts'];
|
return ['parts'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get parts of name (split by the namespace separator).
|
||||||
|
*
|
||||||
|
* @return string[] Parts of name
|
||||||
|
*/
|
||||||
|
public function getParts(): array {
|
||||||
|
return $this->parts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the first part of the name, i.e. everything before the first namespace separator.
|
* Gets the first part of the name, i.e. everything before the first namespace separator.
|
||||||
*
|
*
|
||||||
@@ -150,7 +162,7 @@ class Name extends NodeAbstract
|
|||||||
*
|
*
|
||||||
* @return static|null Sliced name
|
* @return static|null Sliced name
|
||||||
*/
|
*/
|
||||||
public function slice(int $offset, int $length = null) {
|
public function slice(int $offset, ?int $length = null) {
|
||||||
$numParts = count($this->parts);
|
$numParts = count($this->parts);
|
||||||
|
|
||||||
$realOffset = $offset < 0 ? $offset + $numParts : $offset;
|
$realOffset = $offset < 0 ? $offset + $numParts : $offset;
|
||||||
@@ -162,7 +174,7 @@ class Name extends NodeAbstract
|
|||||||
$realLength = $numParts - $realOffset;
|
$realLength = $numParts - $realOffset;
|
||||||
} else {
|
} else {
|
||||||
$realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
|
$realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
|
||||||
if ($realLength < 0 || $realLength > $numParts) {
|
if ($realLength < 0 || $realLength > $numParts - $realOffset) {
|
||||||
throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
|
throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace PhpParser\Node;
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
use PhpParser\NodeAbstract;
|
class NullableType extends ComplexType
|
||||||
|
|
||||||
class NullableType extends NodeAbstract
|
|
||||||
{
|
{
|
||||||
/** @var Identifier|Name Type */
|
/** @var Identifier|Name Type */
|
||||||
public $type;
|
public $type;
|
||||||
|
@@ -6,7 +6,7 @@ use PhpParser\NodeAbstract;
|
|||||||
|
|
||||||
class Param extends NodeAbstract
|
class Param extends NodeAbstract
|
||||||
{
|
{
|
||||||
/** @var null|Identifier|Name|NullableType|UnionType Type declaration */
|
/** @var null|Identifier|Name|ComplexType Type declaration */
|
||||||
public $type;
|
public $type;
|
||||||
/** @var bool Whether parameter is passed by reference */
|
/** @var bool Whether parameter is passed by reference */
|
||||||
public $byRef;
|
public $byRef;
|
||||||
@@ -26,7 +26,7 @@ class Param extends NodeAbstract
|
|||||||
*
|
*
|
||||||
* @param Expr\Variable|Expr\Error $var Parameter variable
|
* @param Expr\Variable|Expr\Error $var Parameter variable
|
||||||
* @param null|Expr $default Default value
|
* @param null|Expr $default Default value
|
||||||
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
|
* @param null|string|Identifier|Name|ComplexType $type Type declaration
|
||||||
* @param bool $byRef Whether is passed by reference
|
* @param bool $byRef Whether is passed by reference
|
||||||
* @param bool $variadic Whether this is a variadic argument
|
* @param bool $variadic Whether this is a variadic argument
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
@@ -34,7 +34,7 @@ class Param extends NodeAbstract
|
|||||||
* @param AttributeGroup[] $attrGroups PHP attribute groups
|
* @param AttributeGroup[] $attrGroups PHP attribute groups
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$var, Expr $default = null, $type = null,
|
$var, ?Expr $default = null, $type = null,
|
||||||
bool $byRef = false, bool $variadic = false,
|
bool $byRef = false, bool $variadic = false,
|
||||||
array $attributes = [],
|
array $attributes = [],
|
||||||
int $flags = 0,
|
int $flags = 0,
|
||||||
|
@@ -24,6 +24,17 @@ class DNumber extends Scalar
|
|||||||
return ['value'];
|
return ['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $attributes
|
||||||
|
*/
|
||||||
|
public static function fromString(string $str, array $attributes = []): DNumber
|
||||||
|
{
|
||||||
|
$attributes['rawValue'] = $str;
|
||||||
|
$float = self::parse($str);
|
||||||
|
|
||||||
|
return new DNumber($float, $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
@@ -36,13 +47,7 @@ class DNumber extends Scalar
|
|||||||
public static function parse(string $str) : float {
|
public static function parse(string $str) : float {
|
||||||
$str = str_replace('_', '', $str);
|
$str = str_replace('_', '', $str);
|
||||||
|
|
||||||
// if string contains any of .eE just cast it to float
|
// Check whether this is one of the special integer notations.
|
||||||
if (false !== strpbrk($str, '.eE')) {
|
|
||||||
return (float) $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise it's an integer notation that overflowed into a float
|
|
||||||
// if it starts with 0 it's one of the special integer notations
|
|
||||||
if ('0' === $str[0]) {
|
if ('0' === $str[0]) {
|
||||||
// hex
|
// hex
|
||||||
if ('x' === $str[1] || 'X' === $str[1]) {
|
if ('x' === $str[1] || 'X' === $str[1]) {
|
||||||
@@ -54,11 +59,13 @@ class DNumber extends Scalar
|
|||||||
return bindec($str);
|
return bindec($str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// oct
|
// oct, but only if the string does not contain any of '.eE'.
|
||||||
// substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit (8 or 9)
|
if (false === strpbrk($str, '.eE')) {
|
||||||
// so that only the digits before that are used
|
// substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit
|
||||||
|
// (8 or 9) so that only the digits before that are used.
|
||||||
return octdec(substr($str, 0, strcspn($str, '89')));
|
return octdec(substr($str, 0, strcspn($str, '89')));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// dec
|
// dec
|
||||||
return (float) $str;
|
return (float) $str;
|
||||||
|
@@ -41,6 +41,8 @@ class LNumber extends Scalar
|
|||||||
* @return LNumber The constructed LNumber, including kind attribute
|
* @return LNumber The constructed LNumber, including kind attribute
|
||||||
*/
|
*/
|
||||||
public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber {
|
public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber {
|
||||||
|
$attributes['rawValue'] = $str;
|
||||||
|
|
||||||
$str = str_replace('_', '', $str);
|
$str = str_replace('_', '', $str);
|
||||||
|
|
||||||
if ('0' !== $str[0] || '0' === $str) {
|
if ('0' !== $str[0] || '0' === $str) {
|
||||||
@@ -62,6 +64,11 @@ class LNumber extends Scalar
|
|||||||
throw new Error('Invalid numeric literal', $attributes);
|
throw new Error('Invalid numeric literal', $attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip optional explicit octal prefix.
|
||||||
|
if ('o' === $str[1] || 'O' === $str[1]) {
|
||||||
|
$str = substr($str, 2);
|
||||||
|
}
|
||||||
|
|
||||||
// use intval instead of octdec to get proper cutting behavior with malformed numbers
|
// use intval instead of octdec to get proper cutting behavior with malformed numbers
|
||||||
$attributes['kind'] = LNumber::KIND_OCT;
|
$attributes['kind'] = LNumber::KIND_OCT;
|
||||||
return new LNumber(intval($str, 8), $attributes);
|
return new LNumber(intval($str, 8), $attributes);
|
||||||
|
@@ -42,6 +42,22 @@ class String_ extends Scalar
|
|||||||
return ['value'];
|
return ['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
|
||||||
|
*/
|
||||||
|
public static function fromString(string $str, array $attributes = [], bool $parseUnicodeEscape = true): self
|
||||||
|
{
|
||||||
|
$attributes['kind'] = ($str[0] === "'" || ($str[1] === "'" && ($str[0] === 'b' || $str[0] === 'B')))
|
||||||
|
? Scalar\String_::KIND_SINGLE_QUOTED
|
||||||
|
: Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||||
|
|
||||||
|
$attributes['rawValue'] = $str;
|
||||||
|
|
||||||
|
$string = self::parse($str, $parseUnicodeEscape);
|
||||||
|
|
||||||
|
return new self($string, $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
|
@@ -15,7 +15,7 @@ class Break_ extends Node\Stmt
|
|||||||
* @param null|Node\Expr $num Number of loops to break
|
* @param null|Node\Expr $num Number of loops to break
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Node\Expr $num = null, array $attributes = []) {
|
public function __construct(?Node\Expr $num = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->num = $num;
|
$this->num = $num;
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@ class Catch_ extends Node\Stmt
|
|||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $types, Expr\Variable $var = null, array $stmts = [], array $attributes = []
|
array $types, ?Expr\Variable $var = null, array $stmts = [], array $attributes = []
|
||||||
) {
|
) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->types = $types;
|
$this->types = $types;
|
||||||
|
@@ -10,8 +10,10 @@ class ClassConst extends Node\Stmt
|
|||||||
public $flags;
|
public $flags;
|
||||||
/** @var Node\Const_[] Constant declarations */
|
/** @var Node\Const_[] Constant declarations */
|
||||||
public $consts;
|
public $consts;
|
||||||
/** @var Node\AttributeGroup[] */
|
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||||
public $attrGroups;
|
public $attrGroups;
|
||||||
|
/** @var Node\Identifier|Node\Name|Node\ComplexType|null Type declaration */
|
||||||
|
public $type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a class const list node.
|
* Constructs a class const list node.
|
||||||
@@ -20,21 +22,24 @@ class ClassConst extends Node\Stmt
|
|||||||
* @param int $flags Modifiers
|
* @param int $flags Modifiers
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
|
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
|
||||||
|
* @param null|string|Node\Identifier|Node\Name|Node\ComplexType $type Type declaration
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $consts,
|
array $consts,
|
||||||
int $flags = 0,
|
int $flags = 0,
|
||||||
array $attributes = [],
|
array $attributes = [],
|
||||||
array $attrGroups = []
|
array $attrGroups = [],
|
||||||
|
$type = null
|
||||||
) {
|
) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->flags = $flags;
|
$this->flags = $flags;
|
||||||
$this->consts = $consts;
|
$this->consts = $consts;
|
||||||
$this->attrGroups = $attrGroups;
|
$this->attrGroups = $attrGroups;
|
||||||
|
$this->type = \is_string($type) ? new Node\Identifier($type) : $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['attrGroups', 'flags', 'consts'];
|
return ['attrGroups', 'flags', 'type', 'consts'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,6 +70,15 @@ class ClassConst extends Node\Stmt
|
|||||||
return (bool) ($this->flags & Class_::MODIFIER_PRIVATE);
|
return (bool) ($this->flags & Class_::MODIFIER_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether constant is final.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isFinal() : bool {
|
||||||
|
return (bool) ($this->flags & Class_::MODIFIER_FINAL);
|
||||||
|
}
|
||||||
|
|
||||||
public function getType() : string {
|
public function getType() : string {
|
||||||
return 'Stmt_ClassConst';
|
return 'Stmt_ClassConst';
|
||||||
}
|
}
|
||||||
|
@@ -4,9 +4,6 @@ namespace PhpParser\Node\Stmt;
|
|||||||
|
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
|
|
||||||
/**
|
|
||||||
* @property Node\Name $namespacedName Namespaced name (if using NameResolver)
|
|
||||||
*/
|
|
||||||
abstract class ClassLike extends Node\Stmt
|
abstract class ClassLike extends Node\Stmt
|
||||||
{
|
{
|
||||||
/** @var Node\Identifier|null Name */
|
/** @var Node\Identifier|null Name */
|
||||||
@@ -16,6 +13,9 @@ abstract class ClassLike extends Node\Stmt
|
|||||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||||
public $attrGroups;
|
public $attrGroups;
|
||||||
|
|
||||||
|
/** @var Node\Name|null Namespaced name (if using NameResolver) */
|
||||||
|
public $namespacedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return TraitUse[]
|
* @return TraitUse[]
|
||||||
*/
|
*/
|
||||||
|
@@ -15,7 +15,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
|||||||
public $name;
|
public $name;
|
||||||
/** @var Node\Param[] Parameters */
|
/** @var Node\Param[] Parameters */
|
||||||
public $params;
|
public $params;
|
||||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
|
/** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
|
||||||
public $returnType;
|
public $returnType;
|
||||||
/** @var Node\Stmt[]|null Statements */
|
/** @var Node\Stmt[]|null Statements */
|
||||||
public $stmts;
|
public $stmts;
|
||||||
@@ -38,6 +38,8 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
|||||||
'__clone' => true,
|
'__clone' => true,
|
||||||
'__invoke' => true,
|
'__invoke' => true,
|
||||||
'__debuginfo' => true,
|
'__debuginfo' => true,
|
||||||
|
'__serialize' => true,
|
||||||
|
'__unserialize' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -13,6 +13,7 @@ class Class_ extends ClassLike
|
|||||||
const MODIFIER_STATIC = 8;
|
const MODIFIER_STATIC = 8;
|
||||||
const MODIFIER_ABSTRACT = 16;
|
const MODIFIER_ABSTRACT = 16;
|
||||||
const MODIFIER_FINAL = 32;
|
const MODIFIER_FINAL = 32;
|
||||||
|
const MODIFIER_READONLY = 64;
|
||||||
|
|
||||||
const VISIBILITY_MODIFIER_MASK = 7; // 1 | 2 | 4
|
const VISIBILITY_MODIFIER_MASK = 7; // 1 | 2 | 4
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ class Class_ extends ClassLike
|
|||||||
* 'extends' => null : Name of extended class
|
* 'extends' => null : Name of extended class
|
||||||
* 'implements' => array(): Names of implemented interfaces
|
* 'implements' => array(): Names of implemented interfaces
|
||||||
* 'stmts' => array(): Statements
|
* 'stmts' => array(): Statements
|
||||||
* '$attrGroups' => array(): PHP attribute groups
|
* 'attrGroups' => array(): PHP attribute groups
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct($name, array $subNodes = [], array $attributes = []) {
|
public function __construct($name, array $subNodes = [], array $attributes = []) {
|
||||||
@@ -67,6 +68,10 @@ class Class_ extends ClassLike
|
|||||||
return (bool) ($this->flags & self::MODIFIER_FINAL);
|
return (bool) ($this->flags & self::MODIFIER_FINAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isReadonly() : bool {
|
||||||
|
return (bool) ($this->flags & self::MODIFIER_READONLY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the class is anonymous.
|
* Whether the class is anonymous.
|
||||||
*
|
*
|
||||||
@@ -76,6 +81,27 @@ class Class_ extends ClassLike
|
|||||||
return null === $this->name;
|
return null === $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static function verifyClassModifier($a, $b) {
|
||||||
|
if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) {
|
||||||
|
throw new Error('Multiple abstract modifiers are not allowed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) {
|
||||||
|
throw new Error('Multiple final modifiers are not allowed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) {
|
||||||
|
throw new Error('Multiple readonly modifiers are not allowed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a & 48 && $b & 48) {
|
||||||
|
throw new Error('Cannot use the final modifier on an abstract class');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@@ -96,6 +122,10 @@ class Class_ extends ClassLike
|
|||||||
throw new Error('Multiple final modifiers are not allowed');
|
throw new Error('Multiple final modifiers are not allowed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) {
|
||||||
|
throw new Error('Multiple readonly modifiers are not allowed');
|
||||||
|
}
|
||||||
|
|
||||||
if ($a & 48 && $b & 48) {
|
if ($a & 48 && $b & 48) {
|
||||||
throw new Error('Cannot use the final modifier on an abstract class member');
|
throw new Error('Cannot use the final modifier on an abstract class member');
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ class Continue_ extends Node\Stmt
|
|||||||
* @param null|Node\Expr $num Number of loops to continue
|
* @param null|Node\Expr $num Number of loops to continue
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Node\Expr $num = null, array $attributes = []) {
|
public function __construct(?Node\Expr $num = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->num = $num;
|
$this->num = $num;
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ class Declare_ extends Node\Stmt
|
|||||||
* @param Node\Stmt[]|null $stmts Statements
|
* @param Node\Stmt[]|null $stmts Statements
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(array $declares, array $stmts = null, array $attributes = []) {
|
public function __construct(array $declares, ?array $stmts = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->declares = $declares;
|
$this->declares = $declares;
|
||||||
$this->stmts = $stmts;
|
$this->stmts = $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';
|
||||||
|
}
|
||||||
|
}
|
@@ -5,9 +5,6 @@ namespace PhpParser\Node\Stmt;
|
|||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\FunctionLike;
|
use PhpParser\Node\FunctionLike;
|
||||||
|
|
||||||
/**
|
|
||||||
* @property Node\Name $namespacedName Namespaced name (if using NameResolver)
|
|
||||||
*/
|
|
||||||
class Function_ extends Node\Stmt implements FunctionLike
|
class Function_ extends Node\Stmt implements FunctionLike
|
||||||
{
|
{
|
||||||
/** @var bool Whether function returns by reference */
|
/** @var bool Whether function returns by reference */
|
||||||
@@ -16,13 +13,16 @@ class Function_ extends Node\Stmt implements FunctionLike
|
|||||||
public $name;
|
public $name;
|
||||||
/** @var Node\Param[] Parameters */
|
/** @var Node\Param[] Parameters */
|
||||||
public $params;
|
public $params;
|
||||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
|
/** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
|
||||||
public $returnType;
|
public $returnType;
|
||||||
/** @var Node\Stmt[] Statements */
|
/** @var Node\Stmt[] Statements */
|
||||||
public $stmts;
|
public $stmts;
|
||||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||||
public $attrGroups;
|
public $attrGroups;
|
||||||
|
|
||||||
|
/** @var Node\Name|null Namespaced name (if using NameResolver) */
|
||||||
|
public $namespacedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a function node.
|
* Constructs a function node.
|
||||||
*
|
*
|
||||||
|
@@ -22,7 +22,7 @@ class Namespace_ extends Node\Stmt
|
|||||||
* @param null|Node\Stmt[] $stmts Statements
|
* @param null|Node\Stmt[] $stmts Statements
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Node\Name $name = null, $stmts = [], array $attributes = []) {
|
public function __construct(?Node\Name $name = null, $stmts = [], array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->stmts = $stmts;
|
$this->stmts = $stmts;
|
||||||
|
@@ -3,10 +3,9 @@
|
|||||||
namespace PhpParser\Node\Stmt;
|
namespace PhpParser\Node\Stmt;
|
||||||
|
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\ComplexType;
|
||||||
use PhpParser\Node\Identifier;
|
use PhpParser\Node\Identifier;
|
||||||
use PhpParser\Node\Name;
|
use PhpParser\Node\Name;
|
||||||
use PhpParser\Node\NullableType;
|
|
||||||
use PhpParser\Node\UnionType;
|
|
||||||
|
|
||||||
class Property extends Node\Stmt
|
class Property extends Node\Stmt
|
||||||
{
|
{
|
||||||
@@ -14,7 +13,7 @@ class Property extends Node\Stmt
|
|||||||
public $flags;
|
public $flags;
|
||||||
/** @var PropertyProperty[] Properties */
|
/** @var PropertyProperty[] Properties */
|
||||||
public $props;
|
public $props;
|
||||||
/** @var null|Identifier|Name|NullableType|UnionType Type declaration */
|
/** @var null|Identifier|Name|ComplexType Type declaration */
|
||||||
public $type;
|
public $type;
|
||||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||||
public $attrGroups;
|
public $attrGroups;
|
||||||
@@ -25,7 +24,7 @@ class Property extends Node\Stmt
|
|||||||
* @param int $flags Modifiers
|
* @param int $flags Modifiers
|
||||||
* @param PropertyProperty[] $props Properties
|
* @param PropertyProperty[] $props Properties
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
|
* @param null|string|Identifier|Name|ComplexType $type Type declaration
|
||||||
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
|
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
|
||||||
*/
|
*/
|
||||||
public function __construct(int $flags, array $props, array $attributes = [], $type = null, array $attrGroups = []) {
|
public function __construct(int $flags, array $props, array $attributes = [], $type = null, array $attrGroups = []) {
|
||||||
@@ -77,6 +76,15 @@ class Property extends Node\Stmt
|
|||||||
return (bool) ($this->flags & Class_::MODIFIER_STATIC);
|
return (bool) ($this->flags & Class_::MODIFIER_STATIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the property is readonly.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isReadonly() : bool {
|
||||||
|
return (bool) ($this->flags & Class_::MODIFIER_READONLY);
|
||||||
|
}
|
||||||
|
|
||||||
public function getType() : string {
|
public function getType() : string {
|
||||||
return 'Stmt_Property';
|
return 'Stmt_Property';
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ class PropertyProperty extends Node\Stmt
|
|||||||
* @param null|Node\Expr $default Default value
|
* @param null|Node\Expr $default Default value
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct($name, Node\Expr $default = null, array $attributes = []) {
|
public function __construct($name, ?Node\Expr $default = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name;
|
$this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name;
|
||||||
$this->default = $default;
|
$this->default = $default;
|
||||||
|
@@ -15,7 +15,7 @@ class Return_ extends Node\Stmt
|
|||||||
* @param null|Node\Expr $expr Expression
|
* @param null|Node\Expr $expr Expression
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(Node\Expr $expr = null, array $attributes = []) {
|
public function __construct(?Node\Expr $expr = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->expr = $expr;
|
$this->expr = $expr;
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ class StaticVar extends Node\Stmt
|
|||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Expr\Variable $var, Node\Expr $default = null, array $attributes = []
|
Expr\Variable $var, ?Node\Expr $default = null, array $attributes = []
|
||||||
) {
|
) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->var = $var;
|
$this->var = $var;
|
||||||
|
@@ -21,7 +21,7 @@ class TryCatch extends Node\Stmt
|
|||||||
* @param null|Finally_ $finally Optional finally node
|
* @param null|Finally_ $finally Optional finally node
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(array $stmts, array $catches, Finally_ $finally = null, array $attributes = []) {
|
public function __construct(array $stmts, array $catches, ?Finally_ $finally = null, array $attributes = []) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$this->stmts = $stmts;
|
$this->stmts = $stmts;
|
||||||
$this->catches = $catches;
|
$this->catches = $catches;
|
||||||
|
@@ -2,17 +2,15 @@
|
|||||||
|
|
||||||
namespace PhpParser\Node;
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
use PhpParser\NodeAbstract;
|
class UnionType extends ComplexType
|
||||||
|
|
||||||
class UnionType extends NodeAbstract
|
|
||||||
{
|
{
|
||||||
/** @var (Identifier|Name)[] Types */
|
/** @var (Identifier|Name|IntersectionType)[] Types */
|
||||||
public $types;
|
public $types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a union type.
|
* Constructs a union type.
|
||||||
*
|
*
|
||||||
* @param (Identifier|Name)[] $types Types
|
* @param (Identifier|Name|IntersectionType)[] $types Types
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(array $types, array $attributes = []) {
|
public function __construct(array $types, array $attributes = []) {
|
||||||
|
27
lib/PhpParser/Node/VariadicPlaceholder.php
Normal file
27
lib/PhpParser/Node/VariadicPlaceholder.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
|
use PhpParser\NodeAbstract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the "..." in "foo(...)" of the first-class callable syntax.
|
||||||
|
*/
|
||||||
|
class VariadicPlaceholder extends NodeAbstract {
|
||||||
|
/**
|
||||||
|
* Create a variadic argument placeholder (first-class callable syntax).
|
||||||
|
*
|
||||||
|
* @param array $attributes Additional attributes
|
||||||
|
*/
|
||||||
|
public function __construct(array $attributes = []) {
|
||||||
|
$this->attributes = $attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string {
|
||||||
|
return 'VariadicPlaceholder';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubNodeNames(): array {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@@ -39,7 +39,7 @@ class NodeDumper
|
|||||||
*
|
*
|
||||||
* @return string Dumped value
|
* @return string Dumped value
|
||||||
*/
|
*/
|
||||||
public function dump($node, string $code = null) : string {
|
public function dump($node, ?string $code = null) : string {
|
||||||
$this->code = $code;
|
$this->code = $code;
|
||||||
return $this->dumpRecursive($node);
|
return $this->dumpRecursive($node);
|
||||||
}
|
}
|
||||||
@@ -128,6 +128,9 @@ class NodeDumper
|
|||||||
if ($flags & Class_::MODIFIER_FINAL) {
|
if ($flags & Class_::MODIFIER_FINAL) {
|
||||||
$strs[] = 'MODIFIER_FINAL';
|
$strs[] = 'MODIFIER_FINAL';
|
||||||
}
|
}
|
||||||
|
if ($flags & Class_::MODIFIER_READONLY) {
|
||||||
|
$strs[] = 'MODIFIER_READONLY';
|
||||||
|
}
|
||||||
|
|
||||||
if ($strs) {
|
if ($strs) {
|
||||||
return implode(' | ', $strs) . ' (' . $flags . ')';
|
return implode(' | ', $strs) . ' (' . $flags . ')';
|
||||||
|
@@ -35,7 +35,7 @@ class NameResolver extends NodeVisitorAbstract
|
|||||||
* @param ErrorHandler|null $errorHandler Error handler
|
* @param ErrorHandler|null $errorHandler Error handler
|
||||||
* @param array $options Options
|
* @param array $options Options
|
||||||
*/
|
*/
|
||||||
public function __construct(ErrorHandler $errorHandler = null, array $options = []) {
|
public function __construct(?ErrorHandler $errorHandler = null, array $options = []) {
|
||||||
$this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing);
|
$this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing);
|
||||||
$this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false;
|
$this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false;
|
||||||
$this->replaceNodes = $options['replaceNodes'] ?? true;
|
$this->replaceNodes = $options['replaceNodes'] ?? true;
|
||||||
@@ -86,6 +86,15 @@ class NameResolver extends NodeVisitorAbstract
|
|||||||
|
|
||||||
$this->resolveAttrGroups($node);
|
$this->resolveAttrGroups($node);
|
||||||
$this->addNamespacedName($node);
|
$this->addNamespacedName($node);
|
||||||
|
} elseif ($node instanceof Stmt\Enum_) {
|
||||||
|
foreach ($node->implements as &$interface) {
|
||||||
|
$interface = $this->resolveClassName($interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->resolveAttrGroups($node);
|
||||||
|
if (null !== $node->name) {
|
||||||
|
$this->addNamespacedName($node);
|
||||||
|
}
|
||||||
} elseif ($node instanceof Stmt\Trait_) {
|
} elseif ($node instanceof Stmt\Trait_) {
|
||||||
$this->resolveAttrGroups($node);
|
$this->resolveAttrGroups($node);
|
||||||
$this->addNamespacedName($node);
|
$this->addNamespacedName($node);
|
||||||
@@ -109,6 +118,11 @@ class NameResolver extends NodeVisitorAbstract
|
|||||||
$this->addNamespacedName($const);
|
$this->addNamespacedName($const);
|
||||||
}
|
}
|
||||||
} else if ($node instanceof Stmt\ClassConst) {
|
} else if ($node instanceof Stmt\ClassConst) {
|
||||||
|
if (null !== $node->type) {
|
||||||
|
$node->type = $this->resolveType($node->type);
|
||||||
|
}
|
||||||
|
$this->resolveAttrGroups($node);
|
||||||
|
} else if ($node instanceof Stmt\EnumCase) {
|
||||||
$this->resolveAttrGroups($node);
|
$this->resolveAttrGroups($node);
|
||||||
} elseif ($node instanceof Expr\StaticCall
|
} elseif ($node instanceof Expr\StaticCall
|
||||||
|| $node instanceof Expr\StaticPropertyFetch
|
|| $node instanceof Expr\StaticPropertyFetch
|
||||||
@@ -150,7 +164,7 @@ class NameResolver extends NodeVisitorAbstract
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) {
|
private function addAlias(Stmt\UseUse $use, int $type, ?Name $prefix = null) {
|
||||||
// Add prefix for group uses
|
// Add prefix for group uses
|
||||||
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
|
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
|
||||||
// Type is determined either by individual element or whole use declaration
|
// Type is determined either by individual element or whole use declaration
|
||||||
@@ -178,7 +192,7 @@ class NameResolver extends NodeVisitorAbstract
|
|||||||
$node->type = $this->resolveType($node->type);
|
$node->type = $this->resolveType($node->type);
|
||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
if ($node instanceof Node\UnionType) {
|
if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) {
|
||||||
foreach ($node->types as &$type) {
|
foreach ($node->types as &$type) {
|
||||||
$type = $this->resolveType($type);
|
$type = $this->resolveType($type);
|
||||||
}
|
}
|
||||||
|
@@ -14,5 +14,5 @@ interface Parser
|
|||||||
* @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and
|
* @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and
|
||||||
* the parser was unable to recover from an error).
|
* the parser was unable to recover from an error).
|
||||||
*/
|
*/
|
||||||
public function parse(string $code, ErrorHandler $errorHandler = null);
|
public function parse(string $code, ?ErrorHandler $errorHandler = null);
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ class Multiple implements Parser
|
|||||||
$this->parsers = $parsers;
|
$this->parsers = $parsers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parse(string $code, ErrorHandler $errorHandler = null) {
|
public function parse(string $code, ?ErrorHandler $errorHandler = null) {
|
||||||
if (null === $errorHandler) {
|
if (null === $errorHandler) {
|
||||||
$errorHandler = new ErrorHandler\Throwing;
|
$errorHandler = new ErrorHandler\Throwing;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2791
lib/PhpParser/Parser/Php8.php
Normal file
2791
lib/PhpParser/Parser/Php8.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -35,110 +35,114 @@ final class Tokens
|
|||||||
const T_COALESCE = 283;
|
const T_COALESCE = 283;
|
||||||
const T_BOOLEAN_OR = 284;
|
const T_BOOLEAN_OR = 284;
|
||||||
const T_BOOLEAN_AND = 285;
|
const T_BOOLEAN_AND = 285;
|
||||||
const T_IS_EQUAL = 286;
|
const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 286;
|
||||||
const T_IS_NOT_EQUAL = 287;
|
const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 287;
|
||||||
const T_IS_IDENTICAL = 288;
|
const T_IS_EQUAL = 288;
|
||||||
const T_IS_NOT_IDENTICAL = 289;
|
const T_IS_NOT_EQUAL = 289;
|
||||||
const T_SPACESHIP = 290;
|
const T_IS_IDENTICAL = 290;
|
||||||
const T_IS_SMALLER_OR_EQUAL = 291;
|
const T_IS_NOT_IDENTICAL = 291;
|
||||||
const T_IS_GREATER_OR_EQUAL = 292;
|
const T_SPACESHIP = 292;
|
||||||
const T_SL = 293;
|
const T_IS_SMALLER_OR_EQUAL = 293;
|
||||||
const T_SR = 294;
|
const T_IS_GREATER_OR_EQUAL = 294;
|
||||||
const T_INSTANCEOF = 295;
|
const T_SL = 295;
|
||||||
const T_INC = 296;
|
const T_SR = 296;
|
||||||
const T_DEC = 297;
|
const T_INSTANCEOF = 297;
|
||||||
const T_INT_CAST = 298;
|
const T_INC = 298;
|
||||||
const T_DOUBLE_CAST = 299;
|
const T_DEC = 299;
|
||||||
const T_STRING_CAST = 300;
|
const T_INT_CAST = 300;
|
||||||
const T_ARRAY_CAST = 301;
|
const T_DOUBLE_CAST = 301;
|
||||||
const T_OBJECT_CAST = 302;
|
const T_STRING_CAST = 302;
|
||||||
const T_BOOL_CAST = 303;
|
const T_ARRAY_CAST = 303;
|
||||||
const T_UNSET_CAST = 304;
|
const T_OBJECT_CAST = 304;
|
||||||
const T_POW = 305;
|
const T_BOOL_CAST = 305;
|
||||||
const T_NEW = 306;
|
const T_UNSET_CAST = 306;
|
||||||
const T_CLONE = 307;
|
const T_POW = 307;
|
||||||
const T_EXIT = 308;
|
const T_NEW = 308;
|
||||||
const T_IF = 309;
|
const T_CLONE = 309;
|
||||||
const T_ELSEIF = 310;
|
const T_EXIT = 310;
|
||||||
const T_ELSE = 311;
|
const T_IF = 311;
|
||||||
const T_ENDIF = 312;
|
const T_ELSEIF = 312;
|
||||||
const T_LNUMBER = 313;
|
const T_ELSE = 313;
|
||||||
const T_DNUMBER = 314;
|
const T_ENDIF = 314;
|
||||||
const T_STRING = 315;
|
const T_LNUMBER = 315;
|
||||||
const T_STRING_VARNAME = 316;
|
const T_DNUMBER = 316;
|
||||||
const T_VARIABLE = 317;
|
const T_STRING = 317;
|
||||||
const T_NUM_STRING = 318;
|
const T_STRING_VARNAME = 318;
|
||||||
const T_INLINE_HTML = 319;
|
const T_VARIABLE = 319;
|
||||||
const T_ENCAPSED_AND_WHITESPACE = 320;
|
const T_NUM_STRING = 320;
|
||||||
const T_CONSTANT_ENCAPSED_STRING = 321;
|
const T_INLINE_HTML = 321;
|
||||||
const T_ECHO = 322;
|
const T_ENCAPSED_AND_WHITESPACE = 322;
|
||||||
const T_DO = 323;
|
const T_CONSTANT_ENCAPSED_STRING = 323;
|
||||||
const T_WHILE = 324;
|
const T_ECHO = 324;
|
||||||
const T_ENDWHILE = 325;
|
const T_DO = 325;
|
||||||
const T_FOR = 326;
|
const T_WHILE = 326;
|
||||||
const T_ENDFOR = 327;
|
const T_ENDWHILE = 327;
|
||||||
const T_FOREACH = 328;
|
const T_FOR = 328;
|
||||||
const T_ENDFOREACH = 329;
|
const T_ENDFOR = 329;
|
||||||
const T_DECLARE = 330;
|
const T_FOREACH = 330;
|
||||||
const T_ENDDECLARE = 331;
|
const T_ENDFOREACH = 331;
|
||||||
const T_AS = 332;
|
const T_DECLARE = 332;
|
||||||
const T_SWITCH = 333;
|
const T_ENDDECLARE = 333;
|
||||||
const T_MATCH = 334;
|
const T_AS = 334;
|
||||||
const T_ENDSWITCH = 335;
|
const T_SWITCH = 335;
|
||||||
const T_CASE = 336;
|
const T_MATCH = 336;
|
||||||
const T_DEFAULT = 337;
|
const T_ENDSWITCH = 337;
|
||||||
const T_BREAK = 338;
|
const T_CASE = 338;
|
||||||
const T_CONTINUE = 339;
|
const T_DEFAULT = 339;
|
||||||
const T_GOTO = 340;
|
const T_BREAK = 340;
|
||||||
const T_FUNCTION = 341;
|
const T_CONTINUE = 341;
|
||||||
const T_FN = 342;
|
const T_GOTO = 342;
|
||||||
const T_CONST = 343;
|
const T_FUNCTION = 343;
|
||||||
const T_RETURN = 344;
|
const T_FN = 344;
|
||||||
const T_TRY = 345;
|
const T_CONST = 345;
|
||||||
const T_CATCH = 346;
|
const T_RETURN = 346;
|
||||||
const T_FINALLY = 347;
|
const T_TRY = 347;
|
||||||
const T_USE = 348;
|
const T_CATCH = 348;
|
||||||
const T_INSTEADOF = 349;
|
const T_FINALLY = 349;
|
||||||
const T_GLOBAL = 350;
|
const T_USE = 350;
|
||||||
const T_STATIC = 351;
|
const T_INSTEADOF = 351;
|
||||||
const T_ABSTRACT = 352;
|
const T_GLOBAL = 352;
|
||||||
const T_FINAL = 353;
|
const T_STATIC = 353;
|
||||||
const T_PRIVATE = 354;
|
const T_ABSTRACT = 354;
|
||||||
const T_PROTECTED = 355;
|
const T_FINAL = 355;
|
||||||
const T_PUBLIC = 356;
|
const T_PRIVATE = 356;
|
||||||
const T_VAR = 357;
|
const T_PROTECTED = 357;
|
||||||
const T_UNSET = 358;
|
const T_PUBLIC = 358;
|
||||||
const T_ISSET = 359;
|
const T_READONLY = 359;
|
||||||
const T_EMPTY = 360;
|
const T_VAR = 360;
|
||||||
const T_HALT_COMPILER = 361;
|
const T_UNSET = 361;
|
||||||
const T_CLASS = 362;
|
const T_ISSET = 362;
|
||||||
const T_TRAIT = 363;
|
const T_EMPTY = 363;
|
||||||
const T_INTERFACE = 364;
|
const T_HALT_COMPILER = 364;
|
||||||
const T_EXTENDS = 365;
|
const T_CLASS = 365;
|
||||||
const T_IMPLEMENTS = 366;
|
const T_TRAIT = 366;
|
||||||
const T_OBJECT_OPERATOR = 367;
|
const T_INTERFACE = 367;
|
||||||
const T_NULLSAFE_OBJECT_OPERATOR = 368;
|
const T_ENUM = 368;
|
||||||
const T_LIST = 369;
|
const T_EXTENDS = 369;
|
||||||
const T_ARRAY = 370;
|
const T_IMPLEMENTS = 370;
|
||||||
const T_CALLABLE = 371;
|
const T_OBJECT_OPERATOR = 371;
|
||||||
const T_CLASS_C = 372;
|
const T_NULLSAFE_OBJECT_OPERATOR = 372;
|
||||||
const T_TRAIT_C = 373;
|
const T_LIST = 373;
|
||||||
const T_METHOD_C = 374;
|
const T_ARRAY = 374;
|
||||||
const T_FUNC_C = 375;
|
const T_CALLABLE = 375;
|
||||||
const T_LINE = 376;
|
const T_CLASS_C = 376;
|
||||||
const T_FILE = 377;
|
const T_TRAIT_C = 377;
|
||||||
const T_START_HEREDOC = 378;
|
const T_METHOD_C = 378;
|
||||||
const T_END_HEREDOC = 379;
|
const T_FUNC_C = 379;
|
||||||
const T_DOLLAR_OPEN_CURLY_BRACES = 380;
|
const T_LINE = 380;
|
||||||
const T_CURLY_OPEN = 381;
|
const T_FILE = 381;
|
||||||
const T_PAAMAYIM_NEKUDOTAYIM = 382;
|
const T_START_HEREDOC = 382;
|
||||||
const T_NAMESPACE = 383;
|
const T_END_HEREDOC = 383;
|
||||||
const T_NS_C = 384;
|
const T_DOLLAR_OPEN_CURLY_BRACES = 384;
|
||||||
const T_DIR = 385;
|
const T_CURLY_OPEN = 385;
|
||||||
const T_NS_SEPARATOR = 386;
|
const T_PAAMAYIM_NEKUDOTAYIM = 386;
|
||||||
const T_ELLIPSIS = 387;
|
const T_NAMESPACE = 387;
|
||||||
const T_NAME_FULLY_QUALIFIED = 388;
|
const T_NS_C = 388;
|
||||||
const T_NAME_QUALIFIED = 389;
|
const T_DIR = 389;
|
||||||
const T_NAME_RELATIVE = 390;
|
const T_NS_SEPARATOR = 390;
|
||||||
const T_ATTRIBUTE = 391;
|
const T_ELLIPSIS = 391;
|
||||||
|
const T_NAME_FULLY_QUALIFIED = 392;
|
||||||
|
const T_NAME_QUALIFIED = 393;
|
||||||
|
const T_NAME_RELATIVE = 394;
|
||||||
|
const T_ATTRIBUTE = 395;
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,12 @@ use PhpParser\Node\Scalar\String_;
|
|||||||
use PhpParser\Node\Stmt\Class_;
|
use PhpParser\Node\Stmt\Class_;
|
||||||
use PhpParser\Node\Stmt\ClassConst;
|
use PhpParser\Node\Stmt\ClassConst;
|
||||||
use PhpParser\Node\Stmt\ClassMethod;
|
use PhpParser\Node\Stmt\ClassMethod;
|
||||||
|
use PhpParser\Node\Stmt\Else_;
|
||||||
|
use PhpParser\Node\Stmt\ElseIf_;
|
||||||
|
use PhpParser\Node\Stmt\Enum_;
|
||||||
use PhpParser\Node\Stmt\Interface_;
|
use PhpParser\Node\Stmt\Interface_;
|
||||||
use PhpParser\Node\Stmt\Namespace_;
|
use PhpParser\Node\Stmt\Namespace_;
|
||||||
|
use PhpParser\Node\Stmt\Nop;
|
||||||
use PhpParser\Node\Stmt\Property;
|
use PhpParser\Node\Stmt\Property;
|
||||||
use PhpParser\Node\Stmt\TryCatch;
|
use PhpParser\Node\Stmt\TryCatch;
|
||||||
use PhpParser\Node\Stmt\UseUse;
|
use PhpParser\Node\Stmt\UseUse;
|
||||||
@@ -60,7 +64,7 @@ abstract class ParserAbstract implements Parser
|
|||||||
|
|
||||||
/** @var int[] Map of states to a displacement into the $action table. The corresponding action for this
|
/** @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
|
* 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;
|
protected $actionBase;
|
||||||
/** @var int[] Table of actions. Indexed according to $actionBase comment. */
|
/** @var int[] Table of actions. Indexed according to $actionBase comment. */
|
||||||
protected $action;
|
protected $action;
|
||||||
@@ -151,7 +155,7 @@ abstract class ParserAbstract implements Parser
|
|||||||
* @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and
|
* @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and
|
||||||
* the parser was unable to recover from an error).
|
* the parser was unable to recover from an error).
|
||||||
*/
|
*/
|
||||||
public function parse(string $code, ErrorHandler $errorHandler = null) {
|
public function parse(string $code, ?ErrorHandler $errorHandler = null) {
|
||||||
$this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing;
|
$this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing;
|
||||||
|
|
||||||
$this->lexer->startLexing($code, $this->errorHandler);
|
$this->lexer->startLexing($code, $this->errorHandler);
|
||||||
@@ -219,10 +223,7 @@ abstract class ParserAbstract implements Parser
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get
|
// Allow productions to access the start attributes of the lookahead token.
|
||||||
// the attributes of the next token, even though they don't contain it themselves.
|
|
||||||
$this->startAttributeStack[$stackPos+1] = $startAttributes;
|
|
||||||
$this->endAttributeStack[$stackPos+1] = $endAttributes;
|
|
||||||
$this->lookaheadStartAttributes = $startAttributes;
|
$this->lookaheadStartAttributes = $startAttributes;
|
||||||
|
|
||||||
//$this->traceRead($symbol);
|
//$this->traceRead($symbol);
|
||||||
@@ -294,7 +295,8 @@ abstract class ParserAbstract implements Parser
|
|||||||
|
|
||||||
/* Goto - shift nonterminal */
|
/* Goto - shift nonterminal */
|
||||||
$lastEndAttributes = $this->endAttributeStack[$stackPos];
|
$lastEndAttributes = $this->endAttributeStack[$stackPos];
|
||||||
$stackPos -= $this->ruleToLength[$rule];
|
$ruleLength = $this->ruleToLength[$rule];
|
||||||
|
$stackPos -= $ruleLength;
|
||||||
$nonTerminal = $this->ruleToNonTerminal[$rule];
|
$nonTerminal = $this->ruleToNonTerminal[$rule];
|
||||||
$idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos];
|
$idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos];
|
||||||
if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) {
|
if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) {
|
||||||
@@ -307,6 +309,10 @@ abstract class ParserAbstract implements Parser
|
|||||||
$stateStack[$stackPos] = $state;
|
$stateStack[$stackPos] = $state;
|
||||||
$this->semStack[$stackPos] = $this->semValue;
|
$this->semStack[$stackPos] = $this->semValue;
|
||||||
$this->endAttributeStack[$stackPos] = $lastEndAttributes;
|
$this->endAttributeStack[$stackPos] = $lastEndAttributes;
|
||||||
|
if ($ruleLength === 0) {
|
||||||
|
// Empty productions use the start attributes of the lookahead token.
|
||||||
|
$this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* error */
|
/* error */
|
||||||
switch ($this->errorState) {
|
switch ($this->errorState) {
|
||||||
@@ -340,6 +346,7 @@ abstract class ParserAbstract implements Parser
|
|||||||
|
|
||||||
// We treat the error symbol as being empty, so we reset the end attributes
|
// We treat the error symbol as being empty, so we reset the end attributes
|
||||||
// to the end attributes of the last non-error symbol
|
// to the end attributes of the last non-error symbol
|
||||||
|
$this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes;
|
||||||
$this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1];
|
$this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1];
|
||||||
$this->endAttributes = $this->endAttributeStack[$stackPos - 1];
|
$this->endAttributes = $this->endAttributeStack[$stackPos - 1];
|
||||||
break;
|
break;
|
||||||
@@ -659,6 +666,8 @@ abstract class ParserAbstract implements Parser
|
|||||||
'null' => true,
|
'null' => true,
|
||||||
'false' => true,
|
'false' => true,
|
||||||
'mixed' => true,
|
'mixed' => true,
|
||||||
|
'never' => true,
|
||||||
|
'true' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!$name->isUnqualified()) {
|
if (!$name->isUnqualified()) {
|
||||||
@@ -870,6 +879,33 @@ abstract class ParserAbstract implements Parser
|
|||||||
return $attributes;
|
return $attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param ElseIf_|Else_ $node */
|
||||||
|
protected function fixupAlternativeElse($node) {
|
||||||
|
// Make sure a trailing nop statement carrying comments is part of the node.
|
||||||
|
$numStmts = \count($node->stmts);
|
||||||
|
if ($numStmts !== 0 && $node->stmts[$numStmts - 1] instanceof Nop) {
|
||||||
|
$nopAttrs = $node->stmts[$numStmts - 1]->getAttributes();
|
||||||
|
if (isset($nopAttrs['endLine'])) {
|
||||||
|
$node->setAttribute('endLine', $nopAttrs['endLine']);
|
||||||
|
}
|
||||||
|
if (isset($nopAttrs['endFilePos'])) {
|
||||||
|
$node->setAttribute('endFilePos', $nopAttrs['endFilePos']);
|
||||||
|
}
|
||||||
|
if (isset($nopAttrs['endTokenPos'])) {
|
||||||
|
$node->setAttribute('endTokenPos', $nopAttrs['endTokenPos']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkClassModifier($a, $b, $modifierPos) {
|
||||||
|
try {
|
||||||
|
Class_::verifyClassModifier($a, $b);
|
||||||
|
} catch (Error $error) {
|
||||||
|
$error->setAttributes($this->getAttributesAt($modifierPos));
|
||||||
|
$this->emitError($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function checkModifier($a, $b, $modifierPos) {
|
protected function checkModifier($a, $b, $modifierPos) {
|
||||||
// Jumping through some hoops here because verifyModifier() is also used elsewhere
|
// Jumping through some hoops here because verifyModifier() is also used elsewhere
|
||||||
try {
|
try {
|
||||||
@@ -909,13 +945,28 @@ abstract class ParserAbstract implements Parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkClass(Class_ $node, $namePos) {
|
private function checkClassName($name, $namePos) {
|
||||||
if (null !== $node->name && $node->name->isSpecialClassName()) {
|
if (null !== $name && $name->isSpecialClassName()) {
|
||||||
$this->emitError(new Error(
|
$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)
|
$this->getAttributesAt($namePos)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
$interface->getAttributes()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkClass(Class_ $node, $namePos) {
|
||||||
|
$this->checkClassName($node->name, $namePos);
|
||||||
|
|
||||||
if ($node->extends && $node->extends->isSpecialClassName()) {
|
if ($node->extends && $node->extends->isSpecialClassName()) {
|
||||||
$this->emitError(new Error(
|
$this->emitError(new Error(
|
||||||
@@ -924,32 +975,17 @@ abstract class ParserAbstract implements Parser
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($node->implements as $interface) {
|
$this->checkImplementedInterfaces($node->implements);
|
||||||
if ($interface->isSpecialClassName()) {
|
|
||||||
$this->emitError(new Error(
|
|
||||||
sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
|
|
||||||
$interface->getAttributes()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkInterface(Interface_ $node, $namePos) {
|
protected function checkInterface(Interface_ $node, $namePos) {
|
||||||
if (null !== $node->name && $node->name->isSpecialClassName()) {
|
$this->checkClassName($node->name, $namePos);
|
||||||
$this->emitError(new Error(
|
$this->checkImplementedInterfaces($node->extends);
|
||||||
sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name),
|
|
||||||
$this->getAttributesAt($namePos)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($node->extends as $interface) {
|
protected function checkEnum(Enum_ $node, $namePos) {
|
||||||
if ($interface->isSpecialClassName()) {
|
$this->checkClassName($node->name, $namePos);
|
||||||
$this->emitError(new Error(
|
$this->checkImplementedInterfaces($node->implements);
|
||||||
sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
|
|
||||||
$interface->getAttributes()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkClassMethod(ClassMethod $node, $modifierPos) {
|
protected function checkClassMethod(ClassMethod $node, $modifierPos) {
|
||||||
@@ -972,6 +1008,12 @@ abstract class ParserAbstract implements Parser
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($node->flags & Class_::MODIFIER_READONLY) {
|
||||||
|
$this->emitError(new Error(
|
||||||
|
sprintf('Method %s() cannot be readonly', $node->name),
|
||||||
|
$this->getAttributesAt($modifierPos)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkClassConst(ClassConst $node, $modifierPos) {
|
protected function checkClassConst(ClassConst $node, $modifierPos) {
|
||||||
@@ -985,9 +1027,9 @@ abstract class ParserAbstract implements Parser
|
|||||||
"Cannot use 'abstract' as constant modifier",
|
"Cannot use 'abstract' as constant modifier",
|
||||||
$this->getAttributesAt($modifierPos)));
|
$this->getAttributesAt($modifierPos)));
|
||||||
}
|
}
|
||||||
if ($node->flags & Class_::MODIFIER_FINAL) {
|
if ($node->flags & Class_::MODIFIER_READONLY) {
|
||||||
$this->emitError(new Error(
|
$this->emitError(new Error(
|
||||||
"Cannot use 'final' as constant modifier",
|
"Cannot use 'readonly' as constant modifier",
|
||||||
$this->getAttributesAt($modifierPos)));
|
$this->getAttributesAt($modifierPos)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace PhpParser;
|
namespace PhpParser;
|
||||||
|
|
||||||
|
use PhpParser\Lexer\Emulative;
|
||||||
|
use PhpParser\Parser\Php7;
|
||||||
|
|
||||||
class ParserFactory
|
class ParserFactory
|
||||||
{
|
{
|
||||||
const PREFER_PHP7 = 1;
|
const PREFER_PHP7 = 1;
|
||||||
@@ -18,7 +21,7 @@ class ParserFactory
|
|||||||
*
|
*
|
||||||
* @return Parser The parser instance
|
* @return Parser The parser instance
|
||||||
*/
|
*/
|
||||||
public function create(int $kind, Lexer $lexer = null, array $parserOptions = []) : Parser {
|
public function create(int $kind, ?Lexer $lexer = null, array $parserOptions = []) : Parser {
|
||||||
if (null === $lexer) {
|
if (null === $lexer) {
|
||||||
$lexer = new Lexer\Emulative();
|
$lexer = new Lexer\Emulative();
|
||||||
}
|
}
|
||||||
@@ -41,4 +44,33 @@ class ParserFactory
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a parser targeting the newest version supported by this library. Code for older
|
||||||
|
* versions will be accepted if there have been no relevant backwards-compatibility breaks in
|
||||||
|
* PHP.
|
||||||
|
*
|
||||||
|
* All supported lexer attributes (comments, startLine, endLine, startTokenPos, endTokenPos,
|
||||||
|
* startFilePos, endFilePos) will be enabled.
|
||||||
|
*/
|
||||||
|
public function createForNewestSupportedVersion(): Parser {
|
||||||
|
return new Php7(new Emulative($this->getLexerOptions()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a parser targeting the host PHP version, that is the PHP version we're currently
|
||||||
|
* running on. This parser will not use any token emulation.
|
||||||
|
*
|
||||||
|
* All supported lexer attributes (comments, startLine, endLine, startTokenPos, endTokenPos,
|
||||||
|
* startFilePos, endFilePos) will be enabled.
|
||||||
|
*/
|
||||||
|
public function createForHostVersion(): Parser {
|
||||||
|
return new Php7(new Lexer($this->getLexerOptions()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getLexerOptions(): array {
|
||||||
|
return ['usedAttributes' => [
|
||||||
|
'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos', 'startFilePos', 'endFilePos',
|
||||||
|
]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,10 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
. $this->p($node->value);
|
. $this->p($node->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node) {
|
||||||
|
return '...';
|
||||||
|
}
|
||||||
|
|
||||||
protected function pConst(Node\Const_ $node) {
|
protected function pConst(Node\Const_ $node) {
|
||||||
return $node->name . ' = ' . $this->p($node->value);
|
return $node->name . ' = ' . $this->p($node->value);
|
||||||
}
|
}
|
||||||
@@ -42,7 +46,19 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function pUnionType(Node\UnionType $node) {
|
protected function pUnionType(Node\UnionType $node) {
|
||||||
return $this->pImplode($node->types, '|');
|
$types = [];
|
||||||
|
foreach ($node->types as $typeNode) {
|
||||||
|
if ($typeNode instanceof Node\IntersectionType) {
|
||||||
|
$types[] = '('. $this->p($typeNode) . ')';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$types[] = $this->p($typeNode);
|
||||||
|
}
|
||||||
|
return implode('|', $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function pIntersectionType(Node\IntersectionType $node) {
|
||||||
|
return $this->pImplode($node->types, '&');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function pIdentifier(Node\Identifier $node) {
|
protected function pIdentifier(Node\Identifier $node) {
|
||||||
@@ -513,7 +529,7 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function pExpr_StaticCall(Expr\StaticCall $node) {
|
protected function pExpr_StaticCall(Expr\StaticCall $node) {
|
||||||
return $this->pDereferenceLhs($node->class) . '::'
|
return $this->pStaticDereferenceLhs($node->class) . '::'
|
||||||
. ($node->name instanceof Expr
|
. ($node->name instanceof Expr
|
||||||
? ($node->name instanceof Expr\Variable
|
? ($node->name instanceof Expr\Variable
|
||||||
? $this->p($node->name)
|
? $this->p($node->name)
|
||||||
@@ -590,7 +606,7 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
|
protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
|
||||||
return $this->pDereferenceLhs($node->class) . '::' . $this->p($node->name);
|
return $this->pStaticDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
|
protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
|
||||||
@@ -602,7 +618,7 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) {
|
protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) {
|
||||||
return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
|
return $this->pStaticDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function pExpr_ShellExec(Expr\ShellExec $node) {
|
protected function pExpr_ShellExec(Expr\ShellExec $node) {
|
||||||
@@ -727,6 +743,14 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function pStmt_Enum(Stmt\Enum_ $node) {
|
||||||
|
return $this->pAttrGroups($node->attrGroups)
|
||||||
|
. 'enum ' . $node->name
|
||||||
|
. ($node->scalarType ? " : $node->scalarType" : '')
|
||||||
|
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
|
||||||
|
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||||
|
}
|
||||||
|
|
||||||
protected function pStmt_Class(Stmt\Class_ $node) {
|
protected function pStmt_Class(Stmt\Class_ $node) {
|
||||||
return $this->pClassCommon($node, ' ' . $node->name);
|
return $this->pClassCommon($node, ' ' . $node->name);
|
||||||
}
|
}
|
||||||
@@ -737,6 +761,13 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
. $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) {
|
protected function pStmt_TraitUse(Stmt\TraitUse $node) {
|
||||||
return 'use ' . $this->pCommaSeparated($node->traits)
|
return 'use ' . $this->pCommaSeparated($node->traits)
|
||||||
. (empty($node->adaptations)
|
. (empty($node->adaptations)
|
||||||
@@ -783,7 +814,9 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
protected function pStmt_ClassConst(Stmt\ClassConst $node) {
|
protected function pStmt_ClassConst(Stmt\ClassConst $node) {
|
||||||
return $this->pAttrGroups($node->attrGroups)
|
return $this->pAttrGroups($node->attrGroups)
|
||||||
. $this->pModifiers($node->flags)
|
. $this->pModifiers($node->flags)
|
||||||
. 'const ' . $this->pCommaSeparated($node->consts) . ';';
|
. 'const '
|
||||||
|
. (null !== $node->type ? $this->p($node->type) . ' ' : '')
|
||||||
|
. $this->pCommaSeparated($node->consts) . ';';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function pStmt_Function(Stmt\Function_ $node) {
|
protected function pStmt_Function(Stmt\Function_ $node) {
|
||||||
@@ -984,14 +1017,27 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
$escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
|
$escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escape other control characters
|
// Escape control characters and non-UTF-8 characters.
|
||||||
return preg_replace_callback('/([\0-\10\16-\37])(?=([0-7]?))/', function ($matches) {
|
// Regex based on https://stackoverflow.com/a/11709412/385378.
|
||||||
$oct = decoct(ord($matches[1]));
|
$regex = '/(
|
||||||
if ($matches[2] !== '') {
|
[\x00-\x08\x0E-\x1F] # Control characters
|
||||||
// If there is a trailing digit, use the full three character form
|
| [\xC0-\xC1] # Invalid UTF-8 Bytes
|
||||||
return '\\' . str_pad($oct, 3, '0', \STR_PAD_LEFT);
|
| [\xF5-\xFF] # Invalid UTF-8 Bytes
|
||||||
}
|
| \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point
|
||||||
return '\\' . $oct;
|
| \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);
|
}, $escaped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1023,6 +1069,14 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function pStaticDereferenceLhs(Node $node) {
|
||||||
|
if (!$this->staticDereferenceLhsRequiresParens($node)) {
|
||||||
|
return $this->p($node);
|
||||||
|
} else {
|
||||||
|
return '(' . $this->p($node) . ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function pCallLhs(Node $node) {
|
protected function pCallLhs(Node $node) {
|
||||||
if (!$this->callLhsRequiresParens($node)) {
|
if (!$this->callLhsRequiresParens($node)) {
|
||||||
return $this->p($node);
|
return $this->p($node);
|
||||||
@@ -1031,16 +1085,19 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function pNewVariable(Node $node) {
|
protected function pNewVariable(Node $node): string {
|
||||||
// TODO: This is not fully accurate.
|
if (!$this->newOperandRequiresParens($node)) {
|
||||||
return $this->pDereferenceLhs($node);
|
return $this->p($node);
|
||||||
|
} else {
|
||||||
|
return '(' . $this->p($node) . ')';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Node[] $nodes
|
* @param Node[] $nodes
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function hasNodeWithComments(array $nodes) {
|
protected function hasNodeWithComments(array $nodes) {
|
||||||
foreach ($nodes as $node) {
|
foreach ($nodes as $node) {
|
||||||
if ($node && $node->getComments()) {
|
if ($node && $node->getComments()) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1049,7 +1106,7 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function pMaybeMultiline(array $nodes, bool $trailingComma = false) {
|
protected function pMaybeMultiline(array $nodes, bool $trailingComma = false) {
|
||||||
if (!$this->hasNodeWithComments($nodes)) {
|
if (!$this->hasNodeWithComments($nodes)) {
|
||||||
return $this->pCommaSeparated($nodes);
|
return $this->pCommaSeparated($nodes);
|
||||||
} else {
|
} else {
|
||||||
@@ -1057,7 +1114,7 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function pAttrGroups(array $nodes, bool $inline = false): string {
|
protected function pAttrGroups(array $nodes, bool $inline = false): string {
|
||||||
$result = '';
|
$result = '';
|
||||||
$sep = $inline ? ' ' : $this->nl;
|
$sep = $inline ? ' ' : $this->nl;
|
||||||
foreach ($nodes as $node) {
|
foreach ($nodes as $node) {
|
||||||
|
@@ -21,6 +21,8 @@ abstract class PrettyPrinterAbstract
|
|||||||
const FIXUP_BRACED_NAME = 4; // Name operand that may require bracing
|
const FIXUP_BRACED_NAME = 4; // Name operand that may require bracing
|
||||||
const FIXUP_VAR_BRACED_NAME = 5; // Name operand that may require ${} bracing
|
const FIXUP_VAR_BRACED_NAME = 5; // Name operand that may require ${} bracing
|
||||||
const FIXUP_ENCAPSED = 6; // Encapsed string part
|
const FIXUP_ENCAPSED = 6; // Encapsed string part
|
||||||
|
const FIXUP_NEW = 7; // New/instanceof operand
|
||||||
|
const FIXUP_STATIC_DEREF_LHS = 8; // LHS of static dereferencing operation
|
||||||
|
|
||||||
protected $precedenceMap = [
|
protected $precedenceMap = [
|
||||||
// [precedence, associativity]
|
// [precedence, associativity]
|
||||||
@@ -66,7 +68,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
BinaryOp\BooleanAnd::class => [120, -1],
|
BinaryOp\BooleanAnd::class => [120, -1],
|
||||||
BinaryOp\BooleanOr::class => [130, -1],
|
BinaryOp\BooleanOr::class => [130, -1],
|
||||||
BinaryOp\Coalesce::class => [140, 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
|
// parser uses %left for assignments, but they really behave as %right
|
||||||
Expr\Assign::class => [160, 1],
|
Expr\Assign::class => [160, 1],
|
||||||
Expr\AssignRef::class => [160, 1],
|
Expr\AssignRef::class => [160, 1],
|
||||||
@@ -656,7 +658,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
$result .= $extraLeft;
|
$result .= $extraLeft;
|
||||||
|
|
||||||
$origIndentLevel = $this->indentLevel;
|
$origIndentLevel = $this->indentLevel;
|
||||||
$this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment);
|
$this->setIndentLevel(max($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment, 0));
|
||||||
|
|
||||||
// If it's the same node that was previously in this position, it certainly doesn't
|
// If it's the same node that was previously in this position, it certainly doesn't
|
||||||
// need fixup. It's important to check this here, because our fixup checks are more
|
// need fixup. It's important to check this here, because our fixup checks are more
|
||||||
@@ -759,7 +761,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
\assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
|
\assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
|
||||||
|
|
||||||
$origIndentLevel = $this->indentLevel;
|
$origIndentLevel = $this->indentLevel;
|
||||||
$lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
|
$lastElemIndentLevel = max($this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment, 0);
|
||||||
$this->setIndentLevel($lastElemIndentLevel);
|
$this->setIndentLevel($lastElemIndentLevel);
|
||||||
|
|
||||||
$comments = $arrItem->getComments();
|
$comments = $arrItem->getComments();
|
||||||
@@ -774,7 +776,8 @@ abstract class PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($skipRemovedNode) {
|
if ($skipRemovedNode) {
|
||||||
if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) {
|
if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
|
||||||
|
$this->origTokens->haveTagInRange($pos, $itemStartPos))) {
|
||||||
// We'd remove the brace of a code block.
|
// We'd remove the brace of a code block.
|
||||||
// TODO: Preserve formatting.
|
// TODO: Preserve formatting.
|
||||||
$this->setIndentLevel($origIndentLevel);
|
$this->setIndentLevel($origIndentLevel);
|
||||||
@@ -824,7 +827,11 @@ abstract class PrettyPrinterAbstract
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($insertStr === ', ' && $this->isMultiline($origNodes)) {
|
// We go multiline if the original code was multiline,
|
||||||
|
// or if it's an array item with a comment above it.
|
||||||
|
if ($insertStr === ', ' &&
|
||||||
|
($this->isMultiline($origNodes) || $arrItem->getComments())
|
||||||
|
) {
|
||||||
$insertStr = ',';
|
$insertStr = ',';
|
||||||
$insertNewline = true;
|
$insertNewline = true;
|
||||||
}
|
}
|
||||||
@@ -842,11 +849,11 @@ abstract class PrettyPrinterAbstract
|
|||||||
$this->setIndentLevel($lastElemIndentLevel);
|
$this->setIndentLevel($lastElemIndentLevel);
|
||||||
|
|
||||||
if ($insertNewline) {
|
if ($insertNewline) {
|
||||||
|
$result .= $insertStr . $this->nl;
|
||||||
$comments = $arrItem->getComments();
|
$comments = $arrItem->getComments();
|
||||||
if ($comments) {
|
if ($comments) {
|
||||||
$result .= $this->nl . $this->pComments($comments);
|
$result .= $this->pComments($comments) . $this->nl;
|
||||||
}
|
}
|
||||||
$result .= $insertStr . $this->nl;
|
|
||||||
} else {
|
} else {
|
||||||
$result .= $insertStr;
|
$result .= $insertStr;
|
||||||
}
|
}
|
||||||
@@ -873,7 +880,8 @@ abstract class PrettyPrinterAbstract
|
|||||||
$pos, $itemStartPos, $indentAdjustment);
|
$pos, $itemStartPos, $indentAdjustment);
|
||||||
$skipRemovedNode = true;
|
$skipRemovedNode = true;
|
||||||
} else {
|
} else {
|
||||||
if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) {
|
if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
|
||||||
|
$this->origTokens->haveTagInRange($pos, $itemStartPos))) {
|
||||||
// We'd remove the brace of a code block.
|
// We'd remove the brace of a code block.
|
||||||
// TODO: Preserve formatting.
|
// TODO: Preserve formatting.
|
||||||
return null;
|
return null;
|
||||||
@@ -919,11 +927,14 @@ abstract class PrettyPrinterAbstract
|
|||||||
foreach ($delayedAdd as $delayedAddNode) {
|
foreach ($delayedAdd as $delayedAddNode) {
|
||||||
if (!$first) {
|
if (!$first) {
|
||||||
$result .= $insertStr;
|
$result .= $insertStr;
|
||||||
|
if ($insertNewline) {
|
||||||
|
$result .= $this->nl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$result .= $this->p($delayedAddNode, true);
|
$result .= $this->p($delayedAddNode, true);
|
||||||
$first = false;
|
$first = false;
|
||||||
}
|
}
|
||||||
$result .= $extraRight;
|
$result .= $extraRight === "\n" ? $this->nl : $extraRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@@ -968,6 +979,19 @@ abstract class PrettyPrinterAbstract
|
|||||||
return '(' . $this->p($subNode) . ')';
|
return '(' . $this->p($subNode) . ')';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case self::FIXUP_STATIC_DEREF_LHS:
|
||||||
|
if ($this->staticDereferenceLhsRequiresParens($subNode)
|
||||||
|
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
|
||||||
|
) {
|
||||||
|
return '(' . $this->p($subNode) . ')';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case self::FIXUP_NEW:
|
||||||
|
if ($this->newOperandRequiresParens($subNode)
|
||||||
|
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
|
||||||
|
return '(' . $this->p($subNode) . ')';
|
||||||
|
}
|
||||||
|
break;
|
||||||
case self::FIXUP_BRACED_NAME:
|
case self::FIXUP_BRACED_NAME:
|
||||||
case self::FIXUP_VAR_BRACED_NAME:
|
case self::FIXUP_VAR_BRACED_NAME:
|
||||||
if ($subNode instanceof Expr
|
if ($subNode instanceof Expr
|
||||||
@@ -1038,13 +1062,26 @@ abstract class PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether the LHS of a dereferencing operation must be wrapped in parenthesis.
|
* Determines whether the LHS of an array/object operation must be wrapped in parentheses.
|
||||||
*
|
*
|
||||||
* @param Node $node LHS of dereferencing operation
|
* @param Node $node LHS of dereferencing operation
|
||||||
*
|
*
|
||||||
* @return bool Whether parentheses are required
|
* @return bool Whether parentheses are required
|
||||||
*/
|
*/
|
||||||
protected function dereferenceLhsRequiresParens(Node $node) : bool {
|
protected function dereferenceLhsRequiresParens(Node $node) : bool {
|
||||||
|
// A constant can occur on the LHS of an array/object deref, but not a static deref.
|
||||||
|
return $this->staticDereferenceLhsRequiresParens($node)
|
||||||
|
&& !$node instanceof Expr\ConstFetch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the LHS of a static operation must be wrapped in parentheses.
|
||||||
|
*
|
||||||
|
* @param Node $node LHS of dereferencing operation
|
||||||
|
*
|
||||||
|
* @return bool Whether parentheses are required
|
||||||
|
*/
|
||||||
|
protected function staticDereferenceLhsRequiresParens(Node $node): bool {
|
||||||
return !($node instanceof Expr\Variable
|
return !($node instanceof Expr\Variable
|
||||||
|| $node instanceof Node\Name
|
|| $node instanceof Node\Name
|
||||||
|| $node instanceof Expr\ArrayDimFetch
|
|| $node instanceof Expr\ArrayDimFetch
|
||||||
@@ -1057,10 +1094,31 @@ abstract class PrettyPrinterAbstract
|
|||||||
|| $node instanceof Expr\StaticCall
|
|| $node instanceof Expr\StaticCall
|
||||||
|| $node instanceof Expr\Array_
|
|| $node instanceof Expr\Array_
|
||||||
|| $node instanceof Scalar\String_
|
|| $node instanceof Scalar\String_
|
||||||
|| $node instanceof Expr\ConstFetch
|
|
||||||
|| $node instanceof Expr\ClassConstFetch);
|
|| $node instanceof Expr\ClassConstFetch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether an expression used in "new" or "instanceof" requires parentheses.
|
||||||
|
*
|
||||||
|
* @param Node $node New or instanceof operand
|
||||||
|
*
|
||||||
|
* @return bool Whether parentheses are required
|
||||||
|
*/
|
||||||
|
protected function newOperandRequiresParens(Node $node): bool {
|
||||||
|
if ($node instanceof Node\Name || $node instanceof Expr\Variable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch ||
|
||||||
|
$node instanceof Expr\NullsafePropertyFetch
|
||||||
|
) {
|
||||||
|
return $this->newOperandRequiresParens($node->var);
|
||||||
|
}
|
||||||
|
if ($node instanceof Expr\StaticPropertyFetch) {
|
||||||
|
return $this->newOperandRequiresParens($node->class);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print modifiers, including trailing whitespace.
|
* Print modifiers, including trailing whitespace.
|
||||||
*
|
*
|
||||||
@@ -1074,7 +1132,8 @@ abstract class PrettyPrinterAbstract
|
|||||||
. ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '')
|
. ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '')
|
||||||
. ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '')
|
. ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '')
|
||||||
. ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '')
|
. ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '')
|
||||||
. ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '');
|
. ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '')
|
||||||
|
. ($modifiers & Stmt\Class_::MODIFIER_READONLY ? 'readonly ' : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1123,7 +1182,8 @@ abstract class PrettyPrinterAbstract
|
|||||||
for ($i = 0; $i < 256; $i++) {
|
for ($i = 0; $i < 256; $i++) {
|
||||||
// Since PHP 7.1 The lower range is 0x80. However, we also want to support code for
|
// Since PHP 7.1 The lower range is 0x80. However, we also want to support code for
|
||||||
// older versions.
|
// older versions.
|
||||||
$this->labelCharMap[chr($i)] = $i >= 0x7f || ctype_alnum($i);
|
$chr = chr($i);
|
||||||
|
$this->labelCharMap[$chr] = $i >= 0x7f || ctype_alnum($chr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1160,7 +1220,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT],
|
Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT],
|
||||||
Expr\Instanceof_::class => [
|
Expr\Instanceof_::class => [
|
||||||
'expr' => self::FIXUP_PREC_LEFT,
|
'expr' => self::FIXUP_PREC_LEFT,
|
||||||
'class' => self::FIXUP_PREC_RIGHT, // TODO: FIXUP_NEW_VARIABLE
|
'class' => self::FIXUP_NEW,
|
||||||
],
|
],
|
||||||
Expr\Ternary::class => [
|
Expr\Ternary::class => [
|
||||||
'cond' => self::FIXUP_PREC_LEFT,
|
'cond' => self::FIXUP_PREC_LEFT,
|
||||||
@@ -1168,10 +1228,13 @@ abstract class PrettyPrinterAbstract
|
|||||||
],
|
],
|
||||||
|
|
||||||
Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
|
Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
|
||||||
Expr\StaticCall::class => ['class' => self::FIXUP_DEREF_LHS],
|
Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
|
||||||
Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
|
Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
|
||||||
Expr\ClassConstFetch::class => ['var' => self::FIXUP_DEREF_LHS],
|
Expr\ClassConstFetch::class => [
|
||||||
Expr\New_::class => ['class' => self::FIXUP_DEREF_LHS], // TODO: FIXUP_NEW_VARIABLE
|
'class' => self::FIXUP_STATIC_DEREF_LHS,
|
||||||
|
'name' => self::FIXUP_BRACED_NAME,
|
||||||
|
],
|
||||||
|
Expr\New_::class => ['class' => self::FIXUP_NEW],
|
||||||
Expr\MethodCall::class => [
|
Expr\MethodCall::class => [
|
||||||
'var' => self::FIXUP_DEREF_LHS,
|
'var' => self::FIXUP_DEREF_LHS,
|
||||||
'name' => self::FIXUP_BRACED_NAME,
|
'name' => self::FIXUP_BRACED_NAME,
|
||||||
@@ -1181,7 +1244,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
'name' => self::FIXUP_BRACED_NAME,
|
'name' => self::FIXUP_BRACED_NAME,
|
||||||
],
|
],
|
||||||
Expr\StaticPropertyFetch::class => [
|
Expr\StaticPropertyFetch::class => [
|
||||||
'class' => self::FIXUP_DEREF_LHS,
|
'class' => self::FIXUP_STATIC_DEREF_LHS,
|
||||||
'name' => self::FIXUP_VAR_BRACED_NAME,
|
'name' => self::FIXUP_VAR_BRACED_NAME,
|
||||||
],
|
],
|
||||||
Expr\PropertyFetch::class => [
|
Expr\PropertyFetch::class => [
|
||||||
@@ -1242,7 +1305,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
/**
|
/**
|
||||||
* Lazily initializes the removal map.
|
* 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.
|
* certain node is replaced by null.
|
||||||
*/
|
*/
|
||||||
protected function initializeRemovalMap() {
|
protected function initializeRemovalMap() {
|
||||||
@@ -1267,8 +1330,11 @@ abstract class PrettyPrinterAbstract
|
|||||||
'Param->default' => $stripEquals,
|
'Param->default' => $stripEquals,
|
||||||
'Stmt_Break->num' => $stripBoth,
|
'Stmt_Break->num' => $stripBoth,
|
||||||
'Stmt_Catch->var' => $stripLeft,
|
'Stmt_Catch->var' => $stripLeft,
|
||||||
|
'Stmt_ClassConst->type' => $stripRight,
|
||||||
'Stmt_ClassMethod->returnType' => $stripColon,
|
'Stmt_ClassMethod->returnType' => $stripColon,
|
||||||
'Stmt_Class->extends' => ['left' => \T_EXTENDS],
|
'Stmt_Class->extends' => ['left' => \T_EXTENDS],
|
||||||
|
'Stmt_Enum->scalarType' => $stripColon,
|
||||||
|
'Stmt_EnumCase->expr' => $stripEquals,
|
||||||
'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS],
|
'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS],
|
||||||
'Stmt_Continue->num' => $stripBoth,
|
'Stmt_Continue->num' => $stripBoth,
|
||||||
'Stmt_Foreach->keyVar' => $stripDoubleArrow,
|
'Stmt_Foreach->keyVar' => $stripDoubleArrow,
|
||||||
@@ -1306,7 +1372,10 @@ abstract class PrettyPrinterAbstract
|
|||||||
'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
|
'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
|
||||||
'Stmt_Catch->var' => [null, false, ' ', null],
|
'Stmt_Catch->var' => [null, false, ' ', null],
|
||||||
'Stmt_ClassMethod->returnType' => [')', false, ' : ', null],
|
'Stmt_ClassMethod->returnType' => [')', false, ' : ', null],
|
||||||
|
'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
|
||||||
'Stmt_Class->extends' => [null, false, ' extends ', 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],
|
'Expr_PrintableNewAnonClass->extends' => [null, ' extends ', null],
|
||||||
'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null],
|
'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null],
|
||||||
'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '],
|
'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '],
|
||||||
@@ -1337,6 +1406,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
//'Scalar_Encapsed->parts' => '',
|
//'Scalar_Encapsed->parts' => '',
|
||||||
'Stmt_Catch->types' => '|',
|
'Stmt_Catch->types' => '|',
|
||||||
'UnionType->types' => '|',
|
'UnionType->types' => '|',
|
||||||
|
'IntersectionType->types' => '&',
|
||||||
'Stmt_If->elseifs' => ' ',
|
'Stmt_If->elseifs' => ' ',
|
||||||
'Stmt_TryCatch->catches' => ' ',
|
'Stmt_TryCatch->catches' => ' ',
|
||||||
|
|
||||||
@@ -1356,6 +1426,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
'Stmt_ClassConst->consts' => ', ',
|
'Stmt_ClassConst->consts' => ', ',
|
||||||
'Stmt_ClassMethod->params' => ', ',
|
'Stmt_ClassMethod->params' => ', ',
|
||||||
'Stmt_Class->implements' => ', ',
|
'Stmt_Class->implements' => ', ',
|
||||||
|
'Stmt_Enum->implements' => ', ',
|
||||||
'Expr_PrintableNewAnonClass->implements' => ', ',
|
'Expr_PrintableNewAnonClass->implements' => ', ',
|
||||||
'Stmt_Const->consts' => ', ',
|
'Stmt_Const->consts' => ', ',
|
||||||
'Stmt_Declare->declares' => ', ',
|
'Stmt_Declare->declares' => ', ',
|
||||||
@@ -1382,6 +1453,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
'Stmt_Case->stmts' => "\n",
|
'Stmt_Case->stmts' => "\n",
|
||||||
'Stmt_Catch->stmts' => "\n",
|
'Stmt_Catch->stmts' => "\n",
|
||||||
'Stmt_Class->stmts' => "\n",
|
'Stmt_Class->stmts' => "\n",
|
||||||
|
'Stmt_Enum->stmts' => "\n",
|
||||||
'Expr_PrintableNewAnonClass->stmts' => "\n",
|
'Expr_PrintableNewAnonClass->stmts' => "\n",
|
||||||
'Stmt_Interface->stmts' => "\n",
|
'Stmt_Interface->stmts' => "\n",
|
||||||
'Stmt_Trait->stmts' => "\n",
|
'Stmt_Trait->stmts' => "\n",
|
||||||
@@ -1397,6 +1469,8 @@ abstract class PrettyPrinterAbstract
|
|||||||
'Stmt_If->stmts' => "\n",
|
'Stmt_If->stmts' => "\n",
|
||||||
'Stmt_Namespace->stmts' => "\n",
|
'Stmt_Namespace->stmts' => "\n",
|
||||||
'Stmt_Class->attrGroups' => "\n",
|
'Stmt_Class->attrGroups' => "\n",
|
||||||
|
'Stmt_Enum->attrGroups' => "\n",
|
||||||
|
'Stmt_EnumCase->attrGroups' => "\n",
|
||||||
'Stmt_Interface->attrGroups' => "\n",
|
'Stmt_Interface->attrGroups' => "\n",
|
||||||
'Stmt_Trait->attrGroups' => "\n",
|
'Stmt_Trait->attrGroups' => "\n",
|
||||||
'Stmt_Function->attrGroups' => "\n",
|
'Stmt_Function->attrGroups' => "\n",
|
||||||
@@ -1435,9 +1509,20 @@ abstract class PrettyPrinterAbstract
|
|||||||
'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''],
|
'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''],
|
||||||
'Expr_StaticCall->args' => ['(', '', ''],
|
'Expr_StaticCall->args' => ['(', '', ''],
|
||||||
'Stmt_Class->implements' => [null, ' implements ', ''],
|
'Stmt_Class->implements' => [null, ' implements ', ''],
|
||||||
|
'Stmt_Enum->implements' => [null, ' implements ', ''],
|
||||||
'Stmt_ClassMethod->params' => ['(', '', ''],
|
'Stmt_ClassMethod->params' => ['(', '', ''],
|
||||||
'Stmt_Interface->extends' => [null, ' extends ', ''],
|
'Stmt_Interface->extends' => [null, ' extends ', ''],
|
||||||
'Stmt_Function->params' => ['(', '', ''],
|
'Stmt_Function->params' => ['(', '', ''],
|
||||||
|
'Stmt_Interface->attrGroups' => [null, '', "\n"],
|
||||||
|
'Stmt_Class->attrGroups' => [null, '', "\n"],
|
||||||
|
'Stmt_ClassConst->attrGroups' => [null, '', "\n"],
|
||||||
|
'Stmt_ClassMethod->attrGroups' => [null, '', "\n"],
|
||||||
|
'Stmt_Function->attrGroups' => [null, '', "\n"],
|
||||||
|
'Stmt_Property->attrGroups' => [null, '', "\n"],
|
||||||
|
'Stmt_Trait->attrGroups' => [null, '', "\n"],
|
||||||
|
'Expr_ArrowFunction->attrGroups' => [null, '', ' '],
|
||||||
|
'Expr_Closure->attrGroups' => [null, '', ' '],
|
||||||
|
'Expr_PrintableNewAnonClass->attrGroups' => [\T_NEW, ' ', ''],
|
||||||
|
|
||||||
/* These cannot be empty to start with:
|
/* These cannot be empty to start with:
|
||||||
* Expr_Isset->vars
|
* Expr_Isset->vars
|
||||||
@@ -1477,6 +1562,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
'Stmt_ClassMethod->flags' => \T_FUNCTION,
|
'Stmt_ClassMethod->flags' => \T_FUNCTION,
|
||||||
'Stmt_Class->flags' => \T_CLASS,
|
'Stmt_Class->flags' => \T_CLASS,
|
||||||
'Stmt_Property->flags' => \T_VARIABLE,
|
'Stmt_Property->flags' => \T_VARIABLE,
|
||||||
|
'Expr_PrintableNewAnonClass->flags' => \T_CLASS,
|
||||||
'Param->flags' => \T_VARIABLE,
|
'Param->flags' => \T_VARIABLE,
|
||||||
//'Stmt_TraitUseAdaptation_Alias->newModifier' => 0, // TODO
|
//'Stmt_TraitUseAdaptation_Alias->newModifier' => 0, // TODO
|
||||||
];
|
];
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user