mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-09 16:36:31 +02:00
Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
dc47765670 | |||
8ab65b4adc | |||
7fc3bcf970 | |||
acf8f5ef01 | |||
ae59794362 | |||
4964f8d5dd | |||
20b0d55f66 | |||
c9d0b6c9f2 | |||
14f9c9df7f | |||
fa02db3f30 | |||
d4fce83c2c | |||
7d3039c378 | |||
447a020a1f | |||
45f70ed80a | |||
74a361814d | |||
6478c5ac53 | |||
8bb415902e | |||
48fd76e542 | |||
d20a197ca7 | |||
62dee28027 | |||
b396e9e0d6 | |||
f212bb7afb | |||
f43324a074 | |||
73b160f8c4 | |||
4f9dc8b0f5 | |||
469377f4a8 | |||
74d3f7fc24 | |||
25828ea952 | |||
05ed79595b | |||
954f7a411f | |||
8eea230464 | |||
9c7a3f8d8f | |||
26573ea64f | |||
3abf7425cd | |||
961f158f6d | |||
e50c67b7a9 | |||
26a0197186 | |||
aedfcc23cd | |||
8d09ba87f4 | |||
23c79fbbfb | |||
b9c8374498 | |||
d0826bd3e4 | |||
5ea6c2938b | |||
b493c51cce | |||
54139ca49b | |||
cde9bab3bb | |||
018da15f3a | |||
ba14437165 | |||
4a22c15169 | |||
514f71024e | |||
6a97061265 | |||
e3f223f623 | |||
03caf4cc99 | |||
b11fc12cce | |||
3beee6ec77 | |||
58de479119 | |||
d3ae2ed679 | |||
beba9c528f | |||
fadccead52 | |||
d8235a2701 | |||
5973c30a46 | |||
b917ba7b9c | |||
caf540443a | |||
683130c2ff | |||
a894652a3d | |||
3ef0811e45 | |||
db1963f9ea | |||
daaadc3bae | |||
7b0384cdbe | |||
d57da64d7c | |||
d327cf2acf | |||
c5ee33df86 | |||
46be4560c4 | |||
f2e037f8ea | |||
b43758e9e9 | |||
09691fc86e | |||
139676794d | |||
70c96493b4 | |||
ec02613432 | |||
af14fdb282 | |||
2218c2252c | |||
ce019e9ad7 | |||
ff095c3c65 | |||
d619c8b4e6 | |||
eb036d5a09 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@ -7,7 +7,11 @@
|
||||
.editorconfig export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.php-cs-fixer.dist.php export-ignore
|
||||
Makefile export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
CONTRIBUTING.md export-ignore
|
||||
phpstan-baseline.neon export-ignore
|
||||
phpstan.neon.dist export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
UPGRADE-*.md export-ignore
|
||||
|
37
.github/workflows/main.yml
vendored
37
.github/workflows/main.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
name: "PHP 7.4 Unit Tests (with coverage)"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
@ -20,7 +20,7 @@ jobs:
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
composer require php-coveralls/php-coveralls:^2.2 --dev --no-update
|
||||
composer update --no-progress --prefer-dist
|
||||
COMPOSER_ROOT_VERSION=dev-master composer update --no-progress --prefer-dist
|
||||
- name: "Tests"
|
||||
run: "php vendor/bin/phpunit --coverage-clover build/logs/clover.xml"
|
||||
- name: Coveralls
|
||||
@ -38,62 +38,67 @@ jobs:
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
- 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 }}"
|
||||
run: "COMPOSER_ROOT_VERSION=dev-master 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.4 Code on PHP 8.2 Integration Tests"
|
||||
name: "PHP 7.4 Code on PHP 8.4 Integration Tests"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
php-version: "8.4"
|
||||
ini-file: "development"
|
||||
tools: composer:v2
|
||||
- name: "Install PHP 8 dependencies"
|
||||
run: "composer update --no-progress --prefer-dist"
|
||||
run: "COMPOSER_ROOT_VERSION=dev-master composer update --no-progress --prefer-dist"
|
||||
- name: "Tests"
|
||||
run: "test_old/run-php-src.sh 7.4.33"
|
||||
test_old_80_70:
|
||||
runs-on: "ubuntu-latest"
|
||||
name: "PHP 8.3 Code on PHP 7.4 Integration Tests"
|
||||
name: "PHP 8.4 Code on PHP 7.4 Integration Tests"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "7.4"
|
||||
ini-file: "development"
|
||||
tools: composer:v2
|
||||
- name: "Install PHP 8 dependencies"
|
||||
run: "composer update --no-progress --prefer-dist"
|
||||
run: "COMPOSER_ROOT_VERSION=dev-master composer update --no-progress --prefer-dist"
|
||||
- name: "Tests"
|
||||
run: "test_old/run-php-src.sh 8.3.0RC2"
|
||||
run: "test_old/run-php-src.sh 8.4.0beta5"
|
||||
phpstan:
|
||||
runs-on: "ubuntu-latest"
|
||||
name: "PHPStan"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
php-version: "8.3"
|
||||
tools: composer:v2
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
@ -105,12 +110,12 @@ jobs:
|
||||
name: "PHP-CS-Fixer"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
php-version: "8.3"
|
||||
tools: composer:v2
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
|
119
CHANGELOG.md
119
CHANGELOG.md
@ -1,3 +1,122 @@
|
||||
Version 5.5.0 (2025-05-31)
|
||||
--------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* [8.5] Added support for attributes on constants. `Stmt\Const_` now has an `attrGroups` subnode.
|
||||
* Added `weakReferences` option to `NodeConnectingVisitor` and `ParentConnectingVisitor`. This
|
||||
will create the parent/next/prev references as WeakReferences, to avoid making the AST cyclic
|
||||
and thus increasing GC pressure.
|
||||
|
||||
### Changed
|
||||
|
||||
* Attributes on parameters are now printed on separate lines if the pretty printer target version
|
||||
is PHP 7.4 or older (which is the default). This allows them to be interpreted as comments,
|
||||
instead of causing a parse error. Specify a target version of PHP 8.0 or newer to restore the
|
||||
previous behavior.
|
||||
|
||||
Version 5.4.0 (2024-12-30)
|
||||
--------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* Added `Property::isAbstract()` and `Property::isFinal()` methods.
|
||||
* Added `PropertyHook::isFinal()` method.
|
||||
* Emit an error if property hook is used on declaration with multiple properties.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Make legacy class aliases compatible with classmap-authoritative autoloader.
|
||||
* `Param::isPromoted()` and `Param::isPublic()` now returns true for parameters that have property
|
||||
hooks but no explicit visibility modifier.
|
||||
* `PropertyHook::getStmts()` now correctly desugars short `set` hooks. `set => $value` will be
|
||||
expanded to `set { $this->propertyName = $value; }`. This requires the `propertyName` attribute
|
||||
on the hook to be set, which is now also set by the parser. If the attribute is not set,
|
||||
`getStmts()` will throw an error for short set hooks, as it is not possible to produce a correct
|
||||
desugaring.
|
||||
|
||||
Version 5.3.1 (2024-10-08)
|
||||
--------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* Added support for declaring functions with name `exit` or `die`, to allow their use in stubs.
|
||||
|
||||
Version 5.3.0 (2024-09-29)
|
||||
--------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* Added `indent` option to pretty printer, which can be used to specify the indentation to use
|
||||
(defaulting to four spaces). This also allows using tab indentation.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Resolve names in `PropertyHook`s in the `NameResolver`.
|
||||
* 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 5.2.0 (2024-09-15)
|
||||
--------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* [8.4] Added support for `__PROPERTY__` magic constant, represented using a
|
||||
`Node\Scalar\MagicConst\Property` node.
|
||||
* [8.4] Added support for property hooks, which are represented using a new `hooks` subnode on
|
||||
`Node\Stmt\Property` and `Node\Param`, which contains an array of `Node\PropertyHook`.
|
||||
* [8.4] Added support for asymmetric visibility modifiers. Property `flags` can now hold the
|
||||
additional bits `Modifiers::PUBLIC_SET`, `Modifiers::PROTECTED_SET` and `Modifiers::PRIVATE_SET`.
|
||||
* [8.4] Added support for generalized exit function. For backwards compatibility, exit without
|
||||
argument or a single plain argument continues to use a `Node\Expr\Exit_` node. Otherwise (e.g.
|
||||
if a named argument is used) it will be represented as a plain `Node\Expr\FuncCall`.
|
||||
* Added support for passing enum values to various builder methods, like `BuilderFactory::val()`.
|
||||
|
||||
### Removed
|
||||
|
||||
* Removed support for alternative array syntax `$array{0}` from the PHP 8 parser. It is still
|
||||
supported by the PHP 7 parser. This is necessary in order to support property hooks.
|
||||
|
||||
Version 5.1.0 (2024-07-01)
|
||||
--------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* [8.4] Added support for dereferencing `new` expressions without parentheses.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed redundant parentheses being added when pretty printing ternary expressions.
|
||||
|
||||
### Changed
|
||||
|
||||
* Made some phpdoc types more precise.
|
||||
|
||||
Version 5.0.2 (2024-03-05)
|
||||
--------------------------
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix handling of indentation on next line after opening PHP tag in formatting-preserving pretty
|
||||
printer.
|
||||
|
||||
### Changed
|
||||
|
||||
* Avoid cyclic references in `Parser` objects. This means that no longer used parser objects are
|
||||
immediately destroyed now, instead of requiring cycle GC.
|
||||
* Update `PhpVersion::getNewestSupported()` to report PHP 8.3 instead of PHP 8.2.
|
||||
|
||||
Version 5.0.1 (2024-02-21)
|
||||
--------------------------
|
||||
|
||||
### Changed
|
||||
|
||||
* Added check to detect use of PHP-Parser with libraries that define `T_*` compatibility tokens
|
||||
with incorrect type (such as string instead of int). This would lead to `TypeError`s down the
|
||||
line. Now an `Error` will be thrown early to indicate the problem.
|
||||
|
||||
Version 5.0.0 (2024-01-07)
|
||||
--------------------------
|
||||
|
||||
|
@ -2,3 +2,31 @@
|
||||
|
||||
This project uses PSR-12 with consistent brace placement. This means that the opening brace is
|
||||
always on the same line, even for class and method declarations.
|
||||
|
||||
## Tools
|
||||
|
||||
This project uses PHP-CS-Fixer and PHPStan. You can invoke them using `make`:
|
||||
|
||||
```shell
|
||||
make php-cs-fixer
|
||||
make phpstan
|
||||
```
|
||||
|
||||
## Adding support for new PHP syntax
|
||||
|
||||
1. If necessary, add emulation support for new tokens.
|
||||
* Add a new subclass of `Lexer\TokenEmulator`. Take inspiration from existing classes.
|
||||
* Add the new class to the array in `Lexer\Emulative`.
|
||||
* Add tests for the emulation in `Lexer\EmulativeTest`. You'll want to modify
|
||||
`provideTestReplaceKeywords()` for new reserved keywords and `provideTestLexNewFeatures()` for
|
||||
other emulations.
|
||||
2. Add any new node classes that are needed.
|
||||
3. Add support for the new syntax in `grammar/php.y`. Regenerate the parser by running
|
||||
`php grammar/rebuildParsers.php`. Use `--debug` if there are conflicts.
|
||||
4. Add pretty-printing support by implementing a `pFooBar()` method in `PrettyPrinter\Standard`.
|
||||
5. Add tests both in `test/code/parser` and `test/code/prettyPrinter`.
|
||||
6. Add support for formatting-preserving pretty-printing. This is done by modifying the data tables
|
||||
at the end of `PrettyPrinterAbstract`. Add a test in `test/code/formatPreservation`.
|
||||
7. Does the new syntax feature namespaced names? If so, add support for name resolution in
|
||||
`NodeVisitor\NameResolver`. Test it in `NodeVisitor\NameResolverTest`.
|
||||
8. Does the new syntax require any changes to builders? Is so, make them :)
|
||||
|
7
Makefile
7
Makefile
@ -4,7 +4,10 @@ tools/vendor:
|
||||
composer install -d tools
|
||||
|
||||
phpstan: tools/vendor
|
||||
tools/vendor/bin/phpstan
|
||||
php tools/vendor/bin/phpstan
|
||||
|
||||
php-cs-fixer: tools/vendor
|
||||
tools/vendor/bin/php-cs-fixer fix
|
||||
php tools/vendor/bin/php-cs-fixer fix
|
||||
|
||||
tests:
|
||||
php vendor/bin/phpunit
|
@ -6,7 +6,7 @@ PHP Parser
|
||||
This is a PHP parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
manipulation.
|
||||
|
||||
[**Documentation for version 5.x**][doc_master] (current; for running on PHP >= 7.4; for parsing PHP 7.0 to PHP 8.3, with limited support for parsing PHP 5.x).
|
||||
[**Documentation for version 5.x**][doc_master] (current; for running on PHP >= 7.4; for parsing PHP 7.0 to PHP 8.4, with limited support for parsing PHP 5.x).
|
||||
|
||||
[Documentation for version 4.x][doc_4_x] (supported; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.3).
|
||||
|
||||
|
@ -37,7 +37,7 @@ The `ParserFactory::create()` method has been removed in favor of three new meth
|
||||
* `createForHostVersion()`: Use this if you're parsing code for the PHP version you're running on.
|
||||
* `createForVersion()`: Use this if you know the PHP version of the code you want to parse.
|
||||
|
||||
The `createForNewestSupportedVersion()` and `creatForHostVersion()` are available since PHP-Parser 4.18.0, to allow libraries to support PHP-Parser 4 and 5 at the same time more easily.
|
||||
The `createForNewestSupportedVersion()` and `createForHostVersion()` are available since PHP-Parser 4.18.0, to allow libraries to support PHP-Parser 4 and 5 at the same time more easily.
|
||||
|
||||
In all cases, the PHP version is a fairly weak hint that is only used on a best-effort basis. The parser will usually accept code for newer versions if it does not have any backwards-compatibility implications.
|
||||
|
||||
@ -308,7 +308,7 @@ PhpParser\Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK -> PhpParser\Modifiers::VIS
|
||||
|
||||
### Changes to node constructors
|
||||
|
||||
Node constructor arguments accepting types now longer accept plain strings. Either an `Identifier` or `Name` (or `ComplexType`) should be passed instead. This affects the following constructor arguments:
|
||||
Node constructor arguments accepting types no longer accept plain strings. Either an `Identifier` or `Name` (or `ComplexType`) should be passed instead. This affects the following constructor arguments:
|
||||
|
||||
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\ArrowFunction`.
|
||||
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\Closure`.
|
||||
@ -519,7 +519,7 @@ $newStmts = $traverser->traverse($oldStmts);
|
||||
|
||||
$parser = (new ParserFactory())->createForNewestSupportedVersion();
|
||||
$oldStmts = $parser->parse($code);
|
||||
$oldTokens = $lexer->getTokens();
|
||||
$oldTokens = $parser->getTokens();
|
||||
|
||||
$traverser = new NodeTraverser(new NodeVisitor\CloningVisitor());
|
||||
$newStmts = $traverser->traverse($oldStmts);
|
||||
|
@ -19,7 +19,7 @@
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"ircmaxell/php-yacc": "^0.0.7"
|
||||
},
|
||||
"extra": {
|
||||
|
@ -43,7 +43,7 @@ following caveats:
|
||||
|
||||
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.
|
||||
This allows to parse PHP 8.3 source code running on PHP 7.4, for example. This emulation is not
|
||||
This allows to parse PHP 8.4 source code running on PHP 7.4, for example. This emulation is not
|
||||
perfect, but works well in practice.
|
||||
|
||||
Finally, it should be noted that the parser aims to accept all valid code, not reject all invalid
|
||||
|
@ -51,3 +51,17 @@ obtained through `$node->getAttribute('next')`.
|
||||
|
||||
`ParentConnectingVisitor` and `NodeConnectingVisitor` should not be used at the same time. The latter
|
||||
includes the functionality of the former.
|
||||
|
||||
|
||||
How can I limit the impact of cyclic references in the AST?
|
||||
-----
|
||||
|
||||
NodeConnectingVisitor adds a parent reference, which introduces a cycle. This means that the AST can now only be collected by the cycle garbage collector.
|
||||
This in turn can lead to performance and/or memory issues.
|
||||
|
||||
To break the cyclic references between AST nodes `NodeConnectingVisitor` supports a boolean `$weakReferences` constructor parameter.
|
||||
When set to `true`, all attributes added by `NodeConnectingVisitor` will be wrapped into a `WeakReference` object.
|
||||
|
||||
After enabling this parameter, the parent node can be obtained through `$node->getAttribute('weak_parent')`,
|
||||
the previous node can be obtained through `$node->getAttribute('weak_previous')`, and the next node can be
|
||||
obtained through `$node->getAttribute('weak_next')`.
|
@ -104,7 +104,7 @@ This will result in the following output (which includes attributes):
|
||||
"nodeType": "Scalar_String",
|
||||
"attributes": {
|
||||
"startLine": 5,
|
||||
"endLine": 5
|
||||
"endLine": 5,
|
||||
"kind": 2,
|
||||
"rawValue": "\"\\n\""
|
||||
},
|
||||
|
@ -37,6 +37,7 @@ integer should be printed as decimal, hexadecimal, etc). Additionally, it suppor
|
||||
* `phpVersion` (defaults to 7.4) allows opting into formatting that is not supported by older PHP
|
||||
versions.
|
||||
* `newline` (defaults to `"\n"`) can be set to `"\r\n"` in order to produce Windows newlines.
|
||||
* `indent` (defaults to four spaces `" "`) can be set to any number of spaces or a single tab.
|
||||
* `shortArraySyntax` determines the used array syntax if the `kind` attribute is not set. This is
|
||||
a legacy option, and `phpVersion` should be used to control this behavior instead.
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
$meta #
|
||||
#semval($) $this->semValue
|
||||
#semval($,%t) $this->semValue
|
||||
#semval($) $self->semValue
|
||||
#semval($,%t) $self->semValue
|
||||
#semval(%n) $stackPos-(%l-%n)
|
||||
#semval(%n,%t) $stackPos-(%l-%n)
|
||||
|
||||
@ -97,7 +97,7 @@ class #(-p) extends \PhpParser\ParserAbstract
|
||||
protected function initReduceCallbacks(): void {
|
||||
$this->reduceCallbacks = [
|
||||
#reduce
|
||||
%n => function ($stackPos) {
|
||||
%n => static function ($self, $stackPos) {
|
||||
%b
|
||||
},
|
||||
#noact
|
||||
|
174
grammar/php.y
174
grammar/php.y
@ -81,7 +81,10 @@
|
||||
%token T_USE
|
||||
%token T_INSTEADOF
|
||||
%token T_GLOBAL
|
||||
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
|
||||
%token T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
|
||||
%token T_PUBLIC_SET
|
||||
%token T_PROTECTED_SET
|
||||
%token T_PRIVATE_SET
|
||||
%token T_VAR
|
||||
%token T_UNSET
|
||||
%token T_ISSET
|
||||
@ -103,6 +106,7 @@
|
||||
%token T_TRAIT_C
|
||||
%token T_METHOD_C
|
||||
%token T_FUNC_C
|
||||
%token T_PROPERTY_C
|
||||
%token T_LINE
|
||||
%token T_FILE
|
||||
%token T_START_HEREDOC
|
||||
@ -252,8 +256,11 @@ top_statement:
|
||||
$this->checkNamespace($$); }
|
||||
| T_USE use_declarations semi { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
||||
| T_USE use_type use_declarations semi { $$ = Stmt\Use_[$3, $2]; }
|
||||
| group_use_declaration semi
|
||||
| T_CONST constant_declaration_list semi { $$ = Stmt\Const_[$2]; }
|
||||
| group_use_declaration
|
||||
| T_CONST constant_declaration_list semi { $$ = new Stmt\Const_($2, attributes(), []); }
|
||||
| attributes T_CONST constant_declaration_list semi
|
||||
{ $$ = new Stmt\Const_($3, attributes(), $1);
|
||||
$this->checkConstantAttributes($$); }
|
||||
;
|
||||
|
||||
use_type:
|
||||
@ -262,9 +269,9 @@ use_type:
|
||||
;
|
||||
|
||||
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]; }
|
||||
| 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]; }
|
||||
;
|
||||
|
||||
@ -460,15 +467,17 @@ block_or_error:
|
||||
| error { $$ = []; }
|
||||
;
|
||||
|
||||
identifier_maybe_readonly:
|
||||
fn_identifier:
|
||||
identifier_not_reserved
|
||||
| T_READONLY { $$ = Node\Identifier[$1]; }
|
||||
| T_EXIT { $$ = Node\Identifier[$1]; }
|
||||
| T_CLONE { $$ = Node\Identifier[$1]; }
|
||||
;
|
||||
|
||||
function_declaration_statement:
|
||||
T_FUNCTION optional_ref identifier_maybe_readonly '(' parameter_list ')' optional_return_type block_or_error
|
||||
T_FUNCTION optional_ref fn_identifier '(' parameter_list ')' optional_return_type block_or_error
|
||||
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8, 'attrGroups' => []]]; }
|
||||
| attributes T_FUNCTION optional_ref identifier_maybe_readonly '(' parameter_list ')' optional_return_type block_or_error
|
||||
| attributes T_FUNCTION optional_ref fn_identifier '(' parameter_list ')' optional_return_type block_or_error
|
||||
{ $$ = Stmt\Function_[$4, ['byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
|
||||
;
|
||||
|
||||
@ -671,18 +680,23 @@ property_modifier:
|
||||
T_PUBLIC { $$ = Modifiers::PUBLIC; }
|
||||
| T_PROTECTED { $$ = Modifiers::PROTECTED; }
|
||||
| T_PRIVATE { $$ = Modifiers::PRIVATE; }
|
||||
| T_PUBLIC_SET { $$ = Modifiers::PUBLIC_SET; }
|
||||
| T_PROTECTED_SET { $$ = Modifiers::PROTECTED_SET; }
|
||||
| T_PRIVATE_SET { $$ = Modifiers::PRIVATE_SET; }
|
||||
| T_READONLY { $$ = Modifiers::READONLY; }
|
||||
;
|
||||
|
||||
parameter:
|
||||
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);
|
||||
$this->checkParam($$); }
|
||||
optional_arg_ref optional_ellipsis plain_variable optional_property_hook_list
|
||||
{ $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1, $7);
|
||||
$this->checkParam($$);
|
||||
$this->addPropertyNameToHooks($$); }
|
||||
| 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);
|
||||
$this->checkParam($$); }
|
||||
optional_arg_ref optional_ellipsis plain_variable '=' expr optional_property_hook_list
|
||||
{ $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1, $9);
|
||||
$this->checkParam($$);
|
||||
$this->addPropertyNameToHooks($$); }
|
||||
| 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); }
|
||||
@ -771,6 +785,22 @@ argument_list:
|
||||
| '(' variadic_placeholder ')' { init($2); }
|
||||
;
|
||||
|
||||
clone_argument_list:
|
||||
'(' ')' { $$ = array(); }
|
||||
| '(' non_empty_clone_argument_list optional_comma ')' { $$ = $2; }
|
||||
| '(' expr ',' ')' { init(Node\Arg[$2, false, false]); }
|
||||
| '(' variadic_placeholder ')' { init($2); }
|
||||
;
|
||||
|
||||
non_empty_clone_argument_list:
|
||||
expr ',' argument
|
||||
{ init(new Node\Arg($1, false, false, stackAttributes(#1)), $3); }
|
||||
| argument_no_expr
|
||||
{ init($1); }
|
||||
| non_empty_clone_argument_list ',' argument
|
||||
{ push($1, $3); }
|
||||
;
|
||||
|
||||
variadic_placeholder:
|
||||
T_ELLIPSIS { $$ = Node\VariadicPlaceholder[]; }
|
||||
;
|
||||
@ -780,14 +810,18 @@ non_empty_argument_list:
|
||||
| non_empty_argument_list ',' argument { push($1, $3); }
|
||||
;
|
||||
|
||||
argument:
|
||||
expr { $$ = Node\Arg[$1, false, false]; }
|
||||
| ampersand variable { $$ = Node\Arg[$2, true, false]; }
|
||||
argument_no_expr:
|
||||
ampersand variable { $$ = Node\Arg[$2, true, false]; }
|
||||
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
||||
| identifier_maybe_reserved ':' expr
|
||||
{ $$ = new Node\Arg($3, false, false, attributes(), $1); }
|
||||
;
|
||||
|
||||
argument:
|
||||
expr { $$ = Node\Arg[$1, false, false]; }
|
||||
| argument_no_expr { $$ = $1; }
|
||||
;
|
||||
|
||||
global_var_list:
|
||||
non_empty_global_var_list no_comma
|
||||
;
|
||||
@ -828,8 +862,14 @@ class_statement_list:
|
||||
|
||||
class_statement:
|
||||
optional_attributes variable_modifiers optional_type_without_static property_declaration_list semi
|
||||
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1);
|
||||
$this->checkProperty($$, #2); }
|
||||
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1); }
|
||||
#if PHP8
|
||||
| optional_attributes variable_modifiers optional_type_without_static property_declaration_list '{' property_hook_list '}'
|
||||
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1, $6);
|
||||
$this->checkPropertyHooksForMultiProperty($$, #5);
|
||||
$this->checkEmptyPropertyHookList($6, #5);
|
||||
$this->addPropertyNameToHooks($$); }
|
||||
#endif
|
||||
| optional_attributes method_modifiers T_CONST class_const_list semi
|
||||
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
|
||||
$this->checkClassConst($$, #2); }
|
||||
@ -901,6 +941,9 @@ member_modifier:
|
||||
T_PUBLIC { $$ = Modifiers::PUBLIC; }
|
||||
| T_PROTECTED { $$ = Modifiers::PROTECTED; }
|
||||
| T_PRIVATE { $$ = Modifiers::PRIVATE; }
|
||||
| T_PUBLIC_SET { $$ = Modifiers::PUBLIC_SET; }
|
||||
| T_PROTECTED_SET { $$ = Modifiers::PROTECTED_SET; }
|
||||
| T_PRIVATE_SET { $$ = Modifiers::PRIVATE_SET; }
|
||||
| T_STATIC { $$ = Modifiers::STATIC; }
|
||||
| T_ABSTRACT { $$ = Modifiers::ABSTRACT; }
|
||||
| T_FINAL { $$ = Modifiers::FINAL; }
|
||||
@ -926,6 +969,39 @@ property_declaration:
|
||||
| property_decl_name '=' expr { $$ = Node\PropertyItem[$1, $3]; }
|
||||
;
|
||||
|
||||
property_hook_list:
|
||||
/* empty */ { $$ = []; }
|
||||
| property_hook_list property_hook { push($1, $2); }
|
||||
;
|
||||
|
||||
optional_property_hook_list:
|
||||
/* empty */ { $$ = []; }
|
||||
#if PHP8
|
||||
| '{' property_hook_list '}' { $$ = $2; $this->checkEmptyPropertyHookList($2, #1); }
|
||||
#endif
|
||||
;
|
||||
|
||||
property_hook:
|
||||
optional_attributes property_hook_modifiers optional_ref identifier_not_reserved property_hook_body
|
||||
{ $$ = Node\PropertyHook[$4, $5, ['flags' => $2, 'byRef' => $3, 'params' => [], 'attrGroups' => $1]];
|
||||
$this->checkPropertyHook($$, null); }
|
||||
| optional_attributes property_hook_modifiers optional_ref identifier_not_reserved '(' parameter_list ')' property_hook_body
|
||||
{ $$ = Node\PropertyHook[$4, $8, ['flags' => $2, 'byRef' => $3, 'params' => $6, 'attrGroups' => $1]];
|
||||
$this->checkPropertyHook($$, #5); }
|
||||
;
|
||||
|
||||
property_hook_body:
|
||||
';' { $$ = null; }
|
||||
| '{' inner_statement_list '}' { $$ = $2; }
|
||||
| T_DOUBLE_ARROW expr ';' { $$ = $2; }
|
||||
;
|
||||
|
||||
property_hook_modifiers:
|
||||
/* empty */ { $$ = 0; }
|
||||
| property_hook_modifiers member_modifier
|
||||
{ $this->checkPropertyHookModifiers($1, $2, #2); $$ = $1 | $2; }
|
||||
;
|
||||
|
||||
expr_list_forbid_comma:
|
||||
non_empty_expr_list no_comma
|
||||
;
|
||||
@ -959,6 +1035,7 @@ expr:
|
||||
}
|
||||
| new_expr
|
||||
| match
|
||||
| T_CLONE clone_argument_list { $$ = Expr\FuncCall[new Node\Name($1, stackAttributes(#1)), $2]; }
|
||||
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
||||
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
|
||||
| variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; }
|
||||
@ -1030,10 +1107,8 @@ expr:
|
||||
| T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; }
|
||||
| T_BOOL_CAST expr { $$ = Expr\Cast\Bool_ [$2]; }
|
||||
| T_UNSET_CAST expr { $$ = Expr\Cast\Unset_ [$2]; }
|
||||
| T_EXIT exit_expr
|
||||
{ $attrs = attributes();
|
||||
$attrs['kind'] = strtolower($1) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
|
||||
$$ = new Expr\Exit_($2, $attrs); }
|
||||
| T_EXIT ctor_arguments
|
||||
{ $$ = $this->createExitExpr($1, #1, $2, attributes()); }
|
||||
| '@' expr { $$ = Expr\ErrorSuppress[$2]; }
|
||||
| scalar
|
||||
| '`' backticks_expr '`' { $$ = Expr\ShellExec[$2]; }
|
||||
@ -1069,12 +1144,21 @@ anonymous_class:
|
||||
$this->checkClass($$[0], -1); }
|
||||
;
|
||||
|
||||
new_expr:
|
||||
T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; }
|
||||
new_dereferenceable:
|
||||
T_NEW class_name_reference argument_list { $$ = Expr\New_[$2, $3]; }
|
||||
| T_NEW anonymous_class
|
||||
{ list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; }
|
||||
;
|
||||
|
||||
new_non_dereferenceable:
|
||||
T_NEW class_name_reference { $$ = Expr\New_[$2, []]; }
|
||||
;
|
||||
|
||||
new_expr:
|
||||
new_dereferenceable
|
||||
| new_non_dereferenceable
|
||||
;
|
||||
|
||||
lexical_vars:
|
||||
/* empty */ { $$ = array(); }
|
||||
| T_USE '(' lexical_var_list ')' { $$ = $3; }
|
||||
@ -1126,12 +1210,7 @@ class_name_reference:
|
||||
|
||||
class_name_or_var:
|
||||
class_name
|
||||
| fully_dereferencable
|
||||
;
|
||||
|
||||
exit_expr:
|
||||
/* empty */ { $$ = null; }
|
||||
| '(' optional_expr ')' { $$ = $2; }
|
||||
| fully_dereferenceable
|
||||
;
|
||||
|
||||
backticks_expr:
|
||||
@ -1156,6 +1235,7 @@ constant:
|
||||
| T_METHOD_C { $$ = Scalar\MagicConst\Method[]; }
|
||||
| T_FUNC_C { $$ = Scalar\MagicConst\Function_[]; }
|
||||
| T_NS_C { $$ = Scalar\MagicConst\Namespace_[]; }
|
||||
| T_PROPERTY_C { $$ = Scalar\MagicConst\Property[]; }
|
||||
;
|
||||
|
||||
class_constant:
|
||||
@ -1175,7 +1255,7 @@ array_short_syntax:
|
||||
$$ = new Expr\Array_($2, $attrs); }
|
||||
;
|
||||
|
||||
dereferencable_scalar:
|
||||
dereferenceable_scalar:
|
||||
T_ARRAY '(' array_pair_list ')'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
|
||||
$$ = new Expr\Array_($3, $attrs);
|
||||
@ -1192,7 +1272,7 @@ scalar:
|
||||
T_LNUMBER
|
||||
{ $$ = $this->parseLNumber($1, attributes(), $this->phpVersion->allowsInvalidOctals()); }
|
||||
| T_DNUMBER { $$ = Scalar\Float_::fromString($1, attributes()); }
|
||||
| dereferencable_scalar
|
||||
| dereferenceable_scalar
|
||||
| constant
|
||||
| class_constant
|
||||
| T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
|
||||
@ -1208,32 +1288,36 @@ optional_expr:
|
||||
| expr
|
||||
;
|
||||
|
||||
fully_dereferencable:
|
||||
fully_dereferenceable:
|
||||
variable
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| dereferencable_scalar
|
||||
| dereferenceable_scalar
|
||||
| class_constant
|
||||
| new_dereferenceable
|
||||
;
|
||||
|
||||
array_object_dereferencable:
|
||||
fully_dereferencable
|
||||
array_object_dereferenceable:
|
||||
fully_dereferenceable
|
||||
| constant
|
||||
;
|
||||
|
||||
callable_expr:
|
||||
callable_variable
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| dereferencable_scalar
|
||||
| dereferenceable_scalar
|
||||
| new_dereferenceable
|
||||
;
|
||||
|
||||
callable_variable:
|
||||
simple_variable
|
||||
| array_object_dereferencable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| array_object_dereferencable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| array_object_dereferenceable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
#if PHP7
|
||||
| array_object_dereferenceable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
#endif
|
||||
| function_call
|
||||
| array_object_dereferencable T_OBJECT_OPERATOR property_name argument_list
|
||||
| array_object_dereferenceable T_OBJECT_OPERATOR property_name argument_list
|
||||
{ $$ = Expr\MethodCall[$1, $3, $4]; }
|
||||
| array_object_dereferencable T_NULLSAFE_OBJECT_OPERATOR property_name argument_list
|
||||
| array_object_dereferenceable T_NULLSAFE_OBJECT_OPERATOR property_name argument_list
|
||||
{ $$ = Expr\NullsafeMethodCall[$1, $3, $4]; }
|
||||
;
|
||||
|
||||
@ -1245,9 +1329,9 @@ optional_plain_variable:
|
||||
variable:
|
||||
callable_variable
|
||||
| static_member
|
||||
| array_object_dereferencable T_OBJECT_OPERATOR property_name
|
||||
| array_object_dereferenceable T_OBJECT_OPERATOR property_name
|
||||
{ $$ = Expr\PropertyFetch[$1, $3]; }
|
||||
| array_object_dereferencable T_NULLSAFE_OBJECT_OPERATOR property_name
|
||||
| array_object_dereferenceable T_NULLSAFE_OBJECT_OPERATOR property_name
|
||||
{ $$ = Expr\NullsafePropertyFetch[$1, $3]; }
|
||||
;
|
||||
|
||||
@ -1271,7 +1355,9 @@ static_member:
|
||||
new_variable:
|
||||
simple_variable
|
||||
| new_variable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
#if PHP7
|
||||
| new_variable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
#endif
|
||||
| new_variable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; }
|
||||
| new_variable T_NULLSAFE_OBJECT_OPERATOR property_name { $$ = Expr\NullsafePropertyFetch[$1, $3]; }
|
||||
| class_name T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name
|
||||
|
@ -23,6 +23,7 @@ function preprocessGrammar($code) {
|
||||
$code = resolveNodes($code);
|
||||
$code = resolveMacros($code);
|
||||
$code = resolveStackAccess($code);
|
||||
$code = str_replace('$this', '$self', $code);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class ClassConst implements PhpParser\Builder {
|
||||
* Creates a class constant builder
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value Value
|
||||
* @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))];
|
||||
@ -38,7 +38,7 @@ class ClassConst implements PhpParser\Builder {
|
||||
* Add another constant to const group
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value Value
|
||||
* @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value Value
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
|
@ -13,7 +13,6 @@ use PhpParser\Node\Stmt;
|
||||
class EnumCase implements PhpParser\Builder {
|
||||
/** @var Identifier|string */
|
||||
protected $name;
|
||||
/** @var ?Node\Expr */
|
||||
protected ?Node\Expr $value = null;
|
||||
/** @var array<string, mixed> */
|
||||
protected array $attributes = [];
|
||||
|
@ -122,6 +122,28 @@ class Param implements PhpParser\Builder {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the promoted property private(set) visibility.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivateSet() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the promoted property protected(set) visibility.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtectedSet() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
|
@ -23,6 +23,8 @@ class Property implements PhpParser\Builder {
|
||||
protected ?Node $type = null;
|
||||
/** @var list<Node\AttributeGroup> */
|
||||
protected array $attributeGroups = [];
|
||||
/** @var list<Node\PropertyHook> */
|
||||
protected array $hooks = [];
|
||||
|
||||
/**
|
||||
* Creates a property builder.
|
||||
@ -88,6 +90,50 @@ class Property implements PhpParser\Builder {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the property abstract. Requires at least one property hook to be specified as well.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeAbstract() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the property final.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeFinal() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the property private(set) visibility.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivateSet() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the property protected(set) visibility.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtectedSet() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default value for the property.
|
||||
*
|
||||
@ -142,12 +188,27 @@ class Property implements PhpParser\Builder {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a property hook.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addHook(Node\PropertyHook $hook) {
|
||||
$this->hooks[] = $hook;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\Property The built property node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
if ($this->flags & Modifiers::ABSTRACT && !$this->hooks) {
|
||||
throw new PhpParser\Error('Only hooked properties may be declared abstract');
|
||||
}
|
||||
|
||||
return new Stmt\Property(
|
||||
$this->flags !== 0 ? $this->flags : Modifiers::PUBLIC,
|
||||
[
|
||||
@ -155,7 +216,8 @@ class Property implements PhpParser\Builder {
|
||||
],
|
||||
$this->attributes,
|
||||
$this->type,
|
||||
$this->attributeGroups
|
||||
$this->attributeGroups,
|
||||
$this->hooks
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ class BuilderFactory {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function val($value): Expr {
|
||||
return BuilderHelpers::normalizeValue($value);
|
||||
|
@ -6,6 +6,7 @@ use PhpParser\Node\ComplexType;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
@ -214,7 +215,7 @@ final class BuilderHelpers {
|
||||
* Normalizes a value: Converts nulls, booleans, integers,
|
||||
* 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
|
||||
*/
|
||||
@ -268,6 +269,10 @@ final class BuilderHelpers {
|
||||
return new Expr\Array_($items);
|
||||
}
|
||||
|
||||
if ($value instanceof \UnitEnum) {
|
||||
return new Expr\ClassConstFetch(new FullyQualified(\get_class($value)), new Identifier($value->name));
|
||||
}
|
||||
|
||||
throw new \LogicException('Invalid value');
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@ class Comment implements \JsonSerializable {
|
||||
* Gets the line number the comment started on.
|
||||
*
|
||||
* @return int Line number (or -1 if not available)
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getStartLine(): int {
|
||||
return $this->startLine;
|
||||
@ -73,6 +74,7 @@ class Comment implements \JsonSerializable {
|
||||
* Gets the line number the comment ends on.
|
||||
*
|
||||
* @return int Line number (or -1 if not available)
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getEndLine(): int {
|
||||
return $this->endLine;
|
||||
|
@ -32,6 +32,7 @@ class Error extends \RuntimeException {
|
||||
* Gets the line the error starts in.
|
||||
*
|
||||
* @return int Error start line
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getStartLine(): int {
|
||||
return $this->attributes['startLine'] ?? -1;
|
||||
@ -41,6 +42,7 @@ class Error extends \RuntimeException {
|
||||
* Gets the line the error ends in.
|
||||
*
|
||||
* @return int Error end line
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getEndLine(): int {
|
||||
return $this->attributes['endLine'] ?? -1;
|
||||
|
@ -20,9 +20,9 @@ class TokenStream {
|
||||
*
|
||||
* @param Token[] $tokens Tokens in PhpToken::tokenize() format
|
||||
*/
|
||||
public function __construct(array $tokens) {
|
||||
public function __construct(array $tokens, int $tabWidth) {
|
||||
$this->tokens = $tokens;
|
||||
$this->indentMap = $this->calcIndentMap();
|
||||
$this->indentMap = $this->calcIndentMap($tabWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,17 +248,21 @@ class TokenStream {
|
||||
*
|
||||
* @return int[] Token position to indentation map
|
||||
*/
|
||||
private function calcIndentMap(): array {
|
||||
private function calcIndentMap(int $tabWidth): array {
|
||||
$indentMap = [];
|
||||
$indent = 0;
|
||||
foreach ($this->tokens as $token) {
|
||||
foreach ($this->tokens as $i => $token) {
|
||||
$indentMap[] = $indent;
|
||||
|
||||
if ($token->id === \T_WHITESPACE) {
|
||||
$content = $token->text;
|
||||
$newlinePos = \strrpos($content, "\n");
|
||||
if (false !== $newlinePos) {
|
||||
$indent = \strlen($content) - $newlinePos - 1;
|
||||
$indent = $this->getIndent(\substr($content, $newlinePos + 1), $tabWidth);
|
||||
} elseif ($i === 1 && $this->tokens[0]->id === \T_OPEN_TAG &&
|
||||
$this->tokens[0]->text[\strlen($this->tokens[0]->text) - 1] === "\n") {
|
||||
// Special case: Newline at the end of opening tag followed by whitespace.
|
||||
$indent = $this->getIndent($content, $tabWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -268,4 +272,11 @@ class TokenStream {
|
||||
|
||||
return $indentMap;
|
||||
}
|
||||
|
||||
private function getIndent(string $ws, int $tabWidth): int {
|
||||
$spaces = \substr_count($ws, " ");
|
||||
$tabs = \substr_count($ws, "\t");
|
||||
assert(\strlen($ws) === $spaces + $tabs);
|
||||
return $spaces + $tabs * $tabWidth;
|
||||
}
|
||||
}
|
||||
|
@ -5,15 +5,13 @@ namespace PhpParser\Lexer;
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Lexer\TokenEmulator\AsymmetricVisibilityTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\PropertyTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
||||
@ -47,6 +45,8 @@ class Emulative extends Lexer {
|
||||
new ReadonlyTokenEmulator(),
|
||||
new ExplicitOctalEmulator(),
|
||||
new ReadonlyFunctionTokenEmulator(),
|
||||
new PropertyTokenEmulator(),
|
||||
new AsymmetricVisibilityTokenEmulator(),
|
||||
];
|
||||
|
||||
// Collect emulators that are relevant for the PHP version we're running
|
||||
|
@ -0,0 +1,93 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\PhpVersion;
|
||||
use PhpParser\Token;
|
||||
|
||||
final class AsymmetricVisibilityTokenEmulator extends TokenEmulator {
|
||||
public function getPhpVersion(): PhpVersion {
|
||||
return PhpVersion::fromComponents(8, 4);
|
||||
}
|
||||
public function isEmulationNeeded(string $code): bool {
|
||||
$code = strtolower($code);
|
||||
return strpos($code, 'public(set)') !== false ||
|
||||
strpos($code, 'protected(set)') !== false ||
|
||||
strpos($code, 'private(set)') !== false;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array {
|
||||
$map = [
|
||||
\T_PUBLIC => \T_PUBLIC_SET,
|
||||
\T_PROTECTED => \T_PROTECTED_SET,
|
||||
\T_PRIVATE => \T_PRIVATE_SET,
|
||||
];
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
$token = $tokens[$i];
|
||||
if (isset($map[$token->id]) && $i + 3 < $c && $tokens[$i + 1]->text === '(' &&
|
||||
$tokens[$i + 2]->id === \T_STRING && \strtolower($tokens[$i + 2]->text) === 'set' &&
|
||||
$tokens[$i + 3]->text === ')' &&
|
||||
$this->isKeywordContext($tokens, $i)
|
||||
) {
|
||||
array_splice($tokens, $i, 4, [
|
||||
new Token(
|
||||
$map[$token->id], $token->text . '(' . $tokens[$i + 2]->text . ')',
|
||||
$token->line, $token->pos),
|
||||
]);
|
||||
$c -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array {
|
||||
$reverseMap = [
|
||||
\T_PUBLIC_SET => \T_PUBLIC,
|
||||
\T_PROTECTED_SET => \T_PROTECTED,
|
||||
\T_PRIVATE_SET => \T_PRIVATE,
|
||||
];
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
$token = $tokens[$i];
|
||||
if (isset($reverseMap[$token->id]) &&
|
||||
\preg_match('/(public|protected|private)\((set)\)/i', $token->text, $matches)
|
||||
) {
|
||||
[, $modifier, $set] = $matches;
|
||||
$modifierLen = \strlen($modifier);
|
||||
array_splice($tokens, $i, 1, [
|
||||
new Token($reverseMap[$token->id], $modifier, $token->line, $token->pos),
|
||||
new Token(\ord('('), '(', $token->line, $token->pos + $modifierLen),
|
||||
new Token(\T_STRING, $set, $token->line, $token->pos + $modifierLen + 1),
|
||||
new Token(\ord(')'), ')', $token->line, $token->pos + $modifierLen + 4),
|
||||
]);
|
||||
$i += 3;
|
||||
$c += 3;
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/** @param Token[] $tokens */
|
||||
protected function isKeywordContext(array $tokens, int $pos): bool {
|
||||
$prevToken = $this->getPreviousNonSpaceToken($tokens, $pos);
|
||||
if ($prevToken === null) {
|
||||
return false;
|
||||
}
|
||||
return $prevToken->id !== \T_OBJECT_OPERATOR
|
||||
&& $prevToken->id !== \T_NULLSAFE_OBJECT_OPERATOR;
|
||||
}
|
||||
|
||||
/** @param Token[] $tokens */
|
||||
private function getPreviousNonSpaceToken(array $tokens, int $start): ?Token {
|
||||
for ($i = $start - 1; $i >= 0; --$i) {
|
||||
if ($tokens[$i]->id === T_WHITESPACE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $tokens[$i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -14,8 +14,12 @@ abstract class KeywordEmulator extends TokenEmulator {
|
||||
|
||||
/** @param Token[] $tokens */
|
||||
protected function isKeywordContext(array $tokens, int $pos): bool {
|
||||
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos);
|
||||
return $previousNonSpaceToken === null || $previousNonSpaceToken->id !== \T_OBJECT_OPERATOR;
|
||||
$prevToken = $this->getPreviousNonSpaceToken($tokens, $pos);
|
||||
if ($prevToken === null) {
|
||||
return false;
|
||||
}
|
||||
return $prevToken->id !== \T_OBJECT_OPERATOR
|
||||
&& $prevToken->id !== \T_NULLSAFE_OBJECT_OPERATOR;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array {
|
||||
|
19
lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php
Normal file
19
lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\PhpVersion;
|
||||
|
||||
final class PropertyTokenEmulator extends KeywordEmulator {
|
||||
public function getPhpVersion(): PhpVersion {
|
||||
return PhpVersion::fromComponents(8, 4);
|
||||
}
|
||||
|
||||
public function getKeywordString(): string {
|
||||
return '__property__';
|
||||
}
|
||||
|
||||
public function getKeywordToken(): int {
|
||||
return \T_PROPERTY_C;
|
||||
}
|
||||
}
|
@ -14,23 +14,47 @@ final class Modifiers {
|
||||
public const ABSTRACT = 16;
|
||||
public const FINAL = 32;
|
||||
public const READONLY = 64;
|
||||
public const PUBLIC_SET = 128;
|
||||
public const PROTECTED_SET = 256;
|
||||
public const PRIVATE_SET = 512;
|
||||
|
||||
public const VISIBILITY_MASK = 1 | 2 | 4;
|
||||
public const VISIBILITY_MASK = self::PUBLIC | self::PROTECTED | self::PRIVATE;
|
||||
|
||||
public const VISIBILITY_SET_MASK = self::PUBLIC_SET | self::PROTECTED_SET | self::PRIVATE_SET;
|
||||
|
||||
private const TO_STRING_MAP = [
|
||||
self::PUBLIC => 'public',
|
||||
self::PROTECTED => 'protected',
|
||||
self::PRIVATE => 'private',
|
||||
self::STATIC => 'static',
|
||||
self::ABSTRACT => 'abstract',
|
||||
self::FINAL => 'final',
|
||||
self::READONLY => 'readonly',
|
||||
self::PUBLIC_SET => 'public(set)',
|
||||
self::PROTECTED_SET => 'protected(set)',
|
||||
self::PRIVATE_SET => 'private(set)',
|
||||
];
|
||||
|
||||
public static function toString(int $modifier): string {
|
||||
if (!isset(self::TO_STRING_MAP[$modifier])) {
|
||||
throw new \InvalidArgumentException("Unknown modifier $modifier");
|
||||
}
|
||||
return self::TO_STRING_MAP[$modifier];
|
||||
}
|
||||
|
||||
private static function isValidModifier(int $modifier): bool {
|
||||
$isPow2 = ($modifier & ($modifier - 1)) == 0 && $modifier != 0;
|
||||
return $isPow2 && $modifier <= self::PRIVATE_SET;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function verifyClassModifier(int $a, int $b): void {
|
||||
if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) {
|
||||
throw new Error('Multiple abstract modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) {
|
||||
throw new Error('Multiple final modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) {
|
||||
throw new Error('Multiple readonly modifiers are not allowed');
|
||||
assert(self::isValidModifier($b));
|
||||
if (($a & $b) != 0) {
|
||||
throw new Error(
|
||||
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & 48 && $b & 48) {
|
||||
@ -42,24 +66,16 @@ final class Modifiers {
|
||||
* @internal
|
||||
*/
|
||||
public static function verifyModifier(int $a, int $b): void {
|
||||
if ($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) {
|
||||
assert(self::isValidModifier($b));
|
||||
if (($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) ||
|
||||
($a & Modifiers::VISIBILITY_SET_MASK && $b & Modifiers::VISIBILITY_SET_MASK)
|
||||
) {
|
||||
throw new Error('Multiple access type modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) {
|
||||
throw new Error('Multiple abstract modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::STATIC && $b & Modifiers::STATIC) {
|
||||
throw new Error('Multiple static modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) {
|
||||
throw new Error('Multiple final modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) {
|
||||
throw new Error('Multiple readonly modifiers are not allowed');
|
||||
if (($a & $b) != 0) {
|
||||
throw new Error(
|
||||
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & 48 && $b & 48) {
|
||||
|
@ -185,7 +185,7 @@ class NameContext {
|
||||
// Check for relevant type-specific use statements
|
||||
foreach ($this->origAliases[$type] as $alias => $orig) {
|
||||
if ($type === Stmt\Use_::TYPE_CONSTANT) {
|
||||
// Constants are are complicated-sensitive
|
||||
// Constants are complicated-sensitive
|
||||
$normalizedOrig = $this->normalizeConstName($orig->toString());
|
||||
if ($normalizedOrig === $this->normalizeConstName($name)) {
|
||||
$possibleNames[] = new Name($alias);
|
||||
|
@ -6,6 +6,7 @@ interface Node {
|
||||
/**
|
||||
* Gets the type of the node.
|
||||
*
|
||||
* @psalm-return non-empty-string
|
||||
* @return string Type of the node
|
||||
*/
|
||||
public function getType(): string;
|
||||
@ -21,6 +22,7 @@ interface Node {
|
||||
* Gets line the node started in (alias of getStartLine).
|
||||
*
|
||||
* @return int Start line (or -1 if not available)
|
||||
* @phpstan-return -1|positive-int
|
||||
*
|
||||
* @deprecated Use getStartLine() instead
|
||||
*/
|
||||
@ -32,6 +34,7 @@ interface Node {
|
||||
* Requires the 'startLine' attribute to be enabled in the lexer (enabled by default).
|
||||
*
|
||||
* @return int Start line (or -1 if not available)
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getStartLine(): int;
|
||||
|
||||
@ -41,6 +44,7 @@ interface Node {
|
||||
* Requires the 'endLine' attribute to be enabled in the lexer (enabled by default).
|
||||
*
|
||||
* @return int End line (or -1 if not available)
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getEndLine(): int;
|
||||
|
||||
|
@ -1,3 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Expr;
|
||||
|
||||
require __DIR__ . '/../ArrayItem.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class ArrayItem extends \PhpParser\Node\ArrayItem {
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Expr;
|
||||
|
||||
require __DIR__ . '/../ClosureUse.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class ClosureUse extends \PhpParser\Node\ClosureUse {
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ use PhpParser\NodeAbstract;
|
||||
* Represents a non-namespaced name. Namespaced names are represented using Name nodes.
|
||||
*/
|
||||
class Identifier extends NodeAbstract {
|
||||
/** @var string Identifier as string */
|
||||
/**
|
||||
* @psalm-var non-empty-string
|
||||
* @var string Identifier as string
|
||||
*/
|
||||
public string $name;
|
||||
|
||||
/** @var array<string, bool> */
|
||||
@ -25,6 +28,10 @@ class Identifier extends NodeAbstract {
|
||||
* @param array<string, mixed> $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(string $name, array $attributes = []) {
|
||||
if ($name === '') {
|
||||
throw new \InvalidArgumentException('Identifier name cannot be empty');
|
||||
}
|
||||
|
||||
$this->attributes = $attributes;
|
||||
$this->name = $name;
|
||||
}
|
||||
@ -36,6 +43,7 @@ class Identifier extends NodeAbstract {
|
||||
/**
|
||||
* Get identifier as string.
|
||||
*
|
||||
* @psalm-return non-empty-string
|
||||
* @return string Identifier as string.
|
||||
*/
|
||||
public function toString(): string {
|
||||
@ -45,6 +53,7 @@ class Identifier extends NodeAbstract {
|
||||
/**
|
||||
* Get lowercased identifier as string.
|
||||
*
|
||||
* @psalm-return non-empty-string&lowercase-string
|
||||
* @return string Lowercased identifier as string
|
||||
*/
|
||||
public function toLowerString(): string {
|
||||
@ -63,6 +72,7 @@ class Identifier extends NodeAbstract {
|
||||
/**
|
||||
* Get identifier as string.
|
||||
*
|
||||
* @psalm-return non-empty-string
|
||||
* @return string Identifier as string
|
||||
*/
|
||||
public function __toString(): string {
|
||||
|
@ -8,7 +8,6 @@ use PhpParser\NodeAbstract;
|
||||
class MatchArm extends NodeAbstract {
|
||||
/** @var null|list<Node\Expr> */
|
||||
public ?array $conds;
|
||||
/** @var Node\Expr */
|
||||
public Expr $body;
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,10 @@ namespace PhpParser\Node;
|
||||
use PhpParser\NodeAbstract;
|
||||
|
||||
class Name extends NodeAbstract {
|
||||
/** @var string Name as string */
|
||||
/**
|
||||
* @psalm-var non-empty-string
|
||||
* @var string Name as string
|
||||
*/
|
||||
public string $name;
|
||||
|
||||
/** @var array<string, bool> */
|
||||
@ -33,6 +36,7 @@ class Name extends NodeAbstract {
|
||||
/**
|
||||
* Get parts of name (split by the namespace separator).
|
||||
*
|
||||
* @psalm-return non-empty-list<string>
|
||||
* @return string[] Parts of name
|
||||
*/
|
||||
public function getParts(): array {
|
||||
@ -103,6 +107,7 @@ class Name extends NodeAbstract {
|
||||
* Returns a string representation of the name itself, without taking the name type into
|
||||
* account (e.g., not including a leading backslash for fully qualified names).
|
||||
*
|
||||
* @psalm-return non-empty-string
|
||||
* @return string String representation
|
||||
*/
|
||||
public function toString(): string {
|
||||
@ -113,6 +118,7 @@ class Name extends NodeAbstract {
|
||||
* Returns a string representation of the name as it would occur in code (e.g., including
|
||||
* leading backslash for fully qualified names.
|
||||
*
|
||||
* @psalm-return non-empty-string
|
||||
* @return string String representation
|
||||
*/
|
||||
public function toCodeString(): string {
|
||||
@ -123,6 +129,7 @@ class Name extends NodeAbstract {
|
||||
* Returns lowercased string representation of the name, without taking the name type into
|
||||
* account (e.g., no leading backslash for fully qualified names).
|
||||
*
|
||||
* @psalm-return non-empty-string&lowercase-string
|
||||
* @return string Lowercased string representation
|
||||
*/
|
||||
public function toLowerString(): string {
|
||||
@ -142,6 +149,7 @@ class Name extends NodeAbstract {
|
||||
* Returns a string representation of the name by imploding the namespace parts with the
|
||||
* namespace separator.
|
||||
*
|
||||
* @psalm-return non-empty-string
|
||||
* @return string String representation
|
||||
*/
|
||||
public function __toString(): string {
|
||||
@ -237,6 +245,7 @@ class Name extends NodeAbstract {
|
||||
*
|
||||
* @param string|string[]|self $name Name to prepare
|
||||
*
|
||||
* @psalm-return non-empty-string
|
||||
* @return string Prepared name
|
||||
*/
|
||||
private static function prepareName($name): string {
|
||||
|
@ -21,6 +21,8 @@ class Param extends NodeAbstract {
|
||||
public int $flags;
|
||||
/** @var AttributeGroup[] PHP attribute groups */
|
||||
public array $attrGroups;
|
||||
/** @var PropertyHook[] Property hooks for promoted properties */
|
||||
public array $hooks;
|
||||
|
||||
/**
|
||||
* Constructs a parameter node.
|
||||
@ -33,13 +35,15 @@ class Param extends NodeAbstract {
|
||||
* @param array<string, mixed> $attributes Additional attributes
|
||||
* @param int $flags Optional visibility flags
|
||||
* @param list<AttributeGroup> $attrGroups PHP attribute groups
|
||||
* @param PropertyHook[] $hooks Property hooks for promoted properties
|
||||
*/
|
||||
public function __construct(
|
||||
Expr $var, ?Expr $default = null, ?Node $type = null,
|
||||
bool $byRef = false, bool $variadic = false,
|
||||
array $attributes = [],
|
||||
int $flags = 0,
|
||||
array $attrGroups = []
|
||||
array $attrGroups = [],
|
||||
array $hooks = []
|
||||
) {
|
||||
$this->attributes = $attributes;
|
||||
$this->type = $type;
|
||||
@ -49,10 +53,11 @@ class Param extends NodeAbstract {
|
||||
$this->default = $default;
|
||||
$this->flags = $flags;
|
||||
$this->attrGroups = $attrGroups;
|
||||
$this->hooks = $hooks;
|
||||
}
|
||||
|
||||
public function getSubNodeNames(): array {
|
||||
return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default'];
|
||||
return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default', 'hooks'];
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
@ -63,11 +68,20 @@ class Param extends NodeAbstract {
|
||||
* Whether this parameter uses constructor property promotion.
|
||||
*/
|
||||
public function isPromoted(): bool {
|
||||
return $this->flags !== 0;
|
||||
return $this->flags !== 0 || $this->hooks !== [];
|
||||
}
|
||||
|
||||
public function isPublic(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PUBLIC);
|
||||
$public = (bool) ($this->flags & Modifiers::PUBLIC);
|
||||
if ($public) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->hooks === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
|
||||
}
|
||||
|
||||
public function isProtected(): bool {
|
||||
@ -81,4 +95,25 @@ class Param extends NodeAbstract {
|
||||
public function isReadonly(): bool {
|
||||
return (bool) ($this->flags & Modifiers::READONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the promoted property has explicit public(set) visibility.
|
||||
*/
|
||||
public function isPublicSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PUBLIC_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the promoted property has explicit protected(set) visibility.
|
||||
*/
|
||||
public function isProtectedSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PROTECTED_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the promoted property has explicit private(set) visibility.
|
||||
*/
|
||||
public function isPrivateSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PRIVATE_SET);
|
||||
}
|
||||
}
|
||||
|
105
lib/PhpParser/Node/PropertyHook.php
Normal file
105
lib/PhpParser/Node/PropertyHook.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node;
|
||||
|
||||
use PhpParser\Modifiers;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\NodeAbstract;
|
||||
|
||||
class PropertyHook extends NodeAbstract implements FunctionLike {
|
||||
/** @var AttributeGroup[] PHP attribute groups */
|
||||
public array $attrGroups;
|
||||
/** @var int Modifiers */
|
||||
public int $flags;
|
||||
/** @var bool Whether hook returns by reference */
|
||||
public bool $byRef;
|
||||
/** @var Identifier Hook name */
|
||||
public Identifier $name;
|
||||
/** @var Param[] Parameters */
|
||||
public array $params;
|
||||
/** @var null|Expr|Stmt[] Hook body */
|
||||
public $body;
|
||||
|
||||
/**
|
||||
* Constructs a property hook node.
|
||||
*
|
||||
* @param string|Identifier $name Hook name
|
||||
* @param null|Expr|Stmt[] $body Hook body
|
||||
* @param array{
|
||||
* flags?: int,
|
||||
* byRef?: bool,
|
||||
* params?: Param[],
|
||||
* attrGroups?: AttributeGroup[],
|
||||
* } $subNodes Array of the following optional subnodes:
|
||||
* 'flags => 0 : Flags
|
||||
* 'byRef' => false : Whether hook returns by reference
|
||||
* 'params' => array(): Parameters
|
||||
* 'attrGroups' => array(): PHP attribute groups
|
||||
* @param array<string, mixed> $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, $body, array $subNodes = [], array $attributes = []) {
|
||||
$this->attributes = $attributes;
|
||||
$this->name = \is_string($name) ? new Identifier($name) : $name;
|
||||
$this->body = $body;
|
||||
$this->flags = $subNodes['flags'] ?? 0;
|
||||
$this->byRef = $subNodes['byRef'] ?? false;
|
||||
$this->params = $subNodes['params'] ?? [];
|
||||
$this->attrGroups = $subNodes['attrGroups'] ?? [];
|
||||
}
|
||||
|
||||
public function returnsByRef(): bool {
|
||||
return $this->byRef;
|
||||
}
|
||||
|
||||
public function getParams(): array {
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function getReturnType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property hook is final.
|
||||
*/
|
||||
public function isFinal(): bool {
|
||||
return (bool) ($this->flags & Modifiers::FINAL);
|
||||
}
|
||||
|
||||
public function getStmts(): ?array {
|
||||
if ($this->body instanceof Expr) {
|
||||
$name = $this->name->toLowerString();
|
||||
if ($name === 'get') {
|
||||
return [new Return_($this->body)];
|
||||
}
|
||||
if ($name === 'set') {
|
||||
if (!$this->hasAttribute('propertyName')) {
|
||||
throw new \LogicException(
|
||||
'Can only use getStmts() on a "set" hook if the "propertyName" attribute is set');
|
||||
}
|
||||
|
||||
$propName = $this->getAttribute('propertyName');
|
||||
$prop = new PropertyFetch(new Variable('this'), (string) $propName);
|
||||
return [new Expression(new Assign($prop, $this->body))];
|
||||
}
|
||||
throw new \LogicException('Unknown property hook "' . $name . '"');
|
||||
}
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
public function getAttrGroups(): array {
|
||||
return $this->attrGroups;
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return 'PropertyHook';
|
||||
}
|
||||
|
||||
public function getSubNodeNames(): array {
|
||||
return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'body'];
|
||||
}
|
||||
}
|
@ -1,3 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
require __DIR__ . '/Float_.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class DNumber extends Float_ {
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
require __DIR__ . '/InterpolatedString.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class Encapsed extends InterpolatedString {
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
use PhpParser\Node\InterpolatedStringPart;
|
||||
|
||||
require __DIR__ . '/../InterpolatedStringPart.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class EncapsedStringPart extends InterpolatedStringPart {
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
require __DIR__ . '/Int_.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class LNumber extends Int_ {
|
||||
}
|
||||
}
|
||||
|
15
lib/PhpParser/Node/Scalar/MagicConst/Property.php
Normal file
15
lib/PhpParser/Node/Scalar/MagicConst/Property.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Scalar\MagicConst;
|
||||
|
||||
use PhpParser\Node\Scalar\MagicConst;
|
||||
|
||||
class Property extends MagicConst {
|
||||
public function getName(): string {
|
||||
return '__PROPERTY__';
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return 'Scalar_MagicConst_Property';
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ abstract class ClassLike extends Node\Stmt {
|
||||
public ?Node\Name $namespacedName;
|
||||
|
||||
/**
|
||||
* @return TraitUse[]
|
||||
* @return list<TraitUse>
|
||||
*/
|
||||
public function getTraitUses(): array {
|
||||
$traitUses = [];
|
||||
@ -30,7 +30,7 @@ abstract class ClassLike extends Node\Stmt {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassConst[]
|
||||
* @return list<ClassConst>
|
||||
*/
|
||||
public function getConstants(): array {
|
||||
$constants = [];
|
||||
@ -43,7 +43,7 @@ abstract class ClassLike extends Node\Stmt {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Property[]
|
||||
* @return list<Property>
|
||||
*/
|
||||
public function getProperties(): array {
|
||||
$properties = [];
|
||||
@ -78,7 +78,7 @@ abstract class ClassLike extends Node\Stmt {
|
||||
/**
|
||||
* Gets all methods defined directly in this class/interface/trait
|
||||
*
|
||||
* @return ClassMethod[]
|
||||
* @return list<ClassMethod>
|
||||
*/
|
||||
public function getMethods(): array {
|
||||
$methods = [];
|
||||
|
@ -7,20 +7,28 @@ use PhpParser\Node;
|
||||
class Const_ extends Node\Stmt {
|
||||
/** @var Node\Const_[] Constant declarations */
|
||||
public array $consts;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public array $attrGroups;
|
||||
|
||||
/**
|
||||
* Constructs a const list node.
|
||||
*
|
||||
* @param Node\Const_[] $consts Constant declarations
|
||||
* @param array<string, mixed> $attributes Additional attributes
|
||||
* @param list<Node\AttributeGroup> $attrGroups PHP attribute groups
|
||||
*/
|
||||
public function __construct(array $consts, array $attributes = []) {
|
||||
public function __construct(
|
||||
array $consts,
|
||||
array $attributes = [],
|
||||
array $attrGroups = []
|
||||
) {
|
||||
$this->attributes = $attributes;
|
||||
$this->attrGroups = $attrGroups;
|
||||
$this->consts = $consts;
|
||||
}
|
||||
|
||||
public function getSubNodeNames(): array {
|
||||
return ['consts'];
|
||||
return ['attrGroups', 'consts'];
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
|
@ -1,3 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node\DeclareItem;
|
||||
|
||||
require __DIR__ . '/../DeclareItem.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class DeclareDeclare extends DeclareItem {
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ class Property extends Node\Stmt {
|
||||
public ?Node $type;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public array $attrGroups;
|
||||
/** @var Node\PropertyHook[] Property hooks */
|
||||
public array $hooks;
|
||||
|
||||
/**
|
||||
* Constructs a class property list node.
|
||||
@ -27,17 +29,19 @@ class Property extends Node\Stmt {
|
||||
* @param array<string, mixed> $attributes Additional attributes
|
||||
* @param null|Identifier|Name|ComplexType $type Type declaration
|
||||
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
|
||||
* @param Node\PropertyHook[] $hooks Property hooks
|
||||
*/
|
||||
public function __construct(int $flags, array $props, array $attributes = [], ?Node $type = null, array $attrGroups = []) {
|
||||
public function __construct(int $flags, array $props, array $attributes = [], ?Node $type = null, array $attrGroups = [], array $hooks = []) {
|
||||
$this->attributes = $attributes;
|
||||
$this->flags = $flags;
|
||||
$this->props = $props;
|
||||
$this->type = $type;
|
||||
$this->attrGroups = $attrGroups;
|
||||
$this->hooks = $hooks;
|
||||
}
|
||||
|
||||
public function getSubNodeNames(): array {
|
||||
return ['attrGroups', 'flags', 'type', 'props'];
|
||||
return ['attrGroups', 'flags', 'type', 'props', 'hooks'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,6 +80,41 @@ class Property extends Node\Stmt {
|
||||
return (bool) ($this->flags & Modifiers::READONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property is abstract.
|
||||
*/
|
||||
public function isAbstract(): bool {
|
||||
return (bool) ($this->flags & Modifiers::ABSTRACT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property is final.
|
||||
*/
|
||||
public function isFinal(): bool {
|
||||
return (bool) ($this->flags & Modifiers::FINAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property has explicit public(set) visibility.
|
||||
*/
|
||||
public function isPublicSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PUBLIC_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property has explicit protected(set) visibility.
|
||||
*/
|
||||
public function isProtectedSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PROTECTED_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property has explicit private(set) visibility.
|
||||
*/
|
||||
public function isPrivateSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PRIVATE_SET);
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return 'Stmt_Property';
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node\PropertyItem;
|
||||
|
||||
require __DIR__ . '/../PropertyItem.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class PropertyProperty extends PropertyItem {
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
require __DIR__ . '/../StaticVar.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class StaticVar extends \PhpParser\Node\StaticVar {
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node\UseItem;
|
||||
|
||||
require __DIR__ . '/../UseItem.php';
|
||||
|
||||
if (false) {
|
||||
// For classmap-authoritative support.
|
||||
class UseUse extends UseItem {
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ abstract class NodeAbstract implements Node, \JsonSerializable {
|
||||
* Gets line the node started in (alias of getStartLine).
|
||||
*
|
||||
* @return int Start line (or -1 if not available)
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getLine(): int {
|
||||
return $this->attributes['startLine'] ?? -1;
|
||||
@ -30,6 +31,7 @@ abstract class NodeAbstract implements Node, \JsonSerializable {
|
||||
* Requires the 'startLine' attribute to be enabled in the lexer (enabled by default).
|
||||
*
|
||||
* @return int Start line (or -1 if not available)
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getStartLine(): int {
|
||||
return $this->attributes['startLine'] ?? -1;
|
||||
@ -41,6 +43,7 @@ abstract class NodeAbstract implements Node, \JsonSerializable {
|
||||
* Requires the 'endLine' attribute to be enabled in the lexer (enabled by default).
|
||||
*
|
||||
* @return int End line (or -1 if not available)
|
||||
* @phpstan-return -1|positive-int
|
||||
*/
|
||||
public function getEndLine(): int {
|
||||
return $this->attributes['endLine'] ?? -1;
|
||||
|
@ -145,7 +145,7 @@ class NodeDumper {
|
||||
} elseif ($node instanceof Comment) {
|
||||
$this->res .= \str_replace("\n", $this->nl, $node->getReformattedText());
|
||||
} elseif (\is_string($node)) {
|
||||
$this->res .= \str_replace("\n", $this->nl, (string)$node);
|
||||
$this->res .= \str_replace("\n", $this->nl, $node);
|
||||
} elseif (\is_int($node) || \is_float($node)) {
|
||||
$this->res .= $node;
|
||||
} elseif (null === $node) {
|
||||
@ -185,6 +185,15 @@ class NodeDumper {
|
||||
if ($flags & Modifiers::READONLY) {
|
||||
$strs[] = 'READONLY';
|
||||
}
|
||||
if ($flags & Modifiers::PUBLIC_SET) {
|
||||
$strs[] = 'PUBLIC_SET';
|
||||
}
|
||||
if ($flags & Modifiers::PROTECTED_SET) {
|
||||
$strs[] = 'PROTECTED_SET';
|
||||
}
|
||||
if ($flags & Modifiers::PRIVATE_SET) {
|
||||
$strs[] = 'PRIVATE_SET';
|
||||
}
|
||||
|
||||
if ($strs) {
|
||||
return implode(' | ', $strs) . ' (' . $flags . ')';
|
||||
|
@ -99,7 +99,14 @@ class NodeTraverser implements NodeTraverserInterface {
|
||||
if ($this->stopTraversal) {
|
||||
break;
|
||||
}
|
||||
} elseif ($subNode instanceof Node) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$subNode instanceof Node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$traverseChildren = true;
|
||||
$visitorIndex = -1;
|
||||
|
||||
@ -163,7 +170,6 @@ class NodeTraverser implements NodeTraverserInterface {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively traverse array (usually of nodes).
|
||||
@ -176,7 +182,13 @@ class NodeTraverser implements NodeTraverserInterface {
|
||||
$doNodes = [];
|
||||
|
||||
foreach ($nodes as $i => $node) {
|
||||
if ($node instanceof Node) {
|
||||
if (!$node instanceof Node) {
|
||||
if (\is_array($node)) {
|
||||
throw new \LogicException('Invalid node structure: Contains nested arrays');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$traverseChildren = true;
|
||||
$visitorIndex = -1;
|
||||
|
||||
@ -245,9 +257,6 @@ class NodeTraverser implements NodeTraverserInterface {
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (\is_array($node)) {
|
||||
throw new \LogicException('Invalid node structure: Contains nested arrays');
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($doNodes)) {
|
||||
|
@ -12,7 +12,7 @@ use PhpParser\NodeVisitorAbstract;
|
||||
class FindingVisitor extends NodeVisitorAbstract {
|
||||
/** @var callable Filter callback */
|
||||
protected $filterCallback;
|
||||
/** @var Node[] Found nodes */
|
||||
/** @var list<Node> Found nodes */
|
||||
protected array $foundNodes;
|
||||
|
||||
public function __construct(callable $filterCallback) {
|
||||
@ -24,7 +24,7 @@ class FindingVisitor extends NodeVisitorAbstract {
|
||||
*
|
||||
* Nodes are returned in pre-order.
|
||||
*
|
||||
* @return Node[] Found nodes
|
||||
* @return list<Node> Found nodes
|
||||
*/
|
||||
public function getFoundNodes(): array {
|
||||
return $this->foundNodes;
|
||||
|
@ -110,10 +110,17 @@ class NameResolver extends NodeVisitorAbstract {
|
||||
$node->type = $this->resolveType($node->type);
|
||||
}
|
||||
$this->resolveAttrGroups($node);
|
||||
} elseif ($node instanceof Node\PropertyHook) {
|
||||
foreach ($node->params as $param) {
|
||||
$param->type = $this->resolveType($param->type);
|
||||
$this->resolveAttrGroups($param);
|
||||
}
|
||||
$this->resolveAttrGroups($node);
|
||||
} elseif ($node instanceof Stmt\Const_) {
|
||||
foreach ($node->consts as $const) {
|
||||
$this->addNamespacedName($const);
|
||||
}
|
||||
$this->resolveAttrGroups($node);
|
||||
} elseif ($node instanceof Stmt\ClassConst) {
|
||||
if (null !== $node->type) {
|
||||
$node->type = $this->resolveType($node->type);
|
||||
|
@ -9,10 +9,12 @@ use PhpParser\NodeVisitorAbstract;
|
||||
* Visitor that connects a child node to its parent node
|
||||
* as well as its sibling nodes.
|
||||
*
|
||||
* On the child node, the parent node can be accessed through
|
||||
* With <code>$weakReferences=false</code> on the child node, the parent node can be accessed through
|
||||
* <code>$node->getAttribute('parent')</code>, the previous
|
||||
* node can be accessed through <code>$node->getAttribute('previous')</code>,
|
||||
* and the next node can be accessed through <code>$node->getAttribute('next')</code>.
|
||||
*
|
||||
* With <code>$weakReferences=true</code> attribute names are prefixed by "weak_", e.g. "weak_parent".
|
||||
*/
|
||||
final class NodeConnectingVisitor extends NodeVisitorAbstract {
|
||||
/**
|
||||
@ -25,6 +27,12 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract {
|
||||
*/
|
||||
private $previous;
|
||||
|
||||
private bool $weakReferences;
|
||||
|
||||
public function __construct(bool $weakReferences = false) {
|
||||
$this->weakReferences = $weakReferences;
|
||||
}
|
||||
|
||||
public function beforeTraverse(array $nodes) {
|
||||
$this->stack = [];
|
||||
$this->previous = null;
|
||||
@ -32,13 +40,27 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract {
|
||||
|
||||
public function enterNode(Node $node) {
|
||||
if (!empty($this->stack)) {
|
||||
$node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
|
||||
$parent = $this->stack[count($this->stack) - 1];
|
||||
if ($this->weakReferences) {
|
||||
$node->setAttribute('weak_parent', \WeakReference::create($parent));
|
||||
} else {
|
||||
$node->setAttribute('parent', $parent);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) {
|
||||
if ($this->previous !== null) {
|
||||
if (
|
||||
$this->weakReferences
|
||||
) {
|
||||
if ($this->previous->getAttribute('weak_parent') === $node->getAttribute('weak_parent')) {
|
||||
$node->setAttribute('weak_previous', \WeakReference::create($this->previous));
|
||||
$this->previous->setAttribute('weak_next', \WeakReference::create($node));
|
||||
}
|
||||
} elseif ($this->previous->getAttribute('parent') === $node->getAttribute('parent')) {
|
||||
$node->setAttribute('previous', $this->previous);
|
||||
$this->previous->setAttribute('next', $node);
|
||||
}
|
||||
}
|
||||
|
||||
$this->stack[] = $node;
|
||||
}
|
||||
|
@ -11,8 +11,10 @@ use function count;
|
||||
/**
|
||||
* Visitor that connects a child node to its parent node.
|
||||
*
|
||||
* On the child node, the parent node can be accessed through
|
||||
* With <code>$weakReferences=false</code> on the child node, the parent node can be accessed through
|
||||
* <code>$node->getAttribute('parent')</code>.
|
||||
*
|
||||
* With <code>$weakReferences=true</code> the attribute name is "weak_parent" instead.
|
||||
*/
|
||||
final class ParentConnectingVisitor extends NodeVisitorAbstract {
|
||||
/**
|
||||
@ -20,13 +22,24 @@ final class ParentConnectingVisitor extends NodeVisitorAbstract {
|
||||
*/
|
||||
private array $stack = [];
|
||||
|
||||
private bool $weakReferences;
|
||||
|
||||
public function __construct(bool $weakReferences = false) {
|
||||
$this->weakReferences = $weakReferences;
|
||||
}
|
||||
|
||||
public function beforeTraverse(array $nodes) {
|
||||
$this->stack = [];
|
||||
}
|
||||
|
||||
public function enterNode(Node $node) {
|
||||
if (!empty($this->stack)) {
|
||||
$node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
|
||||
$parent = $this->stack[count($this->stack) - 1];
|
||||
if ($this->weakReferences) {
|
||||
$node->setAttribute('weak_parent', \WeakReference::create($parent));
|
||||
} else {
|
||||
$node->setAttribute('parent', $parent);
|
||||
}
|
||||
}
|
||||
|
||||
$this->stack[] = $node;
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ namespace PhpParser;
|
||||
* turn is based on work by Masato Bito.
|
||||
*/
|
||||
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\Cast\Double;
|
||||
@ -14,6 +15,7 @@ use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\InterpolatedStringPart;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\PropertyHook;
|
||||
use PhpParser\Node\Scalar\InterpolatedString;
|
||||
use PhpParser\Node\Scalar\Int_;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
@ -21,6 +23,7 @@ use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Const_;
|
||||
use PhpParser\Node\Stmt\Else_;
|
||||
use PhpParser\Node\Stmt\ElseIf_;
|
||||
use PhpParser\Node\Stmt\Enum_;
|
||||
@ -30,6 +33,7 @@ use PhpParser\Node\Stmt\Nop;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\TryCatch;
|
||||
use PhpParser\Node\UseItem;
|
||||
use PhpParser\Node\VarLikeIdentifier;
|
||||
use PhpParser\NodeVisitor\CommentAnnotatingVisitor;
|
||||
|
||||
abstract class ParserAbstract implements Parser {
|
||||
@ -318,7 +322,7 @@ abstract class ParserAbstract implements Parser {
|
||||
try {
|
||||
$callback = $this->reduceCallbacks[$rule];
|
||||
if ($callback !== null) {
|
||||
$callback($stackPos);
|
||||
$callback($this, $stackPos);
|
||||
} elseif ($ruleLength > 0) {
|
||||
$this->semValue = $this->semStack[$stackPos - $ruleLength + 1];
|
||||
}
|
||||
@ -409,8 +413,6 @@ abstract class ParserAbstract implements Parser {
|
||||
$rule = $state - $this->numNonLeafStates;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Reached end of parser loop');
|
||||
}
|
||||
|
||||
protected function emitError(Error $error): void {
|
||||
@ -1137,32 +1139,12 @@ abstract class ParserAbstract implements Parser {
|
||||
}
|
||||
|
||||
protected function checkClassConst(ClassConst $node, int $modifierPos): void {
|
||||
if ($node->flags & Modifiers::STATIC) {
|
||||
foreach ([Modifiers::STATIC, Modifiers::ABSTRACT, Modifiers::READONLY] as $modifier) {
|
||||
if ($node->flags & $modifier) {
|
||||
$this->emitError(new Error(
|
||||
"Cannot use 'static' as constant modifier",
|
||||
"Cannot use '" . Modifiers::toString($modifier) . "' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
if ($node->flags & Modifiers::ABSTRACT) {
|
||||
$this->emitError(new Error(
|
||||
"Cannot use 'abstract' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
if ($node->flags & Modifiers::READONLY) {
|
||||
$this->emitError(new Error(
|
||||
"Cannot use 'readonly' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkProperty(Property $node, int $modifierPos): void {
|
||||
if ($node->flags & Modifiers::ABSTRACT) {
|
||||
$this->emitError(new Error('Properties cannot be declared abstract',
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
|
||||
if ($node->flags & Modifiers::FINAL) {
|
||||
$this->emitError(new Error('Properties cannot be declared final',
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1178,6 +1160,96 @@ abstract class ParserAbstract implements Parser {
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkPropertyHooksForMultiProperty(Property $property, int $hookPos): void {
|
||||
if (count($property->props) > 1) {
|
||||
$this->emitError(new Error(
|
||||
'Cannot use hooks when declaring multiple properties', $this->getAttributesAt($hookPos)));
|
||||
}
|
||||
}
|
||||
|
||||
/** @param PropertyHook[] $hooks */
|
||||
protected function checkEmptyPropertyHookList(array $hooks, int $hookPos): void {
|
||||
if (empty($hooks)) {
|
||||
$this->emitError(new Error(
|
||||
'Property hook list cannot be empty', $this->getAttributesAt($hookPos)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkPropertyHook(PropertyHook $hook, ?int $paramListPos): void {
|
||||
$name = $hook->name->toLowerString();
|
||||
if ($name !== 'get' && $name !== 'set') {
|
||||
$this->emitError(new Error(
|
||||
'Unknown hook "' . $hook->name . '", expected "get" or "set"',
|
||||
$hook->name->getAttributes()));
|
||||
}
|
||||
if ($name === 'get' && $paramListPos !== null) {
|
||||
$this->emitError(new Error(
|
||||
'get hook must not have a parameter list', $this->getAttributesAt($paramListPos)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkPropertyHookModifiers(int $a, int $b, int $modifierPos): void {
|
||||
try {
|
||||
Modifiers::verifyModifier($a, $b);
|
||||
} catch (Error $error) {
|
||||
$error->setAttributes($this->getAttributesAt($modifierPos));
|
||||
$this->emitError($error);
|
||||
}
|
||||
|
||||
if ($b != Modifiers::FINAL) {
|
||||
$this->emitError(new Error(
|
||||
'Cannot use the ' . Modifiers::toString($b) . ' modifier on a property hook',
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkConstantAttributes(Const_ $node): void {
|
||||
if ($node->attrGroups !== [] && count($node->consts) > 1) {
|
||||
$this->emitError(new Error(
|
||||
'Cannot use attributes on multiple constants at once', $node->getAttributes()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property|Param $node
|
||||
*/
|
||||
protected function addPropertyNameToHooks(Node $node): void {
|
||||
if ($node instanceof Property) {
|
||||
$name = $node->props[0]->name->toString();
|
||||
} else {
|
||||
$name = $node->var->name;
|
||||
}
|
||||
foreach ($node->hooks as $hook) {
|
||||
$hook->setAttribute('propertyName', $name);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<Node\Arg|Node\VariadicPlaceholder> $args */
|
||||
private function isSimpleExit(array $args): bool {
|
||||
if (\count($args) === 0) {
|
||||
return true;
|
||||
}
|
||||
if (\count($args) === 1) {
|
||||
$arg = $args[0];
|
||||
return $arg instanceof Arg && $arg->name === null &&
|
||||
$arg->byRef === false && $arg->unpack === false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Node\Arg|Node\VariadicPlaceholder> $args
|
||||
* @param array<string, mixed> $attrs
|
||||
*/
|
||||
protected function createExitExpr(string $name, int $namePos, array $args, array $attrs): Expr {
|
||||
if ($this->isSimpleExit($args)) {
|
||||
// Create Exit node for backwards compatibility.
|
||||
$attrs['kind'] = strtolower($name) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
|
||||
return new Expr\Exit_(\count($args) === 1 ? $args[0]->value : null, $attrs);
|
||||
}
|
||||
return new Expr\FuncCall(new Name($name, $this->getAttributesAt($namePos)), $args, $attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the token map.
|
||||
*
|
||||
@ -1190,42 +1262,23 @@ abstract class ParserAbstract implements Parser {
|
||||
protected function createTokenMap(): array {
|
||||
$tokenMap = [];
|
||||
|
||||
for ($i = 0; $i < 1000; ++$i) {
|
||||
if ($i < 256) {
|
||||
// Single-char tokens use an identity mapping.
|
||||
for ($i = 0; $i < 256; ++$i) {
|
||||
$tokenMap[$i] = $i;
|
||||
} elseif (\T_DOUBLE_COLON === $i) {
|
||||
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
|
||||
$tokenMap[$i] = static::T_PAAMAYIM_NEKUDOTAYIM;
|
||||
} elseif (\T_OPEN_TAG_WITH_ECHO === $i) {
|
||||
}
|
||||
|
||||
foreach ($this->symbolToName as $name) {
|
||||
if ($name[0] === 'T') {
|
||||
$tokenMap[\constant($name)] = constant(static::class . '::' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
|
||||
$tokenMap[$i] = static::T_ECHO;
|
||||
} elseif (\T_CLOSE_TAG === $i) {
|
||||
$tokenMap[\T_OPEN_TAG_WITH_ECHO] = static::T_ECHO;
|
||||
// T_CLOSE_TAG is equivalent to ';'
|
||||
$tokenMap[$i] = ord(';');
|
||||
} elseif ('UNKNOWN' !== $name = token_name($i)) {
|
||||
if (defined($name = static::class . '::' . $name)) {
|
||||
// Other tokens can be mapped directly
|
||||
$tokenMap[$i] = constant($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
$tokenMap[\T_CLOSE_TAG] = ord(';');
|
||||
|
||||
// Assign tokens for which we define compatibility constants, as token_name() does not know them.
|
||||
$tokenMap[\T_FN] = static::T_FN;
|
||||
$tokenMap[\T_COALESCE_EQUAL] = static::T_COALESCE_EQUAL;
|
||||
$tokenMap[\T_NAME_QUALIFIED] = static::T_NAME_QUALIFIED;
|
||||
$tokenMap[\T_NAME_FULLY_QUALIFIED] = static::T_NAME_FULLY_QUALIFIED;
|
||||
$tokenMap[\T_NAME_RELATIVE] = static::T_NAME_RELATIVE;
|
||||
$tokenMap[\T_MATCH] = static::T_MATCH;
|
||||
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = static::T_NULLSAFE_OBJECT_OPERATOR;
|
||||
$tokenMap[\T_ATTRIBUTE] = static::T_ATTRIBUTE;
|
||||
$tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
|
||||
$tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
|
||||
$tokenMap[\T_ENUM] = static::T_ENUM;
|
||||
$tokenMap[\T_READONLY] = static::T_READONLY;
|
||||
|
||||
// We have create a map from PHP token IDs to external symbol IDs.
|
||||
// We have created a map from PHP token IDs to external symbol IDs.
|
||||
// Now map them to the internal symbol ID.
|
||||
$fullTokenMap = [];
|
||||
foreach ($tokenMap as $phpToken => $extSymbol) {
|
||||
|
@ -43,7 +43,7 @@ class PhpVersion {
|
||||
* if it is still under development.
|
||||
*/
|
||||
public static function getNewestSupported(): self {
|
||||
return self::fromComponents(8, 2);
|
||||
return self::fromComponents(8, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,4 +161,11 @@ class PhpVersion {
|
||||
public function supportsUnicodeEscapes(): bool {
|
||||
return $this->id >= 70000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether this version supports attributes.
|
||||
*/
|
||||
public function supportsAttributes(): bool {
|
||||
return $this->id >= 80000;
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,14 @@ class Standard extends PrettyPrinterAbstract {
|
||||
// Special nodes
|
||||
|
||||
protected function pParam(Node\Param $node): string {
|
||||
return $this->pAttrGroups($node->attrGroups, true)
|
||||
return $this->pAttrGroups($node->attrGroups, $this->phpVersion->supportsAttributes())
|
||||
. $this->pModifiers($node->flags)
|
||||
. ($node->type ? $this->p($node->type) . ' ' : '')
|
||||
. ($node->byRef ? '&' : '')
|
||||
. ($node->variadic ? '...' : '')
|
||||
. $this->p($node->var)
|
||||
. ($node->default ? ' = ' . $this->p($node->default) : '');
|
||||
. ($node->default ? ' = ' . $this->p($node->default) : '')
|
||||
. ($node->hooks ? ' {' . $this->pStmts($node->hooks) . $this->nl . '}' : '');
|
||||
}
|
||||
|
||||
protected function pArg(Node\Arg $node): string {
|
||||
@ -125,6 +126,10 @@ class Standard extends PrettyPrinterAbstract {
|
||||
return '__TRAIT__';
|
||||
}
|
||||
|
||||
protected function pScalar_MagicConst_Property(MagicConst\Property $node): string {
|
||||
return '__PROPERTY__';
|
||||
}
|
||||
|
||||
// Scalars
|
||||
|
||||
private function indentString(string $str): string {
|
||||
@ -651,7 +656,7 @@ class Standard extends PrettyPrinterAbstract {
|
||||
return $this->pAttrGroups($node->attrGroups, true)
|
||||
. $this->pStatic($node->static)
|
||||
. 'function ' . ($node->byRef ? '&' : '')
|
||||
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
|
||||
. '(' . $this->pParams($node->params) . ')'
|
||||
. (!empty($node->uses) ? ' use (' . $this->pCommaSeparated($node->uses) . ')' : '')
|
||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||
@ -683,7 +688,7 @@ class Standard extends PrettyPrinterAbstract {
|
||||
$this->pAttrGroups($node->attrGroups, true)
|
||||
. $this->pStatic($node->static)
|
||||
. 'fn' . ($node->byRef ? '&' : '')
|
||||
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
|
||||
. '(' . $this->pParams($node->params) . ')'
|
||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||
. ' => ',
|
||||
$node->expr, $precedence, $lhsPrecedence);
|
||||
@ -827,7 +832,8 @@ class Standard extends PrettyPrinterAbstract {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags))
|
||||
. ($node->type ? $this->p($node->type) . ' ' : '')
|
||||
. $this->pCommaSeparated($node->props) . ';';
|
||||
. $this->pCommaSeparated($node->props)
|
||||
. ($node->hooks ? ' {' . $this->pStmts($node->hooks) . $this->nl . '}' : ';');
|
||||
}
|
||||
|
||||
protected function pPropertyItem(Node\PropertyItem $node): string {
|
||||
@ -835,11 +841,20 @@ class Standard extends PrettyPrinterAbstract {
|
||||
. (null !== $node->default ? ' = ' . $this->p($node->default) : '');
|
||||
}
|
||||
|
||||
protected function pPropertyHook(Node\PropertyHook $node): string {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. $this->pModifiers($node->flags)
|
||||
. ($node->byRef ? '&' : '') . $node->name
|
||||
. ($node->params ? '(' . $this->pParams($node->params) . ')' : '')
|
||||
. (\is_array($node->body) ? ' {' . $this->pStmts($node->body) . $this->nl . '}'
|
||||
: ($node->body !== null ? ' => ' . $this->p($node->body) : '') . ';');
|
||||
}
|
||||
|
||||
protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. $this->pModifiers($node->flags)
|
||||
. 'function ' . ($node->byRef ? '&' : '') . $node->name
|
||||
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
|
||||
. '(' . $this->pParams($node->params) . ')'
|
||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||
. (null !== $node->stmts
|
||||
? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'
|
||||
@ -857,13 +872,15 @@ class Standard extends PrettyPrinterAbstract {
|
||||
protected function pStmt_Function(Stmt\Function_ $node): string {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. 'function ' . ($node->byRef ? '&' : '') . $node->name
|
||||
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
|
||||
. '(' . $this->pParams($node->params) . ')'
|
||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||
}
|
||||
|
||||
protected function pStmt_Const(Stmt\Const_ $node): string {
|
||||
return 'const ' . $this->pCommaSeparated($node->consts) . ';';
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. 'const '
|
||||
. $this->pCommaSeparated($node->consts) . ';';
|
||||
}
|
||||
|
||||
protected function pStmt_Declare(Stmt\Declare_ $node): string {
|
||||
@ -1164,6 +1181,27 @@ class Standard extends PrettyPrinterAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
/** @param Node\Param[] $params
|
||||
*/
|
||||
private function hasParamWithAttributes(array $params): bool {
|
||||
foreach ($params as $param) {
|
||||
if ($param->attrGroups) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @param Node\Param[] $params */
|
||||
protected function pParams(array $params): string {
|
||||
if ($this->hasNodeWithComments($params) ||
|
||||
($this->hasParamWithAttributes($params) && !$this->phpVersion->supportsAttributes())
|
||||
) {
|
||||
return $this->pCommaSeparatedMultiline($params, $this->phpVersion->supportsTrailingCommaInParamList()) . $this->nl;
|
||||
}
|
||||
return $this->pCommaSeparated($params);
|
||||
}
|
||||
|
||||
/** @param Node\AttributeGroup[] $nodes */
|
||||
protected function pAttrGroups(array $nodes, bool $inline = false): string {
|
||||
$result = '';
|
||||
|
@ -14,6 +14,7 @@ use PhpParser\Node\Expr\Cast;
|
||||
use PhpParser\Node\IntersectionType;
|
||||
use PhpParser\Node\MatchArm;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\PropertyHook;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\UnionType;
|
||||
@ -76,7 +77,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
BinaryOp\BooleanAnd::class => [120, 121, 120],
|
||||
BinaryOp\BooleanOr::class => [130, 131, 130],
|
||||
BinaryOp\Coalesce::class => [140, 140, 141],
|
||||
Expr\Ternary::class => [150, -1, -1],
|
||||
Expr\Ternary::class => [150, 150, 150],
|
||||
Expr\Assign::class => [160, -1, -1],
|
||||
Expr\AssignRef::class => [160, -1, -1],
|
||||
AssignOp\Plus::class => [160, -1, -1],
|
||||
@ -105,6 +106,15 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
|
||||
/** @var int Current indentation level. */
|
||||
protected int $indentLevel;
|
||||
/** @var string String for single level of indentation */
|
||||
private string $indent;
|
||||
/** @var int Width in spaces to indent by. */
|
||||
private int $indentWidth;
|
||||
/** @var bool Whether to use tab indentation. */
|
||||
private bool $useTabs;
|
||||
/** @var int Width in spaces of one tab. */
|
||||
private int $tabWidth = 4;
|
||||
|
||||
/** @var string Newline style. Does not include current indentation. */
|
||||
protected string $newline;
|
||||
/** @var string Newline including current indentation. */
|
||||
@ -169,12 +179,14 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
* PHP version while specifying an older target (but the result will
|
||||
* of course not be compatible with the older version in that case).
|
||||
* * string $newline: The newline style to use. Should be "\n" (default) or "\r\n".
|
||||
* * string $indent: The indentation to use. Should either be all spaces or a single
|
||||
* tab. Defaults to four spaces (" ").
|
||||
* * bool $shortArraySyntax: Whether to use [] instead of array() as the default array
|
||||
* syntax, if the node does not specify a format. Defaults to whether
|
||||
* the phpVersion support short array syntax.
|
||||
*
|
||||
* @param array{
|
||||
* phpVersion?: PhpVersion, newline?: string, shortArraySyntax?: bool
|
||||
* phpVersion?: PhpVersion, newline?: string, indent?: string, shortArraySyntax?: bool
|
||||
* } $options Dictionary of formatting options
|
||||
*/
|
||||
public function __construct(array $options = []) {
|
||||
@ -189,6 +201,17 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
$options['shortArraySyntax'] ?? $this->phpVersion->supportsShortArraySyntax();
|
||||
$this->docStringEndToken =
|
||||
$this->phpVersion->supportsFlexibleHeredoc() ? null : '_DOC_STRING_END_' . mt_rand();
|
||||
|
||||
$this->indent = $indent = $options['indent'] ?? ' ';
|
||||
if ($indent === "\t") {
|
||||
$this->useTabs = true;
|
||||
$this->indentWidth = $this->tabWidth;
|
||||
} elseif ($indent === \str_repeat(' ', \strlen($indent))) {
|
||||
$this->useTabs = false;
|
||||
$this->indentWidth = \strlen($indent);
|
||||
} else {
|
||||
throw new \LogicException('Option "indent" must either be all spaces or a single tab');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,24 +230,29 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
*/
|
||||
protected function setIndentLevel(int $level): void {
|
||||
$this->indentLevel = $level;
|
||||
if ($this->useTabs) {
|
||||
$tabs = \intdiv($level, $this->tabWidth);
|
||||
$spaces = $level % $this->tabWidth;
|
||||
$this->nl = $this->newline . \str_repeat("\t", $tabs) . \str_repeat(' ', $spaces);
|
||||
} else {
|
||||
$this->nl = $this->newline . \str_repeat(' ', $level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase indentation level.
|
||||
*/
|
||||
protected function indent(): void {
|
||||
$this->indentLevel += 4;
|
||||
$this->nl .= ' ';
|
||||
$this->indentLevel += $this->indentWidth;
|
||||
$this->nl .= $this->indent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease indentation level.
|
||||
*/
|
||||
protected function outdent(): void {
|
||||
assert($this->indentLevel >= 4);
|
||||
$this->indentLevel -= 4;
|
||||
$this->nl = $this->newline . str_repeat(' ', $this->indentLevel);
|
||||
assert($this->indentLevel >= $this->indentWidth);
|
||||
$this->setIndentLevel($this->indentLevel - $this->indentWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -536,7 +564,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
$this->initializeModifierChangeMap();
|
||||
|
||||
$this->resetState();
|
||||
$this->origTokens = new TokenStream($origTokens);
|
||||
$this->origTokens = new TokenStream($origTokens, $this->tabWidth);
|
||||
|
||||
$this->preprocessNodes($stmts);
|
||||
|
||||
@ -708,7 +736,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
$result .= $extraLeft;
|
||||
|
||||
$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
|
||||
// need fixup. It's important to check this here, because our fixup checks are more
|
||||
@ -811,7 +839,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
\assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
|
||||
|
||||
$origIndentLevel = $this->indentLevel;
|
||||
$lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
|
||||
$lastElemIndentLevel = max($this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment, 0);
|
||||
$this->setIndentLevel($lastElemIndentLevel);
|
||||
|
||||
$comments = $arrItem->getComments();
|
||||
@ -1195,6 +1223,9 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
. ($modifiers & Modifiers::PUBLIC ? 'public ' : '')
|
||||
. ($modifiers & Modifiers::PROTECTED ? 'protected ' : '')
|
||||
. ($modifiers & Modifiers::PRIVATE ? 'private ' : '')
|
||||
. ($modifiers & Modifiers::PUBLIC_SET ? 'public(set) ' : '')
|
||||
. ($modifiers & Modifiers::PROTECTED_SET ? 'protected(set) ' : '')
|
||||
. ($modifiers & Modifiers::PRIVATE_SET ? 'private(set) ' : '')
|
||||
. ($modifiers & Modifiers::STATIC ? 'static ' : '')
|
||||
. ($modifiers & Modifiers::READONLY ? 'readonly ' : '');
|
||||
}
|
||||
@ -1517,6 +1548,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
Stmt\UseUse::class . '->uses' => ', ',
|
||||
MatchArm::class . '->conds' => ', ',
|
||||
AttributeGroup::class . '->attrs' => ', ',
|
||||
PropertyHook::class . '->params' => ', ',
|
||||
|
||||
// statement lists
|
||||
Expr\Closure::class . '->stmts' => "\n",
|
||||
@ -1554,10 +1586,15 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
Expr\Closure::class . '->attrGroups' => ' ',
|
||||
Expr\ArrowFunction::class . '->attrGroups' => ' ',
|
||||
Param::class . '->attrGroups' => ' ',
|
||||
PropertyHook::class . '->attrGroups' => ' ',
|
||||
|
||||
Stmt\Switch_::class . '->cases' => "\n",
|
||||
Stmt\TraitUse::class . '->adaptations' => "\n",
|
||||
Stmt\TryCatch::class . '->stmts' => "\n",
|
||||
Stmt\While_::class . '->stmts' => "\n",
|
||||
PropertyHook::class . '->body' => "\n",
|
||||
Stmt\Property::class . '->hooks' => "\n",
|
||||
Param::class . '->hooks' => "\n",
|
||||
|
||||
// dummy for top-level context
|
||||
'File->stmts' => "\n",
|
||||
@ -1597,6 +1634,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"],
|
||||
Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '],
|
||||
Expr\Closure::class . '->attrGroups' => [null, '', ' '],
|
||||
Stmt\Const_::class . '->attrGroups' => [null, '', "\n"],
|
||||
PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', ''],
|
||||
|
||||
/* These cannot be empty to start with:
|
||||
@ -1641,6 +1679,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
Stmt\Property::class . '->flags' => ['pModifiers', \T_VARIABLE],
|
||||
PrintableNewAnonClassNode::class . '->flags' => ['pModifiers', \T_CLASS],
|
||||
Param::class . '->flags' => ['pModifiers', \T_VARIABLE],
|
||||
PropertyHook::class . '->flags' => ['pModifiers', \T_STRING],
|
||||
Expr\Closure::class . '->static' => ['pStatic', \T_FUNCTION],
|
||||
Expr\ArrowFunction::class . '->static' => ['pStatic', \T_FN],
|
||||
//Stmt\TraitUseAdaptation\Alias::class . '->newModifier' => 0, // TODO
|
||||
|
@ -17,6 +17,11 @@ if (!\function_exists('PhpParser\defineCompatibilityTokens')) {
|
||||
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
|
||||
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
|
||||
'T_READONLY',
|
||||
// PHP 8.4
|
||||
'T_PROPERTY_C',
|
||||
'T_PUBLIC_SET',
|
||||
'T_PROTECTED_SET',
|
||||
'T_PRIVATE_SET',
|
||||
];
|
||||
|
||||
// PHP-Parser might be used together with another library that also emulates some or all
|
||||
@ -26,6 +31,13 @@ if (!\function_exists('PhpParser\defineCompatibilityTokens')) {
|
||||
foreach ($compatTokens as $token) {
|
||||
if (\defined($token)) {
|
||||
$tokenId = \constant($token);
|
||||
if (!\is_int($tokenId)) {
|
||||
throw new \Error(sprintf(
|
||||
'Token %s has ID of type %s, should be int. ' .
|
||||
'You may be using a library with broken token emulation',
|
||||
$token, \gettype($tokenId)
|
||||
));
|
||||
}
|
||||
$clashingToken = $usedTokenIds[$tokenId] ?? null;
|
||||
if ($clashingToken !== null) {
|
||||
throw new \Error(sprintf(
|
||||
|
@ -1,236 +1,259 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Method PhpParser\\\\Builder\\\\ClassConst\\:\\:__construct\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\Builder\\ClassConst\:\:__construct\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/Builder/ClassConst.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\Builder\\\\ClassConst\\:\\:addConst\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\Builder\\ClassConst\:\:addConst\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/Builder/ClassConst.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:args\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderFactory\:\:args\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:attribute\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderFactory\:\:attribute\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:classConst\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderFactory\:\:classConst\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:funcCall\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderFactory\:\:funcCall\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:methodCall\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderFactory\:\:methodCall\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:new\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderFactory\:\:new\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:staticCall\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderFactory\:\:staticCall\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:val\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderFactory\:\:val\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\BuilderHelpers\\:\\:normalizeValue\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\BuilderHelpers\:\:normalizeValue\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/BuilderHelpers.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\ConstExprEvaluator\\:\\:evaluateArray\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\ConstExprEvaluator\:\:evaluateArray\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/ConstExprEvaluator.php
|
||||
|
||||
-
|
||||
message: "#^Unary operation \"~\" on mixed results in an error\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ConstExprEvaluator.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeArray\\(\\) has parameter \\$array with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\JsonDecoder\:\:decodeArray\(\) has parameter \$array with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/JsonDecoder.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeArray\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\JsonDecoder\:\:decodeArray\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/JsonDecoder.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeComment\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\JsonDecoder\:\:decodeComment\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/JsonDecoder.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeNode\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\JsonDecoder\:\:decodeNode\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/JsonDecoder.php
|
||||
|
||||
-
|
||||
message: "#^Call to function assert\\(\\) with false will always evaluate to false\\.$#"
|
||||
message: '#^Call to function assert\(\) with false will always evaluate to false\.$#'
|
||||
identifier: function.impossibleType
|
||||
count: 1
|
||||
path: lib/PhpParser/Lexer/Emulative.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeDumper\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Expr/ArrayItem.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Expr/ClosureUse.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Scalar/DNumber.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Scalar/Encapsed.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Scalar/EncapsedStringPart.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Scalar/LNumber.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Stmt/DeclareDeclare.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Stmt/PropertyProperty.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Stmt/StaticVar.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: lib/PhpParser/Node/Stmt/UseUse.php
|
||||
|
||||
-
|
||||
message: '#^Method PhpParser\\NodeDumper\:\:__construct\(\) has parameter \$options with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeDumper.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeDumper\\:\\:dump\\(\\) has parameter \\$node with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\NodeDumper\:\:dump\(\) has parameter \$node with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeDumper.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeTraverser\\:\\:traverseArray\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\NodeTraverser\:\:traverseArray\(\) has parameter \$nodes with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeTraverser.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeTraverser\\:\\:traverseArray\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
message: '#^Method PhpParser\\NodeTraverser\:\:traverseArray\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeTraverser.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$attrGroups\\.$#"
|
||||
message: '#^Access to an undefined property PhpParser\\Node\:\:\$attrGroups\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$name\\.$#"
|
||||
message: '#^Access to an undefined property PhpParser\\Node\:\:\$name\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$namespacedName\\.$#"
|
||||
message: '#^Access to an undefined property PhpParser\\Node\:\:\$namespacedName\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\NodeConnectingVisitor\\:\\:beforeTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#"
|
||||
message: '#^Method PhpParser\\NodeVisitor\\NodeConnectingVisitor\:\:beforeTraverse\(\) should return array\<PhpParser\\Node\>\|null but return statement is missing\.$#'
|
||||
identifier: return.missing
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\NodeConnectingVisitor\\:\\:enterNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
|
||||
message: '#^Method PhpParser\\NodeVisitor\\NodeConnectingVisitor\:\:enterNode\(\) should return array\<PhpParser\\Node\>\|int\|PhpParser\\Node\|null but return statement is missing\.$#'
|
||||
identifier: return.missing
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\NodeConnectingVisitor\\:\\:leaveNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
|
||||
message: '#^Method PhpParser\\NodeVisitor\\NodeConnectingVisitor\:\:leaveNode\(\) should return array\<PhpParser\\Node\>\|int\|PhpParser\\Node\|null but return statement is missing\.$#'
|
||||
identifier: return.missing
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\ParentConnectingVisitor\\:\\:beforeTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#"
|
||||
message: '#^Method PhpParser\\NodeVisitor\\ParentConnectingVisitor\:\:beforeTraverse\(\) should return array\<PhpParser\\Node\>\|null but return statement is missing\.$#'
|
||||
identifier: return.missing
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\ParentConnectingVisitor\\:\\:enterNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
|
||||
message: '#^Method PhpParser\\NodeVisitor\\ParentConnectingVisitor\:\:enterNode\(\) should return array\<PhpParser\\Node\>\|int\|PhpParser\\Node\|null but return statement is missing\.$#'
|
||||
identifier: return.missing
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\ParentConnectingVisitor\\:\\:leaveNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
|
||||
message: '#^Method PhpParser\\NodeVisitor\\ParentConnectingVisitor\:\:leaveNode\(\) should return array\<PhpParser\\Node\>\|int\|PhpParser\\Node\|null but return statement is missing\.$#'
|
||||
identifier: return.missing
|
||||
count: 1
|
||||
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG\\.$#"
|
||||
message: '#^Access to undefined constant static\(PhpParser\\ParserAbstract\)\:\:T_ECHO\.$#'
|
||||
identifier: classConstant.notFound
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG\\.$#"
|
||||
message: '#^Unary operation "\+" on string results in an error\.$#'
|
||||
identifier: unaryOp.invalid
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_ATTRIBUTE\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_COALESCE_EQUAL\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_ECHO\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_ENUM\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_FN\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_MATCH\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_NAME_FULLY_QUALIFIED\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_NAME_QUALIFIED\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_NAME_RELATIVE\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_NULLSAFE_OBJECT_OPERATOR\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_PAAMAYIM_NEKUDOTAYIM\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_READONLY\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Unary operation \"\\+\" on string results in an error\\.$#"
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
||||
-
|
||||
message: "#^Variable \\$action might not be defined\\.$#"
|
||||
message: '#^Variable \$action might not be defined\.$#'
|
||||
identifier: variable.undefined
|
||||
count: 1
|
||||
path: lib/PhpParser/ParserAbstract.php
|
||||
|
@ -4,6 +4,7 @@
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
bootstrap="./test/bootstrap.php">
|
||||
<testsuites>
|
||||
@ -12,9 +13,9 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">./lib/PhpParser/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
|
@ -20,7 +20,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase {
|
||||
return new ClassConst($name, $value);
|
||||
}
|
||||
|
||||
public function testModifiers() {
|
||||
public function testModifiers(): void {
|
||||
$node = $this->createClassConstBuilder("TEST", 1)
|
||||
->makePrivate()
|
||||
->getNode()
|
||||
@ -82,7 +82,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
public function testDocComment(): void {
|
||||
$node = $this->createClassConstBuilder('TEST', 1)
|
||||
->setDocComment('/** Test */')
|
||||
->makePublic()
|
||||
@ -102,7 +102,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddConst() {
|
||||
public function testAddConst(): void {
|
||||
$node = $this->createClassConstBuilder('FIRST_TEST', 1)
|
||||
->addConst("SECOND_TEST", 2)
|
||||
->getNode();
|
||||
@ -118,7 +118,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
@ -142,7 +142,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testType() {
|
||||
public function testType(): void {
|
||||
$node = $this->createClassConstBuilder('TYPE', 1)
|
||||
->setType('int')
|
||||
->getNode();
|
||||
@ -157,7 +157,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestDefaultValues
|
||||
*/
|
||||
public function testValues($value, $expectedValueNode) {
|
||||
public function testValues($value, $expectedValueNode): void {
|
||||
$node = $this->createClassConstBuilder('TEST', $value)
|
||||
->getNode()
|
||||
;
|
||||
@ -165,7 +165,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals($expectedValueNode, $node->consts[0]->value);
|
||||
}
|
||||
|
||||
public function provideTestDefaultValues() {
|
||||
public static function provideTestDefaultValues() {
|
||||
return [
|
||||
[
|
||||
null,
|
||||
|
@ -18,7 +18,7 @@ class ClassTest extends \PHPUnit\Framework\TestCase {
|
||||
return new Class_($class);
|
||||
}
|
||||
|
||||
public function testExtendsImplements() {
|
||||
public function testExtendsImplements(): void {
|
||||
$node = $this->createClassBuilder('SomeLogger')
|
||||
->extend('BaseLogger')
|
||||
->implement('Namespaced\Logger', new Name('SomeInterface'))
|
||||
@ -40,7 +40,7 @@ class ClassTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testAbstract() {
|
||||
public function testAbstract(): void {
|
||||
$node = $this->createClassBuilder('Test')
|
||||
->makeAbstract()
|
||||
->getNode()
|
||||
@ -54,7 +54,7 @@ class ClassTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testFinal() {
|
||||
public function testFinal(): void {
|
||||
$node = $this->createClassBuilder('Test')
|
||||
->makeFinal()
|
||||
->getNode()
|
||||
@ -68,7 +68,7 @@ class ClassTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testReadonly() {
|
||||
public function testReadonly(): void {
|
||||
$node = $this->createClassBuilder('Test')
|
||||
->makeReadonly()
|
||||
->getNode()
|
||||
@ -82,7 +82,7 @@ class ClassTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testStatementOrder() {
|
||||
public function testStatementOrder(): void {
|
||||
$method = new Stmt\ClassMethod('testMethod');
|
||||
$property = new Stmt\Property(
|
||||
Modifiers::PUBLIC,
|
||||
@ -108,7 +108,7 @@ class ClassTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
public function testDocComment(): void {
|
||||
$docComment = <<<'DOC'
|
||||
/**
|
||||
* Test
|
||||
@ -141,7 +141,7 @@ DOC;
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
@ -162,7 +162,7 @@ DOC;
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidStmtError() {
|
||||
public function testInvalidStmtError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Unexpected node of type "Stmt_Echo"');
|
||||
$this->createClassBuilder('Test')
|
||||
@ -170,21 +170,21 @@ DOC;
|
||||
;
|
||||
}
|
||||
|
||||
public function testInvalidDocComment() {
|
||||
public function testInvalidDocComment(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
|
||||
$this->createClassBuilder('Test')
|
||||
->setDocComment(new Comment('Test'));
|
||||
}
|
||||
|
||||
public function testEmptyName() {
|
||||
public function testEmptyName(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Name cannot be empty');
|
||||
$this->createClassBuilder('Test')
|
||||
->extend('');
|
||||
}
|
||||
|
||||
public function testInvalidName() {
|
||||
public function testInvalidName(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Name must be a string or an instance of Node\Name');
|
||||
$this->createClassBuilder('Test')
|
||||
|
@ -17,7 +17,7 @@ class EnumCaseTest extends \PHPUnit\Framework\TestCase {
|
||||
return new EnumCase($name);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
public function testDocComment(): void {
|
||||
$node = $this->createEnumCaseBuilder('TEST')
|
||||
->setDocComment('/** Test */')
|
||||
->getNode();
|
||||
@ -35,7 +35,7 @@ class EnumCaseTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
@ -59,7 +59,7 @@ class EnumCaseTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestDefaultValues
|
||||
*/
|
||||
public function testValues($value, $expectedValueNode) {
|
||||
public function testValues($value, $expectedValueNode): void {
|
||||
$node = $this->createEnumCaseBuilder('TEST')
|
||||
->setValue($value)
|
||||
->getNode()
|
||||
@ -68,7 +68,7 @@ class EnumCaseTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals($expectedValueNode, $node->expr);
|
||||
}
|
||||
|
||||
public function provideTestDefaultValues() {
|
||||
public static function provideTestDefaultValues() {
|
||||
return [
|
||||
[
|
||||
31415,
|
||||
|
@ -17,7 +17,7 @@ class EnumTest extends \PHPUnit\Framework\TestCase {
|
||||
return new Enum_($class);
|
||||
}
|
||||
|
||||
public function testImplements() {
|
||||
public function testImplements(): void {
|
||||
$node = $this->createEnumBuilder('SomeEnum')
|
||||
->implement('Namespaced\SomeInterface', new Name('OtherInterface'))
|
||||
->getNode()
|
||||
@ -34,7 +34,7 @@ class EnumTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testSetScalarType() {
|
||||
public function testSetScalarType(): void {
|
||||
$node = $this->createEnumBuilder('Test')
|
||||
->setScalarType('int')
|
||||
->getNode()
|
||||
@ -48,7 +48,7 @@ class EnumTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testStatementOrder() {
|
||||
public function testStatementOrder(): void {
|
||||
$method = new Stmt\ClassMethod('testMethod');
|
||||
$enumCase = new Stmt\EnumCase(
|
||||
'TEST_ENUM_CASE'
|
||||
@ -73,7 +73,7 @@ class EnumTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
public function testDocComment(): void {
|
||||
$docComment = <<<'DOC'
|
||||
/**
|
||||
* Test
|
||||
@ -106,7 +106,7 @@ DOC;
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
@ -127,7 +127,7 @@ DOC;
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidStmtError() {
|
||||
public function testInvalidStmtError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Unexpected node of type "PropertyItem"');
|
||||
$this->createEnumBuilder('Test')
|
||||
@ -135,21 +135,21 @@ DOC;
|
||||
;
|
||||
}
|
||||
|
||||
public function testInvalidDocComment() {
|
||||
public function testInvalidDocComment(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
|
||||
$this->createEnumBuilder('Test')
|
||||
->setDocComment(new Comment('Test'));
|
||||
}
|
||||
|
||||
public function testEmptyName() {
|
||||
public function testEmptyName(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Name cannot be empty');
|
||||
$this->createEnumBuilder('Test')
|
||||
->implement('');
|
||||
}
|
||||
|
||||
public function testInvalidName() {
|
||||
public function testInvalidName(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Name must be a string or an instance of Node\Name');
|
||||
$this->createEnumBuilder('Test')
|
||||
|
@ -20,7 +20,7 @@ class FunctionTest extends \PHPUnit\Framework\TestCase {
|
||||
return new Function_($name);
|
||||
}
|
||||
|
||||
public function testReturnByRef() {
|
||||
public function testReturnByRef(): void {
|
||||
$node = $this->createFunctionBuilder('test')
|
||||
->makeReturnByRef()
|
||||
->getNode()
|
||||
@ -34,7 +34,7 @@ class FunctionTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testParams() {
|
||||
public function testParams(): void {
|
||||
$param1 = new Node\Param(new Variable('test1'));
|
||||
$param2 = new Node\Param(new Variable('test2'));
|
||||
$param3 = new Node\Param(new Variable('test3'));
|
||||
@ -53,7 +53,7 @@ class FunctionTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testStmts() {
|
||||
public function testStmts(): void {
|
||||
$stmt1 = new Print_(new String_('test1'));
|
||||
$stmt2 = new Print_(new String_('test2'));
|
||||
$stmt3 = new Print_(new String_('test3'));
|
||||
@ -76,7 +76,7 @@ class FunctionTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
public function testDocComment(): void {
|
||||
$node = $this->createFunctionBuilder('test')
|
||||
->setDocComment('/** Test */')
|
||||
->getNode();
|
||||
@ -86,7 +86,7 @@ class FunctionTest extends \PHPUnit\Framework\TestCase {
|
||||
]), $node);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
@ -102,7 +102,7 @@ class FunctionTest extends \PHPUnit\Framework\TestCase {
|
||||
], []), $node);
|
||||
}
|
||||
|
||||
public function testReturnType() {
|
||||
public function testReturnType(): void {
|
||||
$node = $this->createFunctionBuilder('test')
|
||||
->setReturnType('void')
|
||||
->getNode();
|
||||
@ -112,13 +112,13 @@ class FunctionTest extends \PHPUnit\Framework\TestCase {
|
||||
], []), $node);
|
||||
}
|
||||
|
||||
public function testInvalidNullableVoidType() {
|
||||
public function testInvalidNullableVoidType(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('void type cannot be nullable');
|
||||
$this->createFunctionBuilder('test')->setReturnType('?void');
|
||||
}
|
||||
|
||||
public function testInvalidParamError() {
|
||||
public function testInvalidParamError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Expected parameter node, got "Name"');
|
||||
$this->createFunctionBuilder('test')
|
||||
@ -126,7 +126,7 @@ class FunctionTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testAddNonStmt() {
|
||||
public function testAddNonStmt(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Expected statement or expression node');
|
||||
$this->createFunctionBuilder('test')
|
||||
|
@ -23,13 +23,13 @@ class InterfaceTest extends \PHPUnit\Framework\TestCase {
|
||||
return $pp->prettyPrint([$node]);
|
||||
}
|
||||
|
||||
public function testEmpty() {
|
||||
public function testEmpty(): void {
|
||||
$contract = $this->createInterfaceBuilder()->getNode();
|
||||
$this->assertInstanceOf(Stmt\Interface_::class, $contract);
|
||||
$this->assertEquals(new Node\Identifier('Contract'), $contract->name);
|
||||
}
|
||||
|
||||
public function testExtending() {
|
||||
public function testExtending(): void {
|
||||
$contract = $this->createInterfaceBuilder()
|
||||
->extend('Space\Root1', 'Root2')->getNode();
|
||||
$this->assertEquals(
|
||||
@ -42,13 +42,13 @@ class InterfaceTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddMethod() {
|
||||
public function testAddMethod(): void {
|
||||
$method = new Stmt\ClassMethod('doSomething');
|
||||
$contract = $this->createInterfaceBuilder()->addStmt($method)->getNode();
|
||||
$this->assertSame([$method], $contract->stmts);
|
||||
}
|
||||
|
||||
public function testAddConst() {
|
||||
public function testAddConst(): void {
|
||||
$const = new Stmt\ClassConst([
|
||||
new Node\Const_('SPEED_OF_LIGHT', new Float_(299792458.0))
|
||||
]);
|
||||
@ -56,7 +56,7 @@ class InterfaceTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame(299792458.0, $contract->stmts[0]->consts[0]->value->value);
|
||||
}
|
||||
|
||||
public function testOrder() {
|
||||
public function testOrder(): void {
|
||||
$const = new Stmt\ClassConst([
|
||||
new Node\Const_('SPEED_OF_LIGHT', new Float_(299792458))
|
||||
]);
|
||||
@ -71,7 +71,7 @@ class InterfaceTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertInstanceOf(Stmt\ClassMethod::class, $contract->stmts[1]);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
public function testDocComment(): void {
|
||||
$node = $this->createInterfaceBuilder()
|
||||
->setDocComment('/** Test */')
|
||||
->getNode();
|
||||
@ -81,7 +81,7 @@ class InterfaceTest extends \PHPUnit\Framework\TestCase {
|
||||
]), $node);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
@ -97,13 +97,13 @@ class InterfaceTest extends \PHPUnit\Framework\TestCase {
|
||||
], []), $node);
|
||||
}
|
||||
|
||||
public function testInvalidStmtError() {
|
||||
public function testInvalidStmtError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Unexpected node of type "PropertyItem"');
|
||||
$this->createInterfaceBuilder()->addStmt(new Node\PropertyItem('invalid'));
|
||||
}
|
||||
|
||||
public function testFullFunctional() {
|
||||
public function testFullFunctional(): void {
|
||||
$const = new Stmt\ClassConst([
|
||||
new Node\Const_('SPEED_OF_LIGHT', new Float_(299792458))
|
||||
]);
|
||||
|
@ -21,7 +21,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
return new Method($name);
|
||||
}
|
||||
|
||||
public function testModifiers() {
|
||||
public function testModifiers(): void {
|
||||
$node = $this->createMethodBuilder('test')
|
||||
->makePublic()
|
||||
->makeAbstract()
|
||||
@ -63,7 +63,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testReturnByRef() {
|
||||
public function testReturnByRef(): void {
|
||||
$node = $this->createMethodBuilder('test')
|
||||
->makeReturnByRef()
|
||||
->getNode()
|
||||
@ -77,7 +77,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testParams() {
|
||||
public function testParams(): void {
|
||||
$param1 = new Node\Param(new Variable('test1'));
|
||||
$param2 = new Node\Param(new Variable('test2'));
|
||||
$param3 = new Node\Param(new Variable('test3'));
|
||||
@ -96,7 +96,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testStmts() {
|
||||
public function testStmts(): void {
|
||||
$stmt1 = new Print_(new String_('test1'));
|
||||
$stmt2 = new Print_(new String_('test2'));
|
||||
$stmt3 = new Print_(new String_('test3'));
|
||||
@ -118,7 +118,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
$node
|
||||
);
|
||||
}
|
||||
public function testDocComment() {
|
||||
public function testDocComment(): void {
|
||||
$node = $this->createMethodBuilder('test')
|
||||
->setDocComment('/** Test */')
|
||||
->getNode();
|
||||
@ -128,7 +128,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
]), $node);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
@ -144,7 +144,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
], []), $node);
|
||||
}
|
||||
|
||||
public function testReturnType() {
|
||||
public function testReturnType(): void {
|
||||
$node = $this->createMethodBuilder('test')
|
||||
->setReturnType('bool')
|
||||
->getNode();
|
||||
@ -153,7 +153,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
], []), $node);
|
||||
}
|
||||
|
||||
public function testAddStmtToAbstractMethodError() {
|
||||
public function testAddStmtToAbstractMethodError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Cannot add statements to an abstract method');
|
||||
$this->createMethodBuilder('test')
|
||||
@ -162,7 +162,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testMakeMethodWithStmtsAbstractError() {
|
||||
public function testMakeMethodWithStmtsAbstractError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Cannot make method with statements abstract');
|
||||
$this->createMethodBuilder('test')
|
||||
@ -171,7 +171,7 @@ class MethodTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testInvalidParamError() {
|
||||
public function testInvalidParamError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Expected parameter node, got "Name"');
|
||||
$this->createMethodBuilder('test')
|
||||
|
@ -11,7 +11,7 @@ class NamespaceTest extends \PHPUnit\Framework\TestCase {
|
||||
return new Namespace_($fqn);
|
||||
}
|
||||
|
||||
public function testCreation() {
|
||||
public function testCreation(): void {
|
||||
$stmt1 = new Stmt\Class_('SomeClass');
|
||||
$stmt2 = new Stmt\Interface_('SomeInterface');
|
||||
$stmt3 = new Stmt\Function_('someFunction');
|
||||
|
@ -21,7 +21,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestDefaultValues
|
||||
*/
|
||||
public function testDefaultValues($value, $expectedValueNode) {
|
||||
public function testDefaultValues($value, $expectedValueNode): void {
|
||||
$node = $this->createParamBuilder('test')
|
||||
->setDefault($value)
|
||||
->getNode()
|
||||
@ -30,7 +30,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals($expectedValueNode, $node->default);
|
||||
}
|
||||
|
||||
public function provideTestDefaultValues() {
|
||||
public static function provideTestDefaultValues() {
|
||||
return [
|
||||
[
|
||||
null,
|
||||
@ -89,7 +89,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
* @dataProvider provideTestNullableTypes
|
||||
* @dataProvider provideTestUnionTypes
|
||||
*/
|
||||
public function testTypes($typeHint, $expectedType) {
|
||||
public function testTypes($typeHint, $expectedType): void {
|
||||
$node = $this->createParamBuilder('test')
|
||||
->setType($typeHint)
|
||||
->getNode()
|
||||
@ -107,7 +107,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals($expectedType, $type);
|
||||
}
|
||||
|
||||
public function provideTestTypes() {
|
||||
public static function provideTestTypes() {
|
||||
return [
|
||||
['array', new Node\Identifier('array')],
|
||||
['callable', new Node\Identifier('callable')],
|
||||
@ -127,7 +127,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function provideTestNullableTypes() {
|
||||
public static function provideTestNullableTypes() {
|
||||
return [
|
||||
['?array', new Node\NullableType(new Node\Identifier('array'))],
|
||||
['?Some\Class', new Node\NullableType(new Node\Name('Some\Class'))],
|
||||
@ -142,7 +142,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function provideTestUnionTypes() {
|
||||
public static function provideTestUnionTypes() {
|
||||
return [
|
||||
[
|
||||
new Node\UnionType([
|
||||
@ -169,19 +169,19 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function testVoidTypeError() {
|
||||
public function testVoidTypeError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Parameter type cannot be void');
|
||||
$this->createParamBuilder('test')->setType('void');
|
||||
}
|
||||
|
||||
public function testInvalidTypeError() {
|
||||
public function testInvalidTypeError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Type must be a string, or an instance of Name, Identifier or ComplexType');
|
||||
$this->createParamBuilder('test')->setType(new \stdClass());
|
||||
}
|
||||
|
||||
public function testByRef() {
|
||||
public function testByRef(): void {
|
||||
$node = $this->createParamBuilder('test')
|
||||
->makeByRef()
|
||||
->getNode()
|
||||
@ -193,7 +193,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testVariadic() {
|
||||
public function testVariadic(): void {
|
||||
$node = $this->createParamBuilder('test')
|
||||
->makeVariadic()
|
||||
->getNode()
|
||||
@ -205,7 +205,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testMakePublic() {
|
||||
public function testMakePublic(): void {
|
||||
$node = $this->createParamBuilder('test')
|
||||
->makePublic()
|
||||
->getNode()
|
||||
@ -217,7 +217,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testMakeProtected() {
|
||||
public function testMakeProtected(): void {
|
||||
$node = $this->createParamBuilder('test')
|
||||
->makeProtected()
|
||||
->getNode()
|
||||
@ -227,9 +227,19 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
new Node\Param(new Expr\Variable('test'), null, null, false, false, [], Modifiers::PROTECTED),
|
||||
$node
|
||||
);
|
||||
|
||||
$node = $this->createParamBuilder('test')
|
||||
->makeProtectedSet()
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
new Node\Param(new Expr\Variable('test'), null, null, false, false, [], Modifiers::PROTECTED_SET),
|
||||
$node
|
||||
);
|
||||
}
|
||||
|
||||
public function testMakePrivate() {
|
||||
public function testMakePrivate(): void {
|
||||
$node = $this->createParamBuilder('test')
|
||||
->makePrivate()
|
||||
->getNode()
|
||||
@ -239,9 +249,19 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
new Node\Param(new Expr\Variable('test'), null, null, false, false, [], Modifiers::PRIVATE),
|
||||
$node
|
||||
);
|
||||
|
||||
$node = $this->createParamBuilder('test')
|
||||
->makePrivateSet()
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
new Node\Param(new Expr\Variable('test'), null, null, false, false, [], Modifiers::PRIVATE_SET),
|
||||
$node
|
||||
);
|
||||
}
|
||||
|
||||
public function testMakeReadonly() {
|
||||
public function testMakeReadonly(): void {
|
||||
$node = $this->createParamBuilder('test')
|
||||
->makeReadonly()
|
||||
->getNode()
|
||||
@ -253,7 +273,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser\Comment;
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Modifiers;
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Attribute;
|
||||
@ -10,6 +11,8 @@ use PhpParser\Node\AttributeGroup;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\PropertyHook;
|
||||
use PhpParser\Node\PropertyItem;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Scalar\Int_;
|
||||
use PhpParser\Node\Stmt;
|
||||
@ -19,7 +22,7 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
||||
return new Property($name);
|
||||
}
|
||||
|
||||
public function testModifiers() {
|
||||
public function testModifiers(): void {
|
||||
$node = $this->createPropertyBuilder('test')
|
||||
->makePrivate()
|
||||
->makeStatic()
|
||||
@ -29,9 +32,7 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(
|
||||
Modifiers::PRIVATE | Modifiers::STATIC,
|
||||
[
|
||||
new \PhpParser\Node\PropertyItem('test')
|
||||
]
|
||||
[new PropertyItem('test')]
|
||||
),
|
||||
$node
|
||||
);
|
||||
@ -44,9 +45,7 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(
|
||||
Modifiers::PROTECTED,
|
||||
[
|
||||
new \PhpParser\Node\PropertyItem('test')
|
||||
]
|
||||
[new PropertyItem('test')]
|
||||
),
|
||||
$node
|
||||
);
|
||||
@ -59,9 +58,7 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(
|
||||
Modifiers::PUBLIC,
|
||||
[
|
||||
new \PhpParser\Node\PropertyItem('test')
|
||||
]
|
||||
[new PropertyItem('test')]
|
||||
),
|
||||
$node
|
||||
);
|
||||
@ -74,15 +71,40 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(
|
||||
Modifiers::READONLY,
|
||||
[
|
||||
new \PhpParser\Node\PropertyItem('test')
|
||||
]
|
||||
[new PropertyItem('test')]
|
||||
),
|
||||
$node
|
||||
);
|
||||
|
||||
$node = $this->createPropertyBuilder('test')
|
||||
->makeFinal()
|
||||
->getNode();
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(Modifiers::FINAL, [new PropertyItem('test')]),
|
||||
$node);
|
||||
|
||||
$node = $this->createPropertyBuilder('test')
|
||||
->makePrivateSet()
|
||||
->getNode();
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(Modifiers::PRIVATE_SET, [new PropertyItem('test')]),
|
||||
$node);
|
||||
|
||||
$node = $this->createPropertyBuilder('test')
|
||||
->makeProtectedSet()
|
||||
->getNode();
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(Modifiers::PROTECTED_SET, [new PropertyItem('test')]),
|
||||
$node);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
public function testAbstractWithoutHook() {
|
||||
$this->expectException(Error::class);
|
||||
$this->expectExceptionMessage('Only hooked properties may be declared abstract');
|
||||
$this->createPropertyBuilder('test')->makeAbstract()->getNode();
|
||||
}
|
||||
|
||||
public function testDocComment(): void {
|
||||
$node = $this->createPropertyBuilder('test')
|
||||
->setDocComment('/** Test */')
|
||||
->getNode();
|
||||
@ -101,7 +123,7 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestDefaultValues
|
||||
*/
|
||||
public function testDefaultValues($value, $expectedValueNode) {
|
||||
public function testDefaultValues($value, $expectedValueNode): void {
|
||||
$node = $this->createPropertyBuilder('test')
|
||||
->setDefault($value)
|
||||
->getNode()
|
||||
@ -110,7 +132,7 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals($expectedValueNode, $node->props[0]->default);
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
@ -136,7 +158,24 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function provideTestDefaultValues() {
|
||||
public function testAddHook(): void {
|
||||
$get = new PropertyHook('get', null);
|
||||
$set = new PropertyHook('set', null);
|
||||
$node = $this->createPropertyBuilder('test')
|
||||
->addHook($get)
|
||||
->addHook($set)
|
||||
->makeAbstract()
|
||||
->getNode();
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(
|
||||
Modifiers::ABSTRACT,
|
||||
[new PropertyItem('test')],
|
||||
[], null, [],
|
||||
[$get, $set]),
|
||||
$node);
|
||||
}
|
||||
|
||||
public static function provideTestDefaultValues() {
|
||||
return [
|
||||
[
|
||||
null,
|
||||
|
@ -23,7 +23,7 @@ class TraitTest extends \PHPUnit\Framework\TestCase {
|
||||
return new Trait_($class);
|
||||
}
|
||||
|
||||
public function testStmtAddition() {
|
||||
public function testStmtAddition(): void {
|
||||
$method1 = new Stmt\ClassMethod('test1');
|
||||
$method2 = new Stmt\ClassMethod('test2');
|
||||
$method3 = new Stmt\ClassMethod('test3');
|
||||
@ -49,7 +49,7 @@ class TraitTest extends \PHPUnit\Framework\TestCase {
|
||||
]), $trait);
|
||||
}
|
||||
|
||||
public function testInvalidStmtError() {
|
||||
public function testInvalidStmtError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Unexpected node of type "Stmt_Echo"');
|
||||
$this->createTraitBuilder('Test')
|
||||
@ -57,7 +57,7 @@ class TraitTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testGetMethods() {
|
||||
public function testGetMethods(): void {
|
||||
$methods = [
|
||||
new ClassMethod('foo'),
|
||||
new ClassMethod('bar'),
|
||||
@ -77,7 +77,7 @@ class TraitTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame($methods, $trait->getMethods());
|
||||
}
|
||||
|
||||
public function testGetProperties() {
|
||||
public function testGetProperties(): void {
|
||||
$properties = [
|
||||
new Property(Modifiers::PUBLIC, [new PropertyItem('foo')]),
|
||||
new Property(Modifiers::PUBLIC, [new PropertyItem('bar')]),
|
||||
@ -95,7 +95,7 @@ class TraitTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame($properties, $trait->getProperties());
|
||||
}
|
||||
|
||||
public function testAddAttribute() {
|
||||
public function testAddAttribute(): void {
|
||||
$attribute = new Attribute(
|
||||
new Name('Attr'),
|
||||
[new Arg(new Int_(1), false, false, [], new Identifier('name'))]
|
||||
|
@ -12,7 +12,7 @@ class TraitUseAdaptationTest extends \PHPUnit\Framework\TestCase {
|
||||
return new TraitUseAdaptation($trait, $method);
|
||||
}
|
||||
|
||||
public function testAsMake() {
|
||||
public function testAsMake(): void {
|
||||
$builder = $this->createTraitUseAdaptationBuilder(null, 'foo');
|
||||
|
||||
$this->assertEquals(
|
||||
@ -36,7 +36,7 @@ class TraitUseAdaptationTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testInsteadof() {
|
||||
public function testInsteadof(): void {
|
||||
$node = $this->createTraitUseAdaptationBuilder('SomeTrait', 'foo')
|
||||
->insteadof('AnotherTrait')
|
||||
->getNode()
|
||||
@ -52,7 +52,7 @@ class TraitUseAdaptationTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testAsOnNotAlias() {
|
||||
public function testAsOnNotAlias(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Cannot set alias for not alias adaptation buider');
|
||||
$this->createTraitUseAdaptationBuilder('Test', 'foo')
|
||||
@ -61,7 +61,7 @@ class TraitUseAdaptationTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testInsteadofOnNotPrecedence() {
|
||||
public function testInsteadofOnNotPrecedence(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Cannot add overwritten traits for not precedence adaptation buider');
|
||||
$this->createTraitUseAdaptationBuilder('Test', 'foo')
|
||||
@ -70,7 +70,7 @@ class TraitUseAdaptationTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testInsteadofWithoutTrait() {
|
||||
public function testInsteadofWithoutTrait(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Precedence adaptation must have trait');
|
||||
$this->createTraitUseAdaptationBuilder(null, 'foo')
|
||||
@ -78,7 +78,7 @@ class TraitUseAdaptationTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testMakeOnNotAlias() {
|
||||
public function testMakeOnNotAlias(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Cannot set access modifier for not alias adaptation buider');
|
||||
$this->createTraitUseAdaptationBuilder('Test', 'foo')
|
||||
@ -87,7 +87,7 @@ class TraitUseAdaptationTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testMultipleMake() {
|
||||
public function testMultipleMake(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Multiple access type modifiers are not allowed');
|
||||
$this->createTraitUseAdaptationBuilder(null, 'foo')
|
||||
@ -96,7 +96,7 @@ class TraitUseAdaptationTest extends \PHPUnit\Framework\TestCase {
|
||||
;
|
||||
}
|
||||
|
||||
public function testUndefinedType() {
|
||||
public function testUndefinedType(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Type of adaptation is not defined');
|
||||
$this->createTraitUseAdaptationBuilder(null, 'foo')
|
||||
|
@ -10,7 +10,7 @@ class TraitUseTest extends \PHPUnit\Framework\TestCase {
|
||||
return new TraitUse(...$traits);
|
||||
}
|
||||
|
||||
public function testAnd() {
|
||||
public function testAnd(): void {
|
||||
$node = $this->createTraitUseBuilder('SomeTrait')
|
||||
->and('AnotherTrait')
|
||||
->getNode()
|
||||
@ -25,7 +25,7 @@ class TraitUseTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testWith() {
|
||||
public function testWith(): void {
|
||||
$node = $this->createTraitUseBuilder('SomeTrait')
|
||||
->with(new Stmt\TraitUseAdaptation\Alias(null, 'foo', null, 'bar'))
|
||||
->with((new TraitUseAdaptation(null, 'test'))->as('baz'))
|
||||
@ -41,7 +41,7 @@ class TraitUseTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidAdaptationNode() {
|
||||
public function testInvalidAdaptationNode(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Adaptation must have type TraitUseAdaptation');
|
||||
$this->createTraitUseBuilder('Test')
|
||||
|
@ -11,7 +11,7 @@ class UseTest extends \PHPUnit\Framework\TestCase {
|
||||
return new Builder\Use_($name, $type);
|
||||
}
|
||||
|
||||
public function testCreation() {
|
||||
public function testCreation(): void {
|
||||
$node = $this->createUseBuilder('Foo\Bar')->getNode();
|
||||
$this->assertEquals(new Stmt\Use_([
|
||||
new \PhpParser\Node\UseItem(new Name('Foo\Bar'), null)
|
||||
|
@ -15,12 +15,12 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestFactory
|
||||
*/
|
||||
public function testFactory($methodName, $className) {
|
||||
public function testFactory($methodName, $className): void {
|
||||
$factory = new BuilderFactory();
|
||||
$this->assertInstanceOf($className, $factory->$methodName('test'));
|
||||
}
|
||||
|
||||
public function provideTestFactory() {
|
||||
public static function provideTestFactory() {
|
||||
return [
|
||||
['namespace', Builder\Namespace_::class],
|
||||
['class', Builder\Class_::class],
|
||||
@ -38,12 +38,12 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function testFactoryClassConst() {
|
||||
public function testFactoryClassConst(): void {
|
||||
$factory = new BuilderFactory();
|
||||
$this->assertInstanceOf(Builder\ClassConst::class, $factory->classConst('TEST', 1));
|
||||
}
|
||||
|
||||
public function testAttribute() {
|
||||
public function testAttribute(): void {
|
||||
$factory = new BuilderFactory();
|
||||
$this->assertEquals(
|
||||
new Attribute(new Name('AttributeName'), [new Arg(
|
||||
@ -53,7 +53,7 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testVal() {
|
||||
public function testVal(): void {
|
||||
// This method is a wrapper around BuilderHelpers::normalizeValue(),
|
||||
// which is already tested elsewhere
|
||||
$factory = new BuilderFactory();
|
||||
@ -63,7 +63,7 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testConcat() {
|
||||
public function testConcat(): void {
|
||||
$factory = new BuilderFactory();
|
||||
$varA = new Expr\Variable('a');
|
||||
$varB = new Expr\Variable('b');
|
||||
@ -83,19 +83,19 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testConcatOneError() {
|
||||
public function testConcatOneError(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Expected at least two expressions');
|
||||
(new BuilderFactory())->concat("a");
|
||||
}
|
||||
|
||||
public function testConcatInvalidExpr() {
|
||||
public function testConcatInvalidExpr(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Expected string or Expr');
|
||||
(new BuilderFactory())->concat("a", 42);
|
||||
}
|
||||
|
||||
public function testArgs() {
|
||||
public function testArgs(): void {
|
||||
$factory = new BuilderFactory();
|
||||
$unpack = new Arg(new Expr\Variable('c'), false, true);
|
||||
$this->assertEquals(
|
||||
@ -108,7 +108,7 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testNamedArgs() {
|
||||
public function testNamedArgs(): void {
|
||||
$factory = new BuilderFactory();
|
||||
$this->assertEquals(
|
||||
[
|
||||
@ -119,7 +119,7 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testCalls() {
|
||||
public function testCalls(): void {
|
||||
$factory = new BuilderFactory();
|
||||
|
||||
// Simple function call
|
||||
@ -195,7 +195,7 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testConstFetches() {
|
||||
public function testConstFetches(): void {
|
||||
$factory = new BuilderFactory();
|
||||
$this->assertEquals(
|
||||
new Expr\ConstFetch(new Name('FOO')),
|
||||
@ -215,7 +215,7 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testVar() {
|
||||
public function testVar(): void {
|
||||
$factory = new BuilderFactory();
|
||||
$this->assertEquals(
|
||||
new Expr\Variable("foo"),
|
||||
@ -227,7 +227,7 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testPropertyFetch() {
|
||||
public function testPropertyFetch(): void {
|
||||
$f = new BuilderFactory();
|
||||
$this->assertEquals(
|
||||
new Expr\PropertyFetch(new Expr\Variable('foo'), 'bar'),
|
||||
@ -243,31 +243,31 @@ class BuilderFactoryTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidIdentifier() {
|
||||
public function testInvalidIdentifier(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Expected string or instance of Node\Identifier');
|
||||
(new BuilderFactory())->classConstFetch('Foo', new Name('foo'));
|
||||
}
|
||||
|
||||
public function testInvalidIdentifierOrExpr() {
|
||||
public function testInvalidIdentifierOrExpr(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Expected string or instance of Node\Identifier or Node\Expr');
|
||||
(new BuilderFactory())->staticCall('Foo', new Name('bar'));
|
||||
}
|
||||
|
||||
public function testInvalidNameOrExpr() {
|
||||
public function testInvalidNameOrExpr(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Name must be a string or an instance of Node\Name or Node\Expr');
|
||||
(new BuilderFactory())->funcCall(new Node\Stmt\Return_());
|
||||
}
|
||||
|
||||
public function testInvalidVar() {
|
||||
public function testInvalidVar(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Variable name must be string or Expr');
|
||||
(new BuilderFactory())->var(new Node\Stmt\Return_());
|
||||
}
|
||||
|
||||
public function testIntegration() {
|
||||
public function testIntegration(): void {
|
||||
$factory = new BuilderFactory();
|
||||
$node = $factory->namespace('Name\Space')
|
||||
->addStmt($factory->use('Foo\Bar\SomeOtherClass'))
|
||||
@ -365,7 +365,10 @@ abstract class SomeClass extends SomeOtherClass implements A\Few, \Interfaces
|
||||
* @param SomeClass And takes a parameter
|
||||
*/
|
||||
abstract public function someMethod(SomeClass $someParam);
|
||||
protected function anotherMethod(#[TaggedIterator('app.handlers')] $someParam = 'test')
|
||||
protected function anotherMethod(
|
||||
#[TaggedIterator('app.handlers')]
|
||||
$someParam = 'test'
|
||||
)
|
||||
{
|
||||
print $someParam;
|
||||
}
|
||||
|
@ -4,12 +4,13 @@ namespace PhpParser;
|
||||
|
||||
use PhpParser\Builder\Class_;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Expr;
|
||||
|
||||
class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testNormalizeNode() {
|
||||
public function testNormalizeNode(): void {
|
||||
$builder = new Class_('SomeClass');
|
||||
$this->assertEquals($builder->getNode(), BuilderHelpers::normalizeNode($builder));
|
||||
|
||||
@ -21,7 +22,7 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeNode('test');
|
||||
}
|
||||
|
||||
public function testNormalizeStmt() {
|
||||
public function testNormalizeStmt(): void {
|
||||
$stmt = new Node\Stmt\Class_('Class');
|
||||
$this->assertSame($stmt, BuilderHelpers::normalizeStmt($stmt));
|
||||
|
||||
@ -35,13 +36,13 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeStmt(new Node\Attribute(new Node\Name('Test')));
|
||||
}
|
||||
|
||||
public function testNormalizeStmtInvalidType() {
|
||||
public function testNormalizeStmtInvalidType(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Expected node or builder object');
|
||||
BuilderHelpers::normalizeStmt('test');
|
||||
}
|
||||
|
||||
public function testNormalizeIdentifier() {
|
||||
public function testNormalizeIdentifier(): void {
|
||||
$identifier = new Node\Identifier('fn');
|
||||
$this->assertSame($identifier, BuilderHelpers::normalizeIdentifier($identifier));
|
||||
$this->assertEquals($identifier, BuilderHelpers::normalizeIdentifier('fn'));
|
||||
@ -51,7 +52,7 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeIdentifier(1);
|
||||
}
|
||||
|
||||
public function testNormalizeIdentifierOrExpr() {
|
||||
public function testNormalizeIdentifierOrExpr(): void {
|
||||
$identifier = new Node\Identifier('fn');
|
||||
$this->assertSame($identifier, BuilderHelpers::normalizeIdentifierOrExpr($identifier));
|
||||
|
||||
@ -64,7 +65,7 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeIdentifierOrExpr(1);
|
||||
}
|
||||
|
||||
public function testNormalizeName() {
|
||||
public function testNormalizeName(): void {
|
||||
$name = new Node\Name('test');
|
||||
$this->assertSame($name, BuilderHelpers::normalizeName($name));
|
||||
$this->assertEquals(
|
||||
@ -82,13 +83,13 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeName('');
|
||||
}
|
||||
|
||||
public function testNormalizeNameInvalidType() {
|
||||
public function testNormalizeNameInvalidType(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Name must be a string or an instance of Node\Name');
|
||||
BuilderHelpers::normalizeName(1);
|
||||
}
|
||||
|
||||
public function testNormalizeNameOrExpr() {
|
||||
public function testNormalizeNameOrExpr(): void {
|
||||
$expr = new Expr\Variable('fn');
|
||||
$this->assertSame($expr, BuilderHelpers::normalizeNameOrExpr($expr));
|
||||
|
||||
@ -109,13 +110,13 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeNameOrExpr('');
|
||||
}
|
||||
|
||||
public function testNormalizeNameOrExpInvalidType() {
|
||||
public function testNormalizeNameOrExpInvalidType(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Name must be a string or an instance of Node\Name or Node\Expr');
|
||||
BuilderHelpers::normalizeNameOrExpr(1);
|
||||
}
|
||||
|
||||
public function testNormalizeType() {
|
||||
public function testNormalizeType(): void {
|
||||
$this->assertEquals(new Node\Identifier('array'), BuilderHelpers::normalizeType('array'));
|
||||
$this->assertEquals(new Node\Identifier('callable'), BuilderHelpers::normalizeType('callable'));
|
||||
$this->assertEquals(new Node\Identifier('string'), BuilderHelpers::normalizeType('string'));
|
||||
@ -156,25 +157,25 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeType(1);
|
||||
}
|
||||
|
||||
public function testNormalizeTypeNullableVoid() {
|
||||
public function testNormalizeTypeNullableVoid(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('void type cannot be nullable');
|
||||
BuilderHelpers::normalizeType('?void');
|
||||
}
|
||||
|
||||
public function testNormalizeTypeNullableMixed() {
|
||||
public function testNormalizeTypeNullableMixed(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('mixed type cannot be nullable');
|
||||
BuilderHelpers::normalizeType('?mixed');
|
||||
}
|
||||
|
||||
public function testNormalizeTypeNullableNever() {
|
||||
public function testNormalizeTypeNullableNever(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('never type cannot be nullable');
|
||||
BuilderHelpers::normalizeType('?never');
|
||||
}
|
||||
|
||||
public function testNormalizeValue() {
|
||||
public function testNormalizeValue(): void {
|
||||
$expression = new Scalar\Int_(1);
|
||||
$this->assertSame($expression, BuilderHelpers::normalizeValue($expression));
|
||||
|
||||
@ -200,7 +201,7 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeValue(new \stdClass());
|
||||
}
|
||||
|
||||
public function testNormalizeDocComment() {
|
||||
public function testNormalizeDocComment(): void {
|
||||
$docComment = new Comment\Doc('Some doc comment');
|
||||
$this->assertSame($docComment, BuilderHelpers::normalizeDocComment($docComment));
|
||||
|
||||
@ -211,7 +212,7 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
BuilderHelpers::normalizeDocComment(1);
|
||||
}
|
||||
|
||||
public function testNormalizeAttribute() {
|
||||
public function testNormalizeAttribute(): void {
|
||||
$attribute = new Node\Attribute(new Node\Name('Test'));
|
||||
$attributeGroup = new Node\AttributeGroup([$attribute]);
|
||||
|
||||
@ -222,4 +223,14 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->expectExceptionMessage('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup');
|
||||
BuilderHelpers::normalizeAttribute('test');
|
||||
}
|
||||
|
||||
public function testNormalizeValueEnum() {
|
||||
if (\PHP_VERSION_ID <= 80100) {
|
||||
$this->markTestSkipped('Enums are supported since PHP 8.1');
|
||||
}
|
||||
|
||||
include __DIR__ . '/../fixtures/Suit.php';
|
||||
|
||||
$this->assertEquals(new Expr\ClassConstFetch(new FullyQualified(\Suit::class), new Identifier('Hearts')), BuilderHelpers::normalizeValue(\Suit::Hearts));
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ class CodeParsingTest extends CodeTestAbstract {
|
||||
/**
|
||||
* @dataProvider provideTestParse
|
||||
*/
|
||||
public function testParse($name, $code, $expected, $modeLine) {
|
||||
public function testParse($name, $code, $expected, $modeLine): void {
|
||||
$modes = $this->parseModeLine($modeLine);
|
||||
$parser = $this->createParser($modes['version'] ?? null);
|
||||
list($stmts, $output) = $this->getParseOutput($parser, $code, $modes);
|
||||
@ -50,8 +50,8 @@ class CodeParsingTest extends CodeTestAbstract {
|
||||
return [$stmts, canonicalize($output)];
|
||||
}
|
||||
|
||||
public function provideTestParse() {
|
||||
return $this->getTests(__DIR__ . '/../code/parser', 'test');
|
||||
public static function provideTestParse() {
|
||||
return self::getTests(__DIR__ . '/../code/parser', 'test');
|
||||
}
|
||||
|
||||
private function formatErrorMessage(Error $e, $code) {
|
||||
@ -62,13 +62,13 @@ class CodeParsingTest extends CodeTestAbstract {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
private function checkAttributes($stmts) {
|
||||
private function checkAttributes($stmts): void {
|
||||
if ($stmts === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$traverser = new NodeTraverser(new class () extends NodeVisitorAbstract {
|
||||
public function enterNode(Node $node) {
|
||||
public function enterNode(Node $node): void {
|
||||
$startLine = $node->getStartLine();
|
||||
$endLine = $node->getEndLine();
|
||||
$startFilePos = $node->getStartFilePos();
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace PhpParser;
|
||||
|
||||
abstract class CodeTestAbstract extends \PHPUnit\Framework\TestCase {
|
||||
protected function getTests($directory, $fileExtension, $chunksPerTest = 2) {
|
||||
protected static function getTests($directory, $fileExtension, $chunksPerTest = 2) {
|
||||
$parser = new CodeTestParser();
|
||||
$allTests = [];
|
||||
foreach (filesInDir($directory, $fileExtension) as $fileName => $fileContents) {
|
||||
|
@ -24,7 +24,7 @@ class CodeTestParser {
|
||||
// multiple sections possible with always two forming a pair
|
||||
$chunks = array_chunk($parts, $chunksPerTest);
|
||||
$tests = [];
|
||||
foreach ($chunks as $i => $chunk) {
|
||||
foreach ($chunks as $chunk) {
|
||||
$lastPart = array_pop($chunk);
|
||||
list($lastPart, $mode) = $this->extractMode($lastPart);
|
||||
$tests[] = [$mode, array_merge($chunk, [$lastPart])];
|
||||
@ -61,7 +61,7 @@ class CodeTestParser {
|
||||
return [$expected, null];
|
||||
}
|
||||
|
||||
$expected = (string) substr($expected, $firstNewLine + 1);
|
||||
$expected = substr($expected, $firstNewLine + 1);
|
||||
return [$expected, substr($firstLine, 2)];
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace PhpParser;
|
||||
|
||||
class CommentTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testGetters() {
|
||||
public function testGetters(): void {
|
||||
$comment = new Comment('/* Some comment */',
|
||||
1, 10, 2, 1, 27, 2);
|
||||
|
||||
@ -20,12 +20,12 @@ class CommentTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestReformatting
|
||||
*/
|
||||
public function testReformatting($commentText, $reformattedText) {
|
||||
public function testReformatting($commentText, $reformattedText): void {
|
||||
$comment = new Comment($commentText);
|
||||
$this->assertSame($reformattedText, $comment->getReformattedText());
|
||||
}
|
||||
|
||||
public function provideTestReformatting() {
|
||||
public static function provideTestReformatting() {
|
||||
return [
|
||||
['// Some text', '// Some text'],
|
||||
['/* Some text */', '/* Some text */'],
|
||||
|
@ -13,7 +13,7 @@ class CompatibilityTest extends \PHPUnit\Framework\TestCase {
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testAliases1() {
|
||||
public function testAliases1(): void {
|
||||
$var = new Expr\Variable('x');
|
||||
$node = new Node\ClosureUse($var);
|
||||
$this->assertTrue($node instanceof Expr\ClosureUse);
|
||||
@ -41,7 +41,7 @@ class CompatibilityTest extends \PHPUnit\Framework\TestCase {
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testAliases2() {
|
||||
public function testAliases2(): void {
|
||||
$var = new Expr\Variable('x');
|
||||
$node = new Node\Expr\ClosureUse($var);
|
||||
$this->assertTrue($node instanceof Node\ClosureUse);
|
||||
|
@ -7,14 +7,14 @@ use PhpParser\Node\Scalar;
|
||||
|
||||
class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
||||
/** @dataProvider provideTestEvaluate */
|
||||
public function testEvaluate($exprString, $expected) {
|
||||
public function testEvaluate($exprString, $expected): void {
|
||||
$parser = new Parser\Php7(new Lexer());
|
||||
$expr = $parser->parse('<?php ' . $exprString . ';')[0]->expr;
|
||||
$evaluator = new ConstExprEvaluator();
|
||||
$this->assertSame($expected, $evaluator->evaluateDirectly($expr));
|
||||
}
|
||||
|
||||
public function provideTestEvaluate() {
|
||||
public static function provideTestEvaluate() {
|
||||
return [
|
||||
['1', 1],
|
||||
['1.0', 1.0],
|
||||
@ -74,14 +74,14 @@ class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function testEvaluateFails() {
|
||||
public function testEvaluateFails(): void {
|
||||
$this->expectException(ConstExprEvaluationException::class);
|
||||
$this->expectExceptionMessage('Expression of type Expr_Variable cannot be evaluated');
|
||||
$evaluator = new ConstExprEvaluator();
|
||||
$evaluator->evaluateDirectly(new Expr\Variable('a'));
|
||||
}
|
||||
|
||||
public function testEvaluateFallback() {
|
||||
public function testEvaluateFallback(): void {
|
||||
$evaluator = new ConstExprEvaluator(function (Expr $expr) {
|
||||
if ($expr instanceof Scalar\MagicConst\Line) {
|
||||
return 42;
|
||||
@ -98,7 +98,7 @@ class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestEvaluateSilently
|
||||
*/
|
||||
public function testEvaluateSilently($expr, $exception, $msg) {
|
||||
public function testEvaluateSilently($expr, $exception, $msg): void {
|
||||
$evaluator = new ConstExprEvaluator();
|
||||
|
||||
try {
|
||||
@ -115,7 +115,7 @@ class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public function provideTestEvaluateSilently() {
|
||||
public static function provideTestEvaluateSilently() {
|
||||
return [
|
||||
[
|
||||
new Expr\BinaryOp\Mod(new Scalar\Int_(42), new Scalar\Int_(0)),
|
||||
|
@ -5,7 +5,7 @@ namespace PhpParser\ErrorHandler;
|
||||
use PhpParser\Error;
|
||||
|
||||
class CollectingTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testHandleError() {
|
||||
public function testHandleError(): void {
|
||||
$errorHandler = new Collecting();
|
||||
$this->assertFalse($errorHandler->hasErrors());
|
||||
$this->assertEmpty($errorHandler->getErrors());
|
||||
|
@ -5,7 +5,7 @@ namespace PhpParser\ErrorHandler;
|
||||
use PhpParser\Error;
|
||||
|
||||
class ThrowingTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testHandleError() {
|
||||
public function testHandleError(): void {
|
||||
$this->expectException(Error::class);
|
||||
$this->expectExceptionMessage('Test');
|
||||
$errorHandler = new Throwing();
|
||||
|
@ -22,7 +22,7 @@ class ErrorTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @depends testConstruct
|
||||
*/
|
||||
public function testSetMessageAndLine(Error $error) {
|
||||
public function testSetMessageAndLine(Error $error): void {
|
||||
$error->setRawMessage('Some other error');
|
||||
$this->assertSame('Some other error', $error->getRawMessage());
|
||||
|
||||
@ -31,7 +31,7 @@ class ErrorTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame('Some other error on line 15', $error->getMessage());
|
||||
}
|
||||
|
||||
public function testUnknownLine() {
|
||||
public function testUnknownLine(): void {
|
||||
$error = new Error('Some error');
|
||||
|
||||
$this->assertSame(-1, $error->getStartLine());
|
||||
@ -40,7 +40,7 @@ class ErrorTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
|
||||
/** @dataProvider provideTestColumnInfo */
|
||||
public function testColumnInfo($code, $startPos, $endPos, $startColumn, $endColumn) {
|
||||
public function testColumnInfo($code, $startPos, $endPos, $startColumn, $endColumn): void {
|
||||
$error = new Error('Some error', [
|
||||
'startFilePos' => $startPos,
|
||||
'endFilePos' => $endPos,
|
||||
@ -51,7 +51,7 @@ class ErrorTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame($endColumn, $error->getEndColumn($code));
|
||||
}
|
||||
|
||||
public function provideTestColumnInfo() {
|
||||
public static function provideTestColumnInfo() {
|
||||
return [
|
||||
// Error at "bar"
|
||||
["<?php foo bar baz", 10, 12, 11, 13],
|
||||
@ -72,7 +72,7 @@ class ErrorTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function testNoColumnInfo() {
|
||||
public function testNoColumnInfo(): void {
|
||||
$error = new Error('Some error', ['startLine' => 3]);
|
||||
|
||||
$this->assertFalse($error->hasColumnInfo());
|
||||
@ -90,7 +90,7 @@ class ErrorTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public function testInvalidPosInfo() {
|
||||
public function testInvalidPosInfo(): void {
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessage('Invalid position information');
|
||||
$error = new Error('Some error', [
|
||||
|
@ -28,7 +28,7 @@ class DifferTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
|
||||
/** @dataProvider provideTestDiff */
|
||||
public function testDiff($oldStr, $newStr, $expectedDiffStr) {
|
||||
public function testDiff($oldStr, $newStr, $expectedDiffStr): void {
|
||||
$differ = new Differ(function ($a, $b) {
|
||||
return $a === $b;
|
||||
});
|
||||
@ -36,7 +36,7 @@ class DifferTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame($expectedDiffStr, $this->formatDiffString($diff));
|
||||
}
|
||||
|
||||
public function provideTestDiff() {
|
||||
public static function provideTestDiff() {
|
||||
return [
|
||||
['abc', 'abc', 'abc'],
|
||||
['abc', 'abcdef', 'abc+d+e+f'],
|
||||
@ -49,7 +49,7 @@ class DifferTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
|
||||
/** @dataProvider provideTestDiffWithReplacements */
|
||||
public function testDiffWithReplacements($oldStr, $newStr, $expectedDiffStr) {
|
||||
public function testDiffWithReplacements($oldStr, $newStr, $expectedDiffStr): void {
|
||||
$differ = new Differ(function ($a, $b) {
|
||||
return $a === $b;
|
||||
});
|
||||
@ -57,7 +57,7 @@ class DifferTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame($expectedDiffStr, $this->formatDiffString($diff));
|
||||
}
|
||||
|
||||
public function provideTestDiffWithReplacements() {
|
||||
public static function provideTestDiffWithReplacements() {
|
||||
return [
|
||||
['abcde', 'axyze', 'a/bx/cy/dze'],
|
||||
['abcde', 'xbcdy', '/axbcd/ey'],
|
||||
@ -66,7 +66,7 @@ class DifferTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function testNonContiguousIndices() {
|
||||
public function testNonContiguousIndices(): void {
|
||||
$differ = new Differ(function ($a, $b) {
|
||||
return $a === $b;
|
||||
});
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace PhpParser;
|
||||
|
||||
class JsonDecoderTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testRoundTrip() {
|
||||
public function testRoundTrip(): void {
|
||||
$code = <<<'PHP'
|
||||
<?php
|
||||
// comment
|
||||
@ -23,14 +23,14 @@ PHP;
|
||||
}
|
||||
|
||||
/** @dataProvider provideTestDecodingError */
|
||||
public function testDecodingError($json, $expectedMessage) {
|
||||
public function testDecodingError($json, $expectedMessage): void {
|
||||
$jsonDecoder = new JsonDecoder();
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessage($expectedMessage);
|
||||
$jsonDecoder->decode($json);
|
||||
}
|
||||
|
||||
public function provideTestDecodingError() {
|
||||
public static function provideTestDecodingError() {
|
||||
return [
|
||||
['???', 'JSON decoding error: Syntax error'],
|
||||
['{"nodeType":123}', 'Node type must be a string'],
|
||||
|
@ -19,7 +19,7 @@ class EmulativeTest extends LexerTest {
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
public function testReplaceKeywords(string $keyword, int $expectedToken) {
|
||||
public function testReplaceKeywords(string $keyword, int $expectedToken): void {
|
||||
$lexer = $this->getLexer();
|
||||
$code = '<?php ' . $keyword;
|
||||
$this->assertEquals([
|
||||
@ -32,7 +32,7 @@ class EmulativeTest extends LexerTest {
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
public function testReplaceKeywordsUppercase(string $keyword, int $expectedToken) {
|
||||
public function testReplaceKeywordsUppercase(string $keyword, int $expectedToken): void {
|
||||
$lexer = $this->getLexer();
|
||||
$code = '<?php ' . strtoupper($keyword);
|
||||
|
||||
@ -46,7 +46,7 @@ class EmulativeTest extends LexerTest {
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
public function testNoReplaceKeywordsAfterObjectOperator(string $keyword) {
|
||||
public function testNoReplaceKeywordsAfterObjectOperator(string $keyword): void {
|
||||
$lexer = $this->getLexer();
|
||||
$code = '<?php ->' . $keyword;
|
||||
|
||||
@ -61,7 +61,7 @@ class EmulativeTest extends LexerTest {
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
public function testNoReplaceKeywordsAfterObjectOperatorWithSpaces(string $keyword) {
|
||||
public function testNoReplaceKeywordsAfterObjectOperatorWithSpaces(string $keyword): void {
|
||||
$lexer = $this->getLexer();
|
||||
$code = '<?php -> ' . $keyword;
|
||||
|
||||
@ -77,7 +77,7 @@ class EmulativeTest extends LexerTest {
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
public function testNoReplaceKeywordsAfterNullsafeObjectOperator(string $keyword) {
|
||||
public function testNoReplaceKeywordsAfterNullsafeObjectOperator(string $keyword): void {
|
||||
$lexer = $this->getLexer();
|
||||
$code = '<?php ?->' . $keyword;
|
||||
|
||||
@ -89,8 +89,11 @@ class EmulativeTest extends LexerTest {
|
||||
], $lexer->tokenize($code));
|
||||
}
|
||||
|
||||
public function provideTestReplaceKeywords() {
|
||||
public static function provideTestReplaceKeywords() {
|
||||
return [
|
||||
// PHP 8.4
|
||||
['__PROPERTY__', \T_PROPERTY_C],
|
||||
|
||||
// PHP 8.0
|
||||
['match', \T_MATCH],
|
||||
|
||||
@ -115,7 +118,7 @@ class EmulativeTest extends LexerTest {
|
||||
];
|
||||
}
|
||||
|
||||
private function assertSameTokens(array $expectedTokens, array $tokens) {
|
||||
private function assertSameTokens(array $expectedTokens, array $tokens): void {
|
||||
$reducedTokens = [];
|
||||
foreach ($tokens as $token) {
|
||||
if ($token->id === 0 || $token->isIgnorable()) {
|
||||
@ -129,7 +132,7 @@ class EmulativeTest extends LexerTest {
|
||||
/**
|
||||
* @dataProvider provideTestLexNewFeatures
|
||||
*/
|
||||
public function testLexNewFeatures(string $code, array $expectedTokens) {
|
||||
public function testLexNewFeatures(string $code, array $expectedTokens): void {
|
||||
$lexer = $this->getLexer();
|
||||
$this->assertSameTokens($expectedTokens, $lexer->tokenize('<?php ' . $code));
|
||||
}
|
||||
@ -137,7 +140,7 @@ class EmulativeTest extends LexerTest {
|
||||
/**
|
||||
* @dataProvider provideTestLexNewFeatures
|
||||
*/
|
||||
public function testLeaveStuffAloneInStrings(string $code) {
|
||||
public function testLeaveStuffAloneInStrings(string $code): void {
|
||||
$stringifiedToken = '"' . addcslashes($code, '"\\') . '"';
|
||||
|
||||
$lexer = $this->getLexer();
|
||||
@ -153,7 +156,7 @@ class EmulativeTest extends LexerTest {
|
||||
/**
|
||||
* @dataProvider provideTestLexNewFeatures
|
||||
*/
|
||||
public function testErrorAfterEmulation($code) {
|
||||
public function testErrorAfterEmulation($code): void {
|
||||
$errorHandler = new ErrorHandler\Collecting();
|
||||
$lexer = $this->getLexer();
|
||||
$lexer->tokenize('<?php ' . $code . "\0", $errorHandler);
|
||||
@ -173,7 +176,7 @@ class EmulativeTest extends LexerTest {
|
||||
$this->assertSame($expLine, $attrs['endLine']);
|
||||
}
|
||||
|
||||
public function provideTestLexNewFeatures() {
|
||||
public static function provideTestLexNewFeatures() {
|
||||
return [
|
||||
['yield from', [
|
||||
[\T_YIELD_FROM, 'yield from'],
|
||||
@ -390,18 +393,49 @@ class EmulativeTest extends LexerTest {
|
||||
[\T_READONLY, 'readonly'],
|
||||
[ord('('), '('],
|
||||
]],
|
||||
|
||||
// PHP 8.4: Asymmetric visibility modifiers
|
||||
['private(set)', [
|
||||
[\T_PRIVATE_SET, 'private(set)']
|
||||
]],
|
||||
['PROTECTED(SET)', [
|
||||
[\T_PROTECTED_SET, 'PROTECTED(SET)']
|
||||
]],
|
||||
['Public(Set)', [
|
||||
[\T_PUBLIC_SET, 'Public(Set)']
|
||||
]],
|
||||
['public (set)', [
|
||||
[\T_PUBLIC, 'public'],
|
||||
[\ord('('), '('],
|
||||
[\T_STRING, 'set'],
|
||||
[\ord(')'), ')'],
|
||||
]],
|
||||
['->public(set)', [
|
||||
[\T_OBJECT_OPERATOR, '->'],
|
||||
[\T_STRING, 'public'],
|
||||
[\ord('('), '('],
|
||||
[\T_STRING, 'set'],
|
||||
[\ord(')'), ')'],
|
||||
]],
|
||||
['?-> public(set)', [
|
||||
[\T_NULLSAFE_OBJECT_OPERATOR, '?->'],
|
||||
[\T_STRING, 'public'],
|
||||
[\ord('('), '('],
|
||||
[\T_STRING, 'set'],
|
||||
[\ord(')'), ')'],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestTargetVersion
|
||||
*/
|
||||
public function testTargetVersion(string $phpVersion, string $code, array $expectedTokens) {
|
||||
public function testTargetVersion(string $phpVersion, string $code, array $expectedTokens): void {
|
||||
$lexer = new Emulative(PhpVersion::fromString($phpVersion));
|
||||
$this->assertSameTokens($expectedTokens, $lexer->tokenize('<?php ' . $code));
|
||||
}
|
||||
|
||||
public function provideTestTargetVersion() {
|
||||
public static function provideTestTargetVersion() {
|
||||
return [
|
||||
['8.0', 'match', [[\T_MATCH, 'match']]],
|
||||
['7.4', 'match', [[\T_STRING, 'match']]],
|
||||
@ -424,6 +458,19 @@ class EmulativeTest extends LexerTest {
|
||||
[\T_ENCAPSED_AND_WHITESPACE, ' baz'],
|
||||
[ord('"'), '"'],
|
||||
]],
|
||||
['8.4', '__PROPERTY__', [[\T_PROPERTY_C, '__PROPERTY__']]],
|
||||
['8.3', '__PROPERTY__', [[\T_STRING, '__PROPERTY__']]],
|
||||
['8.4', '__property__', [[\T_PROPERTY_C, '__property__']]],
|
||||
['8.3', '__property__', [[\T_STRING, '__property__']]],
|
||||
['8.4', 'public(set)', [
|
||||
[\T_PUBLIC_SET, 'public(set)'],
|
||||
]],
|
||||
['8.3', 'public(set)', [
|
||||
[\T_PUBLIC, 'public'],
|
||||
[\ord('('), '('],
|
||||
[\T_STRING, 'set'],
|
||||
[\ord(')'), ')']
|
||||
]],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class LexerTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestError
|
||||
*/
|
||||
public function testError($code, $messages) {
|
||||
public function testError($code, $messages): void {
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('HHVM does not throw warnings from token_get_all()');
|
||||
}
|
||||
@ -29,7 +29,7 @@ class LexerTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public function provideTestError() {
|
||||
public static function provideTestError() {
|
||||
return [
|
||||
["<?php /*", ["Unterminated comment from 1:7 to 1:9"]],
|
||||
["<?php /*\n", ["Unterminated comment from 1:7 to 2:1"]],
|
||||
@ -45,7 +45,7 @@ class LexerTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function testDefaultErrorHandler() {
|
||||
public function testDefaultErrorHandler(): void {
|
||||
$this->expectException(Error::class);
|
||||
$this->expectExceptionMessage('Unterminated comment on line 1');
|
||||
$lexer = $this->getLexer();
|
||||
@ -55,7 +55,7 @@ class LexerTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestLex
|
||||
*/
|
||||
public function testLex($code, $expectedTokens) {
|
||||
public function testLex($code, $expectedTokens): void {
|
||||
$lexer = $this->getLexer();
|
||||
$tokens = $lexer->tokenize($code);
|
||||
foreach ($tokens as $token) {
|
||||
@ -70,7 +70,7 @@ class LexerTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public function provideTestLex() {
|
||||
public static function provideTestLex() {
|
||||
return [
|
||||
// tests PHP 8 T_NAME_* emulation
|
||||
[
|
||||
@ -97,7 +97,7 @@ class LexerTest extends \PHPUnit\Framework\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetTokens() {
|
||||
public function testGetTokens(): void {
|
||||
$code = '<?php "a";' . "\n" . '// foo' . "\n" . '// bar' . "\n\n" . '"b";';
|
||||
$expectedTokens = [
|
||||
new Token(T_OPEN_TAG, '<?php ', 1, 0),
|
||||
|
18
test/PhpParser/ModifiersTest.php
Normal file
18
test/PhpParser/ModifiersTest.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ModifiersTest extends TestCase {
|
||||
public function testToString() {
|
||||
$this->assertSame('public', Modifiers::toString(Modifiers::PUBLIC));
|
||||
}
|
||||
|
||||
public function testToStringInvalid() {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Unknown modifier 3');
|
||||
Modifiers::toString(Modifiers::PUBLIC | Modifiers::PROTECTED);
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ class NameContextTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestGetPossibleNames
|
||||
*/
|
||||
public function testGetPossibleNames($type, $name, $expectedPossibleNames) {
|
||||
public function testGetPossibleNames($type, $name, $expectedPossibleNames): void {
|
||||
$nameContext = new NameContext(new ErrorHandler\Throwing());
|
||||
$nameContext->startNamespace(new Name('NS'));
|
||||
$nameContext->addAlias(new Name('Foo'), 'Foo', Use_::TYPE_NORMAL);
|
||||
@ -32,7 +32,7 @@ class NameContextTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function provideTestGetPossibleNames() {
|
||||
public static function provideTestGetPossibleNames() {
|
||||
return [
|
||||
[Use_::TYPE_NORMAL, 'Test', ['\Test']],
|
||||
[Use_::TYPE_NORMAL, 'Test\Namespaced', ['\Test\Namespaced']],
|
||||
|
@ -11,14 +11,14 @@ class CallableLikeTest extends \PHPUnit\Framework\TestCase {
|
||||
/**
|
||||
* @dataProvider provideTestIsFirstClassCallable
|
||||
*/
|
||||
public function testIsFirstClassCallable(CallLike $node, bool $isFirstClassCallable) {
|
||||
public function testIsFirstClassCallable(CallLike $node, bool $isFirstClassCallable): void {
|
||||
$this->assertSame($isFirstClassCallable, $node->isFirstClassCallable());
|
||||
if (!$isFirstClassCallable) {
|
||||
$this->assertSame($node->getRawArgs(), $node->getArgs());
|
||||
}
|
||||
}
|
||||
|
||||
public function provideTestIsFirstClassCallable() {
|
||||
public static function provideTestIsFirstClassCallable() {
|
||||
$normalArgs = [new Arg(new Int_(1))];
|
||||
$callableArgs = [new VariadicPlaceholder()];
|
||||
return [
|
||||
|
@ -3,7 +3,12 @@
|
||||
namespace PhpParser\Node;
|
||||
|
||||
class IdentifierTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testToString() {
|
||||
public function testConstructorThrows(): void {
|
||||
self::expectException(\InvalidArgumentException::class);
|
||||
new Identifier('');
|
||||
}
|
||||
|
||||
public function testToString(): void {
|
||||
$identifier = new Identifier('Foo');
|
||||
|
||||
$this->assertSame('Foo', (string) $identifier);
|
||||
@ -12,12 +17,12 @@ class IdentifierTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
|
||||
/** @dataProvider provideTestIsSpecialClassName */
|
||||
public function testIsSpecialClassName($identifier, $expected) {
|
||||
public function testIsSpecialClassName($identifier, $expected): void {
|
||||
$identifier = new Identifier($identifier);
|
||||
$this->assertSame($expected, $identifier->isSpecialClassName());
|
||||
}
|
||||
|
||||
public function provideTestIsSpecialClassName() {
|
||||
public static function provideTestIsSpecialClassName() {
|
||||
return [
|
||||
['self', true],
|
||||
['PARENT', true],
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace PhpParser\Node;
|
||||
|
||||
class NameTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testConstruct() {
|
||||
public function testConstruct(): void {
|
||||
$name = new Name(['foo', 'bar']);
|
||||
$this->assertSame('foo\bar', $name->name);
|
||||
|
||||
@ -14,7 +14,7 @@ class NameTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame('foo\bar', $name->name);
|
||||
}
|
||||
|
||||
public function testGet() {
|
||||
public function testGet(): void {
|
||||
$name = new Name('foo');
|
||||
$this->assertSame('foo', $name->getFirst());
|
||||
$this->assertSame('foo', $name->getLast());
|
||||
@ -26,7 +26,7 @@ class NameTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame(['foo', 'bar'], $name->getParts());
|
||||
}
|
||||
|
||||
public function testToString() {
|
||||
public function testToString(): void {
|
||||
$name = new Name('Foo\Bar');
|
||||
|
||||
$this->assertSame('Foo\Bar', (string) $name);
|
||||
@ -34,7 +34,7 @@ class NameTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame('foo\bar', $name->toLowerString());
|
||||
}
|
||||
|
||||
public function testSlice() {
|
||||
public function testSlice(): void {
|
||||
$name = new Name('foo\bar\baz');
|
||||
$this->assertEquals(new Name('foo\bar\baz'), $name->slice(0));
|
||||
$this->assertEquals(new Name('bar\baz'), $name->slice(1));
|
||||
@ -50,37 +50,37 @@ class NameTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertNull($name->slice(-2, -2));
|
||||
}
|
||||
|
||||
public function testSliceOffsetTooLarge() {
|
||||
public function testSliceOffsetTooLarge(): void {
|
||||
$this->expectException(\OutOfBoundsException::class);
|
||||
$this->expectExceptionMessage('Offset 4 is out of bounds');
|
||||
(new Name('foo\bar\baz'))->slice(4);
|
||||
}
|
||||
|
||||
public function testSliceOffsetTooSmall() {
|
||||
public function testSliceOffsetTooSmall(): void {
|
||||
$this->expectException(\OutOfBoundsException::class);
|
||||
$this->expectExceptionMessage('Offset -4 is out of bounds');
|
||||
(new Name('foo\bar\baz'))->slice(-4);
|
||||
}
|
||||
|
||||
public function testSliceLengthTooLarge() {
|
||||
public function testSliceLengthTooLarge(): void {
|
||||
$this->expectException(\OutOfBoundsException::class);
|
||||
$this->expectExceptionMessage('Length 4 is out of bounds');
|
||||
(new Name('foo\bar\baz'))->slice(0, 4);
|
||||
}
|
||||
|
||||
public function testSliceLengthTooSmall() {
|
||||
public function testSliceLengthTooSmall(): void {
|
||||
$this->expectException(\OutOfBoundsException::class);
|
||||
$this->expectExceptionMessage('Length -4 is out of bounds');
|
||||
(new Name('foo\bar\baz'))->slice(0, -4);
|
||||
}
|
||||
|
||||
public function testSliceLengthTooLargeWithOffset() {
|
||||
public function testSliceLengthTooLargeWithOffset(): void {
|
||||
$this->expectException(\OutOfBoundsException::class);
|
||||
$this->expectExceptionMessage('Length 3 is out of bounds');
|
||||
(new Name('foo\bar\baz'))->slice(1, 3);
|
||||
}
|
||||
|
||||
public function testConcat() {
|
||||
public function testConcat(): void {
|
||||
$this->assertEquals(new Name('foo\bar\baz'), Name::concat('foo', 'bar\baz'));
|
||||
$this->assertEquals(
|
||||
new Name\FullyQualified('foo\bar'),
|
||||
@ -98,7 +98,7 @@ class NameTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertNull(Name::concat(null, null));
|
||||
}
|
||||
|
||||
public function testNameTypes() {
|
||||
public function testNameTypes(): void {
|
||||
$name = new Name('foo');
|
||||
$this->assertTrue($name->isUnqualified());
|
||||
$this->assertFalse($name->isQualified());
|
||||
@ -128,31 +128,31 @@ class NameTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertSame('namespace\foo', $name->toCodeString());
|
||||
}
|
||||
|
||||
public function testInvalidArg() {
|
||||
public function testInvalidArg(): void {
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Expected string, array of parts or Name instance');
|
||||
Name::concat('foo', new \stdClass());
|
||||
}
|
||||
|
||||
public function testInvalidEmptyString() {
|
||||
public function testInvalidEmptyString(): void {
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Name cannot be empty');
|
||||
new Name('');
|
||||
}
|
||||
|
||||
public function testInvalidEmptyArray() {
|
||||
public function testInvalidEmptyArray(): void {
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Name cannot be empty');
|
||||
new Name([]);
|
||||
}
|
||||
|
||||
/** @dataProvider provideTestIsSpecialClassName */
|
||||
public function testIsSpecialClassName($name, $expected) {
|
||||
public function testIsSpecialClassName($name, $expected): void {
|
||||
$name = new Name($name);
|
||||
$this->assertSame($expected, $name->isSpecialClassName());
|
||||
}
|
||||
|
||||
public function provideTestIsSpecialClassName() {
|
||||
public static function provideTestIsSpecialClassName() {
|
||||
return [
|
||||
['self', true],
|
||||
['PARENT', true],
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user