Compare commits

..

55 Commits

Author SHA1 Message Date
9a9981c347 Release PHP-Parser 4.3.0 2019-11-08 14:50:10 +01:00
2c42f64475 Update php-yacc dependency
This version has fixed error handling.
2019-11-08 14:46:41 +01:00
664c10121e Add support for union types
We definitely need to introduce a general "Type" abstraction in
the next major version.
2019-11-08 14:45:32 +01:00
5b1cd2e4f2 Fix phpdoc for Param\Builer::$type 2019-11-02 18:29:08 +01:00
b76bbc3c51 Release PHP-Parser 4.2.5 2019-10-25 20:33:07 +02:00
eacc5dbe19 Default to using php-yacc to generate the parsers 2019-10-19 15:24:38 +02:00
3226eb4086 Accept KMYACC env var in rebuildParsers.php 2019-10-19 12:28:45 +02:00
54c37f6b3b Remove -l flag from kmyacc invocation
For PHP this doesn't do anything anyway.
2019-10-19 11:14:42 +02:00
0a80b2d8ee Fix PHP 8 compatibility
This is a fix to preserve current behavior, but this should be
changed towards doing the same as PHP 8 does.
2019-10-19 10:49:37 +02:00
2f45e05042 Skip php-src test file 2019-10-19 10:44:01 +02:00
69c105dde1 Add .gitignore to .gitattributes 2019-10-19 10:26:01 +02:00
603203177e Optimize production build by ignoring dev files 2019-09-18 11:25:40 +02:00
97e59c7a16 Release PHP-Parser 4.2.4 2019-09-01 09:51:21 +02:00
006acba066 add getTraitUses() method to ClassLike 2019-08-31 09:26:51 +02:00
005bb1dba7 add getProperties() and getConstants() to ClassLike 2019-08-30 20:47:14 +02:00
40e7b67d69 gitignore: add phpunit test cache 2019-08-30 20:47:14 +02:00
5644a916bc Sync flexible heredoc emulation with label fix
This was recently fixed in PHP via
310708845f
so we should fix it here as well.

I'm not adding a dedicated test as it will fail until new PHP versions
are released. This is indirectly tested through the php-src tester.
2019-08-30 20:21:28 +02:00
e612609022 Release PHP-Parser 4.2.3 2019-08-12 22:17:41 +02:00
4fd36b9946 Fix compatibility with T_BAD_CHARACTER in PHP 7.4 2019-08-12 22:10:02 +02:00
a1f72690ef Fix harmless typos in documentation 2019-07-23 12:32:37 +02:00
2e2954ccdf Avoid notices in php 7.4 with hexdec/base_convert (#619)
This is made to avoid notices caused by
https://wiki.php.net/rfc/base_convert_improvements

(seen with `php -d error_reporting=E_ALL vendor/bin/phpunit`)
2019-07-14 10:56:13 +02:00
3f718ee2c3 [PHP 7.4] Add support for numeric literal separators (#615)
Implements RFC https://wiki.php.net/rfc/numeric_literal_separator.

Closes #614.
2019-06-30 12:13:28 +02:00
b9b45dd2bc Insert T_BAD_CHARACTER tokens for missing characters
The token stream should cover all characters in the original code,
insert a dummy token for missing illegal characters. We should
really be doing this in token_get_all() as well.
2019-06-30 11:43:48 +02:00
a4b43edb03 Fix one-character inconsistency 2019-06-30 09:25:26 +02:00
3cf61fdd26 Only ignore-platform-reqs on nightly 2019-06-23 15:11:05 +02:00
9484baf8f8 Make compatible with PhpUnit 8 2019-06-23 15:03:40 +02:00
aad0e2896f Remove token registration from TokenEmulator interface 2019-06-23 14:50:14 +02:00
624f71fa6f Resolve return type of arrow functions (#613) 2019-06-04 16:25:12 +02:00
1bd73cc04c Release PHP-Parser 4.2.2 2019-05-25 22:07:01 +02:00
94d93f27a5 Revert "Recover from error inside alternative array deref syntax"
This reverts commit 9d44edf85d.
2019-05-24 22:58:13 +02:00
a167aa2061 Optimize attribue checks in the lexer 2019-05-12 15:26:26 +02:00
993f29906b Avoid parent constructor call during node construction
Instead explicitly assign the attributes. This is a minor
performance improvement.
2019-05-12 14:55:21 +02:00
9d44edf85d Recover from error inside alternative array deref syntax
This is to improve error recovery for cases like #545.
2019-05-12 11:38:15 +02:00
aa97a9bb69 Add changelog entries 2019-05-11 23:08:31 +02:00
aa72c5d674 FPPP: Support inserting into empty lists 2019-05-11 22:49:32 +02:00
60d025a914 Fix attributes for zero-length nop nodes
Previously zero-length nop nodes used the lookahead start attributes
and current end attributes. This choice ends up being somewhat weird,
because the end attributes will be the at the last non-whitespace,
non-comment token, which might be quite far back. More problematically,
we may not have encountered any non-discarded token if we're at the
start of the file, in which case we will have no end attributes to
assign.

Change things to use a canonical "zero-length" node representation,
where the end position (token & file) will be exactly one before the
start position.

Fixes #589.
2019-05-11 20:01:25 +02:00
b2cecec6bc Remove bogus exprStmt mode test
We're always generating expression statements nowadays, this flag
hasn't existed for a long while now...
2019-05-11 18:51:37 +02:00
8012faea54 [PHP 7.4] Add array spread 2019-05-09 19:15:35 +02:00
f3b19c19ef [PHP 7.4] Add support for arrow functions (#602)
Per RFC https://wiki.php.net/rfc/arrow_functions_v2.
2019-05-09 14:17:28 +02:00
78d9985d11 Print messages to stderr in bin/php-parse and fix exit status
Close #605.
2019-04-28 22:06:06 +02:00
57b8673ea7 Use --ignore-platform-reqs on Travis
Allows testing on nightly, which is PHP 8.
2019-02-16 21:58:22 +01:00
5221f49a60 Release PHP-Parser 4.2.1 2019-02-16 21:54:15 +01:00
ae4e90d558 Add PHP 7.4 to Travis matrix 2019-02-16 21:52:44 +01:00
9de96821f7 Add support for ??= operator
Introduced in PHP 5.4, represented using an AssignOp\Coalesce node.
2019-02-09 11:16:26 +01:00
b7e6361536 updates via "rectorphp/rector" (#573)
- "global" -> remove unused "use" statements
- "phpunit" -> fix "@covers" comments
- "phpunit" -> replace "->will($this->returnValue()" with "->willReturn()"
- "UseTest.php" -> add missing namespace
- "composer.json" -> use "autoload-dev"
- remove -> "require_once" usage in the tests (use autoload-dev via composer.json)

-> most of the changes are done automatically by "https://github.com/rectorphp/rector"
2019-01-19 11:18:00 +01:00
594bcae1fc Release PHP-Parser 4.2.0 2019-01-12 17:31:37 +01:00
d5180f0d95 Change test to use correct node type
Class name is an Identifier, not a Name.
2019-01-12 17:31:37 +01:00
1f95f9215c Tweak type annotation to include null 2019-01-12 17:31:37 +01:00
6b9dd7afe7 Avoid redundant argument 2019-01-12 17:31:37 +01:00
ba092652fe Enhancement: Reference phpunit.xsd as installed with composer 2019-01-09 21:50:03 +01:00
ec0d834c5f Enhancement: Normalize composer.json 2019-01-09 21:49:38 +01:00
ea3657fc5f Bump version to 4.2.0-dev 2019-01-05 21:34:53 +01:00
af8c729603 Add PHP 7.4 typed properties support 2019-01-05 21:34:25 +01:00
0ef61b49bb add float/double/real KIND support to Cast\Double node 2019-01-05 19:23:00 +01:00
90ee36a7fc Actually remove assertion... 2018-12-26 14:19:18 +01:00
231 changed files with 5671 additions and 3255 deletions

8
.gitattributes vendored Normal file
View File

@ -0,0 +1,8 @@
/test export-ignore
/test_old export-ignore
/doc export-ignore
CHANGELOG.md export-ignore
.travis.yml export-ignore
phpunit.xml.dist export-ignore
UPGRADE-*.md export-ignore
.gitignore export-ignore

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ vendor/
composer.lock composer.lock
grammar/kmyacc.exe grammar/kmyacc.exe
grammar/y.output grammar/y.output
.phpunit.result.cache

View File

@ -11,11 +11,17 @@ php:
- 7.1 - 7.1
- 7.2 - 7.2
- 7.3 - 7.3
- 7.4snapshot
- nightly - nightly
install: install:
- if [ $TRAVIS_PHP_VERSION = '7.0' ]; then composer require satooshi/php-coveralls '~1.0'; fi - if [ $TRAVIS_PHP_VERSION = '7.0' ]; then composer require satooshi/php-coveralls '~1.0'; fi
- composer install --prefer-dist - |
if [ $TRAVIS_PHP_VERSION = 'nightly' ]; then
composer install --prefer-dist --ignore-platform-reqs;
else
composer install --prefer-dist;
fi
matrix: matrix:
allow_failures: allow_failures:

View File

@ -1,15 +1,94 @@
Version 4.1.2-dev Version 4.3.1-dev
----------------- -----------------
Nothing yet. Nothing yet.
Version 4.3.0 (2019-11-08)
--------------------------
### Added
* [PHP 8.0] Added support for union types using a new `UnionType` node.
Version 4.2.5 (2019-10-25)
--------------------------
### Changed
* Tests and documentation are no longer included in source archives. They can still be accessed
by cloning the repository.
* php-yacc is now used to generate the parser. This has no impact on users of the library.
Version 4.2.4 (2019-09-01)
--------------------------
### Added
* Added getProperties(), getConstants() and getTraitUses() to ClassLike. (#629, #630)
### Fixed
* Fixed flexible heredoc emulation to check for digits after the end label. This synchronizes
behavior with the upcoming PHP 7.3.10 release.
Version 4.2.3 (2019-08-12)
--------------------------
### Added
* [PHP 7.4] Add support for numeric literal separators. (#615)
### Fixed
* Fixed resolution of return types for arrow functions. (#613)
* Fixed compatibility with PHP 7.4.
Version 4.2.2 (2019-05-25)
--------------------------
### Added
* [PHP 7.4] Add support for arrow functions using a new `Expr\ArrowFunction` node. (#602)
* [PHP 7.4] Add support for array spreads, using a new `unpack` subnode on `ArrayItem`. (#609)
* Added support for inserting into empty list nodes in the formatting preserving pretty printer.
### Changed
* `php-parse` will now print messages to stderr, so that stdout only contains the actual result of
the operation (such as a JSON dump). (#605)
### Fixed
* Fixed attribute assignment for zero-length nop statements, and a related assertion failure in
the formatting-preserving pretty printer. (#589)
Version 4.2.1 (2019-02-16)
--------------------------
### Added
* [PHP 7.4] Add support for `??=` operator through a new `AssignOp\Coalesce` node. (#575)
Version 4.2.0 (2019-01-12)
--------------------------
### Added
* [PHP 7.4] Add support for typed properties through a new `type` subnode of `Stmt\Property`.
Additionally `Builder\Property` now has a `setType()` method. (#567)
* Add `kind` attribute to `Cast\Double_`, which allows to distinguish between `(float)`,
`(double)` and `(real)`. The form of the cast will be preserved by the pretty printer. (#565)
### Fixed
* Remove assertion when pretty printing anonymous class with a name (#554).
Version 4.1.1 (2018-12-26) Version 4.1.1 (2018-12-26)
-------------------------- --------------------------
### Fixed ### Fixed
* Fix "undefined offset" notice when parsing specific malformed code (#551). * Fix "undefined offset" notice when parsing specific malformed code (#551).
* Remove assertion when pretty printing anonymous class with a name (#554).
### Added ### Added

View File

@ -3,10 +3,10 @@ PHP Parser
[![Build Status](https://travis-ci.org/nikic/PHP-Parser.svg?branch=master)](https://travis-ci.org/nikic/PHP-Parser) [![Coverage Status](https://coveralls.io/repos/github/nikic/PHP-Parser/badge.svg?branch=master)](https://coveralls.io/github/nikic/PHP-Parser?branch=master) [![Build Status](https://travis-ci.org/nikic/PHP-Parser.svg?branch=master)](https://travis-ci.org/nikic/PHP-Parser) [![Coverage Status](https://coveralls.io/repos/github/nikic/PHP-Parser/badge.svg?branch=master)](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
This is a PHP 5.2 to PHP 7.3 parser written in PHP. Its purpose is to simplify static code analysis and This is a PHP 5.2 to PHP 7.4 parser written in PHP. Its purpose is to simplify static code analysis and
manipulation. manipulation.
[**Documentation for version 4.x**][doc_master] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 7.3). [**Documentation for version 4.x**][doc_master] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 7.4).
[Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2). [Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).

View File

@ -45,14 +45,15 @@ $traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
foreach ($files as $file) { foreach ($files as $file) {
if (strpos($file, '<?php') === 0) { if (strpos($file, '<?php') === 0) {
$code = $file; $code = $file;
echo "====> Code $code\n"; fwrite(STDERR, "====> Code $code\n");
} else { } else {
if (!file_exists($file)) { if (!file_exists($file)) {
die("File $file does not exist.\n"); fwrite(STDERR, "File $file does not exist.\n");
exit(1);
} }
$code = file_get_contents($file); $code = file_get_contents($file);
echo "====> File $file:\n"; fwrite(STDERR, "====> File $file:\n");
} }
if ($attributes['with-recovery']) { if ($attributes['with-recovery']) {
@ -60,7 +61,7 @@ foreach ($files as $file) {
$stmts = $parser->parse($code, $errorHandler); $stmts = $parser->parse($code, $errorHandler);
foreach ($errorHandler->getErrors() as $error) { foreach ($errorHandler->getErrors() as $error) {
$message = formatErrorMessage($error, $code, $attributes['with-column-info']); $message = formatErrorMessage($error, $code, $attributes['with-column-info']);
echo $message . "\n"; fwrite(STDERR, $message . "\n");
} }
if (null === $stmts) { if (null === $stmts) {
continue; continue;
@ -70,25 +71,26 @@ foreach ($files as $file) {
$stmts = $parser->parse($code); $stmts = $parser->parse($code);
} catch (PhpParser\Error $error) { } catch (PhpParser\Error $error) {
$message = formatErrorMessage($error, $code, $attributes['with-column-info']); $message = formatErrorMessage($error, $code, $attributes['with-column-info']);
die($message . "\n"); fwrite(STDERR, $message . "\n");
exit(1);
} }
} }
foreach ($operations as $operation) { foreach ($operations as $operation) {
if ('dump' === $operation) { if ('dump' === $operation) {
echo "==> Node dump:\n"; fwrite(STDERR, "==> Node dump:\n");
echo $dumper->dump($stmts, $code), "\n"; echo $dumper->dump($stmts, $code), "\n";
} elseif ('pretty-print' === $operation) { } elseif ('pretty-print' === $operation) {
echo "==> Pretty print:\n"; fwrite(STDERR, "==> Pretty print:\n");
echo $prettyPrinter->prettyPrintFile($stmts), "\n"; echo $prettyPrinter->prettyPrintFile($stmts), "\n";
} elseif ('json-dump' === $operation) { } elseif ('json-dump' === $operation) {
echo "==> JSON dump:\n"; fwrite(STDERR, "==> JSON dump:\n");
echo json_encode($stmts, JSON_PRETTY_PRINT), "\n"; echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";
} elseif ('var-dump' === $operation) { } elseif ('var-dump' === $operation) {
echo "==> var_dump():\n"; fwrite(STDERR, "==> var_dump():\n");
var_dump($stmts); var_dump($stmts);
} elseif ('resolve-names' === $operation) { } elseif ('resolve-names' === $operation) {
echo "==> Resolved names.\n"; fwrite(STDERR, "==> Resolved names.\n");
$stmts = $traverser->traverse($stmts); $stmts = $traverser->traverse($stmts);
} }
} }
@ -104,9 +106,9 @@ function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) {
function showHelp($error = '') { function showHelp($error = '') {
if ($error) { if ($error) {
echo $error . "\n\n"; fwrite(STDERR, $error . "\n\n");
} }
die(<<<OUTPUT fwrite($error ? STDERR : STDOUT, <<<OUTPUT
Usage: php-parse [operations] file1.php [file2.php ...] Usage: php-parse [operations] file1.php [file2.php ...]
or: php-parse [operations] "<?php code" or: php-parse [operations] "<?php code"
Turn PHP source code into an abstract syntax tree. Turn PHP source code into an abstract syntax tree.
@ -131,6 +133,7 @@ Example:
OUTPUT OUTPUT
); );
exit($error ? 1 : 0);
} }
function parseArgs($args) { function parseArgs($args) {

View File

@ -1,8 +1,11 @@
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"description": "A PHP parser written in PHP",
"keywords": ["php", "parser"],
"type": "library", "type": "library",
"description": "A PHP parser written in PHP",
"keywords": [
"php",
"parser"
],
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"authors": [ "authors": [
{ {
@ -14,17 +17,25 @@
"ext-tokenizer": "*" "ext-tokenizer": "*"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^6.5 || ^7.0" "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0",
"ircmaxell/php-yacc": "0.0.5"
},
"extra": {
"branch-alias": {
"dev-master": "4.3-dev"
}
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"PhpParser\\": "lib/PhpParser" "PhpParser\\": "lib/PhpParser"
} }
}, },
"bin": ["bin/php-parse"], "autoload-dev": {
"extra": { "psr-4": {
"branch-alias": { "PhpParser\\": "test/PhpParser/"
"dev-master": "4.1-dev"
} }
} },
"bin": [
"bin/php-parse"
]
} }

View File

@ -1,7 +1,7 @@
Introduction Introduction
============ ============
This project is a PHP 5.2 to PHP 7.3 parser **written in PHP itself**. This project is a PHP 5.2 to PHP 7.4 parser **written in PHP itself**.
What is this for? What is this for?
----------------- -----------------
@ -26,11 +26,11 @@ programmatic PHP code analysis are incidentally PHP developers, not C developers
What can it parse? What can it parse?
------------------ ------------------
The parser supports parsing PHP 5.2-7.3. The parser supports parsing PHP 5.2-7.4.
As the parser is based on the tokens returned by `token_get_all` (which is only able to lex the PHP As the parser is based on the tokens returned by `token_get_all` (which is only able to lex the PHP
version it runs on), additionally a wrapper for emulating tokens from newer versions is provided. version it runs on), additionally a wrapper for emulating tokens from newer versions is provided.
This allows to parse PHP 7.3 source code running on PHP 7.0, for example. This emulation is somewhat This allows to parse PHP 7.4 source code running on PHP 7.0, for example. This emulation is somewhat
hacky and not perfect, but it should work well on any sane code. hacky and not perfect, but it should work well on any sane code.
What output does it produce? What output does it produce?
@ -56,7 +56,7 @@ array(
``` ```
This matches the structure of the code: An echo statement, which takes two strings as expressions, This matches the structure of the code: An echo statement, which takes two strings as expressions,
with the values `Hi` and `World!`. with the values `Hi` and `World`.
You can also see that the AST does not contain any whitespace information (but most comments are saved). You can also see that the AST does not contain any whitespace information (but most comments are saved).
So using it for formatting analysis is not possible. So using it for formatting analysis is not possible.

View File

@ -77,7 +77,7 @@ A parser instance can be reused to parse multiple files.
Node dumping Node dumping
------------ ------------
To dump the abstact syntax tree in human readable form, a `NodeDumper` can be used: To dump the abstract syntax tree in human readable form, a `NodeDumper` can be used:
```php ```php
<?php <?php

View File

@ -48,7 +48,7 @@ $node = $factory->namespace('Name\Space')
->makePublic() ->makePublic()
->makeAbstract() // ->makeFinal() ->makeAbstract() // ->makeFinal()
->setReturnType('bool') // ->makeReturnByRef() ->setReturnType('bool') // ->makeReturnByRef()
->addParam($factory->param('someParam')->setTypeHint('SomeClass')) ->addParam($factory->param('someParam')->setType('SomeClass'))
->setDocComment('/** ->setDocComment('/**
* This method does something. * This method does something.
* *

View File

@ -312,7 +312,7 @@ $extendingClasses = $nodeFinder->find($stmts, function(Node $node) {
&& $node->extends !== null; && $node->extends !== null;
}); });
// Find first class occuring in the AST. Returns null if no class exists. // Find first class occurring in the AST. Returns null if no class exists.
$class = $nodeFinder->findFirstInstanceOf($stmts, Node\Stmt\Class_::class); $class = $nodeFinder->findFirstInstanceOf($stmts, Node\Stmt\Class_::class);
// Find first class that has name $name // Find first class that has name $name

View File

@ -21,8 +21,10 @@ applied to it:
Building the parser Building the parser
=================== ===================
In order to rebuild the parser, you need [moriyoshi's fork of kmyacc](https://github.com/moriyoshi/kmyacc-forked). Run `php grammar/rebuildParsers.php` to rebuild the parsers. Additional options:
After you compiled/installed it, run the `rebuildParsers.php` script.
By default only the `Parser.php` is built. If you want to additionally emit debug symbols and create `y.output`, run the * The `KMYACC` environment variable can be used to specify an alternative `kmyacc` binary.
script with `--debug`. If you want to retain the preprocessed grammar pass `--keep-tmp-grammar`. By default the `phpyacc` dev dependency will be used. To use the original `kmyacc`, you
need to compile [moriyoshi's fork](https://github.com/moriyoshi/kmyacc-forked).
* The `--debug` option enables emission of debug symbols and creates the `y.output` file.
* The `--keep-tmp-grammar` option preserves the preprocessed grammar file.

View File

@ -16,7 +16,7 @@ top_statement_list_ex:
top_statement_list: top_statement_list:
top_statement_list_ex top_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes); { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; } if ($nop !== null) { $1[] = $nop; } $$ = $1; }
; ;
@ -27,7 +27,7 @@ reserved_non_modifiers:
| T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO | T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN
; ;
semi_reserved: semi_reserved:
@ -160,7 +160,7 @@ inner_statement_list_ex:
inner_statement_list: inner_statement_list:
inner_statement_list_ex inner_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes); { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; } if ($nop !== null) { $1[] = $nop; } $$ = $1; }
; ;
@ -461,7 +461,7 @@ class_statement_list_ex:
class_statement_list: class_statement_list:
class_statement_list_ex class_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes); { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; } if ($nop !== null) { $1[] = $nop; } $$ = $1; }
; ;
@ -579,6 +579,7 @@ expr:
| variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; } | variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
| variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; } | variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
| variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; } | variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; }
| variable T_COALESCE_EQUAL expr { $$ = Expr\AssignOp\Coalesce [$1, $3]; }
| variable T_INC { $$ = Expr\PostInc[$1]; } | variable T_INC { $$ = Expr\PostInc[$1]; }
| T_INC variable { $$ = Expr\PreInc [$2]; } | T_INC variable { $$ = Expr\PreInc [$2]; }
| variable T_DEC { $$ = Expr\PostDec[$1]; } | variable T_DEC { $$ = Expr\PostDec[$1]; }
@ -628,7 +629,10 @@ expr:
| T_REQUIRE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; } | T_REQUIRE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; }
| T_REQUIRE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; } | T_REQUIRE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; }
| T_INT_CAST expr { $$ = Expr\Cast\Int_ [$2]; } | T_INT_CAST expr { $$ = Expr\Cast\Int_ [$2]; }
| T_DOUBLE_CAST expr { $$ = Expr\Cast\Double [$2]; } | T_DOUBLE_CAST expr
{ $attrs = attributes();
$attrs['kind'] = $this->getFloatCastKind($1);
$$ = new Expr\Cast\Double($2, $attrs); }
| T_STRING_CAST expr { $$ = Expr\Cast\String_ [$2]; } | T_STRING_CAST expr { $$ = Expr\Cast\String_ [$2]; }
| T_ARRAY_CAST expr { $$ = Expr\Cast\Array_ [$2]; } | T_ARRAY_CAST expr { $$ = Expr\Cast\Array_ [$2]; }
| T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; } | T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; }
@ -984,6 +988,7 @@ array_pair:
| expr { $$ = Expr\ArrayItem[$1, null, false]; } | expr { $$ = Expr\ArrayItem[$1, null, false]; }
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; } | expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; } | '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
; ;
encaps_list: encaps_list:

View File

@ -16,7 +16,7 @@ top_statement_list_ex:
top_statement_list: top_statement_list:
top_statement_list_ex top_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes); { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; } if ($nop !== null) { $1[] = $nop; } $$ = $1; }
; ;
@ -27,7 +27,7 @@ reserved_non_modifiers:
| T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO | T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN
; ;
semi_reserved: semi_reserved:
@ -196,7 +196,7 @@ inner_statement_list_ex:
inner_statement_list: inner_statement_list:
inner_statement_list_ex inner_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes); { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; } if ($nop !== null) { $1[] = $nop; } $$ = $1; }
; ;
@ -450,17 +450,18 @@ non_empty_parameter_list:
; ;
parameter: parameter:
optional_param_type optional_ref optional_ellipsis plain_variable optional_type optional_ref optional_ellipsis plain_variable
{ $$ = Node\Param[$4, null, $1, $2, $3]; $this->checkParam($$); } { $$ = Node\Param[$4, null, $1, $2, $3]; $this->checkParam($$); }
| optional_param_type optional_ref optional_ellipsis plain_variable '=' expr | optional_type optional_ref optional_ellipsis plain_variable '=' expr
{ $$ = Node\Param[$4, $6, $1, $2, $3]; $this->checkParam($$); } { $$ = Node\Param[$4, $6, $1, $2, $3]; $this->checkParam($$); }
| optional_param_type optional_ref optional_ellipsis error | optional_type optional_ref optional_ellipsis error
{ $$ = Node\Param[Expr\Error[], null, $1, $2, $3]; } { $$ = Node\Param[Expr\Error[], null, $1, $2, $3]; }
; ;
type_expr: type_expr:
type { $$ = $1; } type { $$ = $1; }
| '?' type { $$ = Node\NullableType[$2]; } | '?' type { $$ = Node\NullableType[$2]; }
| union_type { $$ = Node\UnionType[$1]; }
; ;
type: type:
@ -469,7 +470,12 @@ type:
| T_CALLABLE { $$ = Node\Identifier['callable']; } | T_CALLABLE { $$ = Node\Identifier['callable']; }
; ;
optional_param_type: union_type:
type '|' type { init($1, $3); }
| union_type '|' type { push($1, $3); }
;
optional_type:
/* empty */ { $$ = null; } /* empty */ { $$ = null; }
| type_expr { $$ = $1; } | type_expr { $$ = $1; }
; ;
@ -530,13 +536,14 @@ class_statement_list_ex:
class_statement_list: class_statement_list:
class_statement_list_ex class_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes); { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; } if ($nop !== null) { $1[] = $nop; } $$ = $1; }
; ;
class_statement: class_statement:
variable_modifiers property_declaration_list ';' variable_modifiers optional_type property_declaration_list ';'
{ $$ = Stmt\Property[$1, $2]; $this->checkProperty($$, #1); } { $attrs = attributes();
$$ = new Stmt\Property($1, $3, $attrs, $2); $this->checkProperty($$, #1); }
| method_modifiers T_CONST class_const_list ';' | method_modifiers T_CONST class_const_list ';'
{ $$ = Stmt\ClassConst[$3, $1]; $this->checkClassConst($$, #1); } { $$ = Stmt\ClassConst[$3, $1]; $this->checkClassConst($$, #1); }
| method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body | method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body
@ -659,6 +666,7 @@ expr:
| variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; } | variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
| variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; } | variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
| variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; } | variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; }
| variable T_COALESCE_EQUAL expr { $$ = Expr\AssignOp\Coalesce [$1, $3]; }
| variable T_INC { $$ = Expr\PostInc[$1]; } | variable T_INC { $$ = Expr\PostInc[$1]; }
| T_INC variable { $$ = Expr\PreInc [$2]; } | T_INC variable { $$ = Expr\PreInc [$2]; }
| variable T_DEC { $$ = Expr\PostDec[$1]; } | variable T_DEC { $$ = Expr\PostDec[$1]; }
@ -706,7 +714,10 @@ expr:
| T_REQUIRE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; } | T_REQUIRE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; }
| T_REQUIRE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; } | T_REQUIRE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; }
| T_INT_CAST expr { $$ = Expr\Cast\Int_ [$2]; } | T_INT_CAST expr { $$ = Expr\Cast\Int_ [$2]; }
| T_DOUBLE_CAST expr { $$ = Expr\Cast\Double [$2]; } | T_DOUBLE_CAST expr
{ $attrs = attributes();
$attrs['kind'] = $this->getFloatCastKind($1);
$$ = new Expr\Cast\Double($2, $attrs); }
| T_STRING_CAST expr { $$ = Expr\Cast\String_ [$2]; } | T_STRING_CAST expr { $$ = Expr\Cast\String_ [$2]; }
| T_ARRAY_CAST expr { $$ = Expr\Cast\Array_ [$2]; } | T_ARRAY_CAST expr { $$ = Expr\Cast\Array_ [$2]; }
| T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; } | T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; }
@ -724,6 +735,12 @@ expr:
| T_YIELD expr { $$ = Expr\Yield_[$2, null]; } | T_YIELD expr { $$ = Expr\Yield_[$2, null]; }
| T_YIELD expr T_DOUBLE_ARROW expr { $$ = Expr\Yield_[$4, $2]; } | T_YIELD expr T_DOUBLE_ARROW expr { $$ = Expr\Yield_[$4, $2]; }
| T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; } | T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; }
| T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
{ $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8]]; }
| T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
{ $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9]]; }
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type | T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
block_or_error block_or_error
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8]]; } { $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8]]; }
@ -972,6 +989,7 @@ array_pair:
| expr { $$ = Expr\ArrayItem[$1, null, false]; } | expr { $$ = Expr\ArrayItem[$1, null, false]; }
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; } | expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; } | '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
| /* empty */ { $$ = null; } | /* empty */ { $$ = null; }
; ;

View File

@ -13,10 +13,10 @@ $tmpResultFile = __DIR__ . '/tmp_parser.php';
$resultDir = __DIR__ . '/../lib/PhpParser/Parser'; $resultDir = __DIR__ . '/../lib/PhpParser/Parser';
$tokensResultsFile = $resultDir . '/Tokens.php'; $tokensResultsFile = $resultDir . '/Tokens.php';
// check for kmyacc.exe binary in this directory, otherwise fall back to global name $kmyacc = getenv('KMYACC');
$kmyacc = __DIR__ . '/kmyacc.exe'; if (!$kmyacc) {
if (!file_exists($kmyacc)) { // Use phpyacc from dev dependencies by default.
$kmyacc = 'kmyacc'; $kmyacc = PHP_BINARY . ' ' . __DIR__ . '/../vendor/bin/phpyacc';
} }
$options = array_flip($argv); $options = array_flip($argv);
@ -59,8 +59,7 @@ foreach ($grammarFileToName as $grammarFile => $name) {
$additionalArgs = $optionDebug ? '-t -v' : ''; $additionalArgs = $optionDebug ? '-t -v' : '';
echo "Building $name parser.\n"; echo "Building $name parser.\n";
$output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile -p $name $tmpGrammarFile 2>&1")); $output = execCmd("$kmyacc $additionalArgs -m $skeletonFile -p $name $tmpGrammarFile");
echo "Output: \"$output\"\n";
$resultCode = file_get_contents($tmpResultFile); $resultCode = file_get_contents($tmpResultFile);
$resultCode = removeTrailingWhitespace($resultCode); $resultCode = removeTrailingWhitespace($resultCode);
@ -70,8 +69,7 @@ foreach ($grammarFileToName as $grammarFile => $name) {
unlink($tmpResultFile); unlink($tmpResultFile);
echo "Building token definition.\n"; echo "Building token definition.\n";
$output = trim(shell_exec("$kmyacc -l -m $tokensTemplate $tmpGrammarFile 2>&1")); $output = execCmd("$kmyacc -m $tokensTemplate $tmpGrammarFile");
assert($output === '');
rename($tmpResultFile, $tokensResultsFile); rename($tmpResultFile, $tokensResultsFile);
if (!$optionKeepTmpGrammar) { if (!$optionKeepTmpGrammar) {
@ -175,6 +173,15 @@ function resolveMacros($code) {
. ' else { ' . $args[0] . ' = null; }'; . ' else { ' . $args[0] . ' = null; }';
} }
if ('makeZeroLengthNop' == $name) {
assertArgs(2, $args, $name);
return '$startAttributes = ' . $args[1] . ';'
. ' if (isset($startAttributes[\'comments\']))'
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); }'
. ' else { ' . $args[0] . ' = null; }';
}
if ('strKind' == $name) { if ('strKind' == $name) {
assertArgs(1, $args, $name); assertArgs(1, $args, $name);
@ -222,6 +229,15 @@ function ensureDirExists($dir) {
} }
} }
function execCmd($cmd) {
$output = trim(shell_exec("$cmd 2>&1"));
if ($output !== "") {
echo "> " . $cmd . "\n";
echo $output;
}
return $output;
}
////////////////////////////// //////////////////////////////
/// Regex helper functions /// /// Regex helper functions ///
////////////////////////////// //////////////////////////////

View File

@ -10,7 +10,7 @@
%right T_YIELD %right T_YIELD
%right T_DOUBLE_ARROW %right T_DOUBLE_ARROW
%right T_YIELD_FROM %right T_YIELD_FROM
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL %left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL T_COALESCE_EQUAL
%left '?' ':' %left '?' ':'
%right T_COALESCE %right T_COALESCE
%left T_BOOLEAN_OR %left T_BOOLEAN_OR
@ -64,6 +64,7 @@
%token T_CONTINUE %token T_CONTINUE
%token T_GOTO %token T_GOTO
%token T_FUNCTION %token T_FUNCTION
%token T_FN
%token T_CONST %token T_CONST
%token T_RETURN %token T_RETURN
%token T_TRY %token T_TRY

View File

@ -12,7 +12,7 @@ class Param implements PhpParser\Builder
protected $default = null; protected $default = null;
/** @var string|Node\Name|Node\NullableType|null */ /** @var Node\Identifier|Node\Name|Node\NullableType|null */
protected $type = null; protected $type = null;
protected $byRef = false; protected $byRef = false;

View File

@ -4,6 +4,9 @@ namespace PhpParser\Builder;
use PhpParser; use PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Property implements PhpParser\Builder class Property implements PhpParser\Builder
@ -14,6 +17,9 @@ class Property implements PhpParser\Builder
protected $default = null; protected $default = null;
protected $attributes = []; protected $attributes = [];
/** @var null|Identifier|Name|NullableType */
protected $type;
/** /**
* Creates a property builder. * Creates a property builder.
* *
@ -95,6 +101,19 @@ class Property implements PhpParser\Builder
return $this; return $this;
} }
/**
* Sets the property type for PHP 7.4+.
*
* @param string|Name|NullableType|Identifier $type
*
* @return $this
*/
public function setType($type) {
$this->type = BuilderHelpers::normalizeType($type);
return $this;
}
/** /**
* Returns the built class node. * Returns the built class node.
* *
@ -106,7 +125,8 @@ class Property implements PhpParser\Builder
[ [
new Stmt\PropertyProperty($this->name, $this->default) new Stmt\PropertyProperty($this->name, $this->default)
], ],
$this->attributes $this->attributes,
$this->type
); );
} }
} }

View File

@ -8,7 +8,6 @@ use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Identifier; use PhpParser\Node\Identifier;
use PhpParser\Node\Name; use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_; use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Use_; use PhpParser\Node\Stmt\Use_;
class BuilderFactory class BuilderFactory
@ -77,7 +76,7 @@ class BuilderFactory
* @return Builder\TraitUseAdaptation The create trait use adaptation builder * @return Builder\TraitUseAdaptation The create trait use adaptation builder
*/ */
public function traitUseAdaptation($trait, $method = null) : Builder\TraitUseAdaptation { public function traitUseAdaptation($trait, $method = null) : Builder\TraitUseAdaptation {
if (is_null($method)) { if ($method === null) {
$method = $trait; $method = $trait;
$trait = null; $trait = null;
} }

View File

@ -41,7 +41,6 @@ class PrintableNewAnonClassNode extends Expr
assert($class instanceof Node\Stmt\Class_); assert($class instanceof Node\Stmt\Class_);
// We don't assert that $class->name is null here, to allow consumers to assign unique names // We don't assert that $class->name is null here, to allow consumers to assign unique names
// to anonymous classes for their own purposes. We simplify ignore the name here. // to anonymous classes for their own purposes. We simplify ignore the name here.
assert($class->name === null);
return new self( return new self(
$newNode->args, $class->extends, $class->implements, $newNode->args, $class->extends, $class->implements,
$class->stmts, $newNode->getAttributes() $class->stmts, $newNode->getAttributes()

View File

@ -16,7 +16,13 @@ class Lexer
protected $tokenMap; protected $tokenMap;
protected $dropTokens; protected $dropTokens;
protected $usedAttributes; private $attributeStartLineUsed;
private $attributeEndLineUsed;
private $attributeStartTokenPosUsed;
private $attributeEndTokenPosUsed;
private $attributeStartFilePosUsed;
private $attributeEndFilePosUsed;
private $attributeCommentsUsed;
/** /**
* Creates a Lexer. * Creates a Lexer.
@ -31,18 +37,28 @@ class Lexer
// map from internal tokens to PhpParser tokens // map from internal tokens to PhpParser tokens
$this->tokenMap = $this->createTokenMap(); $this->tokenMap = $this->createTokenMap();
// Compatibility define for PHP < 7.4
if (!defined('T_BAD_CHARACTER')) {
\define('T_BAD_CHARACTER', -1);
}
// map of tokens to drop while lexing (the map is only used for isset lookup, // map of tokens to drop while lexing (the map is only used for isset lookup,
// that's why the value is simply set to 1; the value is never actually used.) // that's why the value is simply set to 1; the value is never actually used.)
$this->dropTokens = array_fill_keys( $this->dropTokens = array_fill_keys(
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT], 1 [\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1
); );
// the usedAttributes member is a map of the used attribute names to a dummy $defaultAttributes = ['comments', 'startLine', 'endLine'];
// value (here "true") $usedAttributes = array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, true);
$options += [
'usedAttributes' => ['comments', 'startLine', 'endLine'], // Create individual boolean properties to make these checks faster.
]; $this->attributeStartLineUsed = isset($usedAttributes['startLine']);
$this->usedAttributes = array_fill_keys($options['usedAttributes'], true); $this->attributeEndLineUsed = isset($usedAttributes['endLine']);
$this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']);
$this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']);
$this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']);
$this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']);
$this->attributeCommentsUsed = isset($usedAttributes['comments']);
} }
/** /**
@ -81,13 +97,9 @@ class Lexer
} }
private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) { private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) {
$tokens = [];
for ($i = $start; $i < $end; $i++) { for ($i = $start; $i < $end; $i++) {
$chr = $this->code[$i]; $chr = $this->code[$i];
if ($chr === 'b' || $chr === 'B') {
// HHVM does not treat b" tokens correctly, so ignore these
continue;
}
if ($chr === "\0") { if ($chr === "\0") {
// PHP cuts error message after null byte, so need special case // PHP cuts error message after null byte, so need special case
$errorMsg = 'Unexpected null byte'; $errorMsg = 'Unexpected null byte';
@ -97,6 +109,7 @@ class Lexer
); );
} }
$tokens[] = [\T_BAD_CHARACTER, $chr, $line];
$errorHandler->handleError(new Error($errorMsg, [ $errorHandler->handleError(new Error($errorMsg, [
'startLine' => $line, 'startLine' => $line,
'endLine' => $line, 'endLine' => $line,
@ -104,6 +117,7 @@ class Lexer
'endFilePos' => $i, 'endFilePos' => $i,
])); ]));
} }
return $tokens;
} }
/** /**
@ -129,6 +143,16 @@ class Lexer
return true; return true;
} }
if (PHP_VERSION_ID >= 80000) {
// PHP 8 converts the "bad character" case into a parse error, rather than treating
// it as a lexing warning. To preserve previous behavior, we need to assume that an
// error occurred.
// TODO: We should handle this the same way as PHP 8: Only generate T_BAD_CHARACTER
// token here (for older PHP versions) and leave generationg of the actual parse error
// to the parser. This will also save the full token scan on PHP 8 here.
return true;
}
return null !== error_get_last(); return null !== error_get_last();
} }
@ -144,16 +168,29 @@ class Lexer
$filePos = 0; $filePos = 0;
$line = 1; $line = 1;
foreach ($this->tokens as $token) { $numTokens = \count($this->tokens);
for ($i = 0; $i < $numTokens; $i++) {
$token = $this->tokens[$i];
// Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token.
// In this case we only need to emit an error.
if ($token[0] === \T_BAD_CHARACTER) {
$this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler);
}
$tokenValue = \is_string($token) ? $token : $token[1]; $tokenValue = \is_string($token) ? $token : $token[1];
$tokenLen = \strlen($tokenValue); $tokenLen = \strlen($tokenValue);
if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) { if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) {
// Something is missing, must be an invalid character // Something is missing, must be an invalid character
$nextFilePos = strpos($this->code, $tokenValue, $filePos); $nextFilePos = strpos($this->code, $tokenValue, $filePos);
$this->handleInvalidCharacterRange( $badCharTokens = $this->handleInvalidCharacterRange(
$filePos, $nextFilePos, $line, $errorHandler); $filePos, $nextFilePos, $line, $errorHandler);
$filePos = (int) $nextFilePos; $filePos = (int) $nextFilePos;
array_splice($this->tokens, $i, 0, $badCharTokens);
$numTokens += \count($badCharTokens);
$i += \count($badCharTokens);
} }
$filePos += $tokenLen; $filePos += $tokenLen;
@ -176,8 +213,9 @@ class Lexer
$this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line]; $this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line];
} else { } else {
// Invalid characters at the end of the input // Invalid characters at the end of the input
$this->handleInvalidCharacterRange( $badCharTokens = $this->handleInvalidCharacterRange(
$filePos, \strlen($this->code), $line, $errorHandler); $filePos, \strlen($this->code), $line, $errorHandler);
$this->tokens = array_merge($this->tokens, $badCharTokens);
} }
return; return;
} }
@ -230,13 +268,13 @@ class Lexer
$token = "\0"; $token = "\0";
} }
if (isset($this->usedAttributes['startLine'])) { if ($this->attributeStartLineUsed) {
$startAttributes['startLine'] = $this->line; $startAttributes['startLine'] = $this->line;
} }
if (isset($this->usedAttributes['startTokenPos'])) { if ($this->attributeStartTokenPosUsed) {
$startAttributes['startTokenPos'] = $this->pos; $startAttributes['startTokenPos'] = $this->pos;
} }
if (isset($this->usedAttributes['startFilePos'])) { if ($this->attributeStartFilePosUsed) {
$startAttributes['startFilePos'] = $this->filePos; $startAttributes['startFilePos'] = $this->filePos;
} }
@ -263,7 +301,7 @@ class Lexer
$this->filePos += \strlen($value); $this->filePos += \strlen($value);
} else { } else {
if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) { if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
if (isset($this->usedAttributes['comments'])) { if ($this->attributeCommentsUsed) {
$comment = \T_DOC_COMMENT === $token[0] $comment = \T_DOC_COMMENT === $token[0]
? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos) ? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos)
: new Comment($token[1], $this->line, $this->filePos, $this->pos); : new Comment($token[1], $this->line, $this->filePos, $this->pos);
@ -276,13 +314,13 @@ class Lexer
continue; continue;
} }
if (isset($this->usedAttributes['endLine'])) { if ($this->attributeEndLineUsed) {
$endAttributes['endLine'] = $this->line; $endAttributes['endLine'] = $this->line;
} }
if (isset($this->usedAttributes['endTokenPos'])) { if ($this->attributeEndTokenPosUsed) {
$endAttributes['endTokenPos'] = $this->pos; $endAttributes['endTokenPos'] = $this->pos;
} }
if (isset($this->usedAttributes['endFilePos'])) { if ($this->attributeEndFilePosUsed) {
$endAttributes['endFilePos'] = $this->filePos - 1; $endAttributes['endFilePos'] = $this->filePos - 1;
} }

View File

@ -4,26 +4,61 @@ namespace PhpParser\Lexer;
use PhpParser\Error; use PhpParser\Error;
use PhpParser\ErrorHandler; use PhpParser\ErrorHandler;
use PhpParser\Lexer;
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulatorInterface;
use PhpParser\Parser\Tokens;
class Emulative extends \PhpParser\Lexer class Emulative extends Lexer
{ {
const PHP_7_3 = '7.3.0dev'; const PHP_7_3 = '7.3.0dev';
const PHP_7_4 = '7.4.0dev';
const T_COALESCE_EQUAL = 1007;
const T_FN = 1008;
const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX'
/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n
(?:.*\r?\n)*?
(?<indentation>\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
REGEX;
/** @var mixed[] Patches used to reverse changes introduced in the code */
private $patches = [];
/** @var TokenEmulatorInterface[] */
private $tokenEmulators = [];
/** /**
* @var array Patches used to reverse changes introduced in the code * @param mixed[] $options
*/ */
private $patches; public function __construct(array $options = [])
{
parent::__construct($options);
$this->tokenEmulators[] = new FnTokenEmulator();
$this->tokenEmulators[] = new CoaleseEqualTokenEmulator();
$this->tokenEmulators[] = new NumericLiteralSeparatorEmulator();
$this->tokenMap[self::T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL;
$this->tokenMap[self::T_FN] = Tokens::T_FN;
}
public function startLexing(string $code, ErrorHandler $errorHandler = null) { public function startLexing(string $code, ErrorHandler $errorHandler = null) {
$this->patches = []; $this->patches = [];
$preparedCode = $this->prepareCode($code);
if (null === $preparedCode) { if ($this->isEmulationNeeded($code) === false) {
// Nothing to emulate, yay // Nothing to emulate, yay
parent::startLexing($code, $errorHandler); parent::startLexing($code, $errorHandler);
return; return;
} }
$collector = new ErrorHandler\Collecting(); $collector = new ErrorHandler\Collecting();
// 1. emulation of heredoc and nowdoc new syntax
$preparedCode = $this->processHeredocNowdoc($code);
parent::startLexing($preparedCode, $collector); parent::startLexing($preparedCode, $collector);
$this->fixupTokens(); $this->fixupTokens();
@ -34,32 +69,34 @@ class Emulative extends \PhpParser\Lexer
$errorHandler->handleError($error); $errorHandler->handleError($error);
} }
} }
// add token emulation
foreach ($this->tokenEmulators as $emulativeToken) {
if ($emulativeToken->isEmulationNeeded($code)) {
$this->tokens = $emulativeToken->emulate($code, $this->tokens);
}
}
} }
/** private function isHeredocNowdocEmulationNeeded(string $code): bool
* Prepares code for emulation. If nothing has to be emulated null is returned. {
* // skip version where this works without emulation
* @param string $code
* @return null|string
*/
private function prepareCode(string $code) {
if (version_compare(\PHP_VERSION, self::PHP_7_3, '>=')) { if (version_compare(\PHP_VERSION, self::PHP_7_3, '>=')) {
return null; return false;
} }
if (strpos($code, '<<<') === false) { return strpos($code, '<<<') !== false;
// Definitely doesn't contain heredoc/nowdoc }
return null;
private function processHeredocNowdoc(string $code): string
{
if ($this->isHeredocNowdocEmulationNeeded($code) === false) {
return $code;
} }
$flexibleDocStringRegex = <<<'REGEX' if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n
(?:.*\r?\n)*?
(?<indentation>\h*)\2(?![a-zA-Z_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
REGEX;
if (!preg_match_all($flexibleDocStringRegex, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
// No heredoc/nowdoc found // No heredoc/nowdoc found
return null; return $code;
} }
// Keep track of how much we need to adjust string offsets due to the modifications we // Keep track of how much we need to adjust string offsets due to the modifications we
@ -93,19 +130,29 @@ REGEX;
} }
} }
if (empty($this->patches)) {
// We did not end up emulating anything
return null;
}
return $code; return $code;
} }
private function fixupTokens() { private function isEmulationNeeded(string $code): bool
assert(count($this->patches) > 0); {
foreach ($this->tokenEmulators as $emulativeToken) {
if ($emulativeToken->isEmulationNeeded($code)) {
return true;
}
}
return $this->isHeredocNowdocEmulationNeeded($code);
}
private function fixupTokens()
{
if (\count($this->patches) === 0) {
return;
}
// Load first patch // Load first patch
$patchIdx = 0; $patchIdx = 0;
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
// We use a manual loop over the tokens, because we modify the array on the fly // We use a manual loop over the tokens, because we modify the array on the fly
@ -200,4 +247,4 @@ REGEX;
$error->setAttributes($attrs); $error->setAttributes($attrs);
} }
} }
} }

View File

@ -0,0 +1,41 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
final class CoaleseEqualTokenEmulator implements TokenEmulatorInterface
{
public function isEmulationNeeded(string $code) : bool
{
// skip version where this is supported
if (version_compare(\PHP_VERSION, Emulative::PHP_7_4, '>=')) {
return false;
}
return strpos($code, '??=') !== false;
}
public function emulate(string $code, array $tokens): array
{
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way
$line = 1;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if (isset($tokens[$i + 1])) {
if ($tokens[$i][0] === T_COALESCE && $tokens[$i + 1] === '=') {
array_splice($tokens, $i, 2, [
[Emulative::T_COALESCE_EQUAL, '??=', $line]
]);
$c--;
continue;
}
}
if (\is_array($tokens[$i])) {
$line += substr_count($tokens[$i][1], "\n");
}
}
return $tokens;
}
}

View File

@ -0,0 +1,53 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
final class FnTokenEmulator implements TokenEmulatorInterface
{
public function isEmulationNeeded(string $code) : bool
{
// skip version where this is supported
if (version_compare(\PHP_VERSION, Emulative::PHP_7_4, '>=')) {
return false;
}
return strpos($code, 'fn') !== false;
}
public function emulate(string $code, array $tokens): array
{
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way
foreach ($tokens as $i => $token) {
if ($token[0] === T_STRING && $token[1] === 'fn') {
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $i);
if ($previousNonSpaceToken !== null && $previousNonSpaceToken[0] === T_OBJECT_OPERATOR) {
continue;
}
$tokens[$i][0] = Emulative::T_FN;
}
}
return $tokens;
}
/**
* @param mixed[] $tokens
* @return mixed[]|null
*/
private function getPreviousNonSpaceToken(array $tokens, int $start)
{
for ($i = $start - 1; $i >= 0; --$i) {
if ($tokens[$i][0] === T_WHITESPACE) {
continue;
}
return $tokens[$i];
}
return null;
}
}

View File

@ -0,0 +1,98 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
final class NumericLiteralSeparatorEmulator implements TokenEmulatorInterface
{
const BIN = '(?:0b[01]+(?:_[01]+)*)';
const HEX = '(?:0x[0-9a-f]+(?:_[0-9a-f]+)*)';
const DEC = '(?:[0-9]+(?:_[0-9]+)*)';
const SIMPLE_FLOAT = '(?:' . self::DEC . '\.' . self::DEC . '?|\.' . self::DEC . ')';
const EXP = '(?:e[+-]?' . self::DEC . ')';
const FLOAT = '(?:' . self::SIMPLE_FLOAT . self::EXP . '?|' . self::DEC . self::EXP . ')';
const NUMBER = '~' . self::FLOAT . '|' . self::BIN . '|' . self::HEX . '|' . self::DEC . '~iA';
public function isEmulationNeeded(string $code) : bool
{
// skip version where this is supported
if (version_compare(\PHP_VERSION, Emulative::PHP_7_4, '>=')) {
return false;
}
return preg_match('~[0-9a-f]_[0-9a-f]~i', $code) !== false;
}
public function emulate(string $code, array $tokens): array
{
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way
$codeOffset = 0;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
$token = $tokens[$i];
$tokenLen = \strlen(\is_array($token) ? $token[1] : $token);
if ($token[0] !== T_LNUMBER && $token[0] !== T_DNUMBER) {
$codeOffset += $tokenLen;
continue;
}
$res = preg_match(self::NUMBER, $code, $matches, 0, $codeOffset);
assert($res, "No number at number token position");
$match = $matches[0];
$matchLen = \strlen($match);
if ($matchLen === $tokenLen) {
// Original token already holds the full number.
$codeOffset += $tokenLen;
continue;
}
$tokenKind = $this->resolveIntegerOrFloatToken($match);
$newTokens = [[$tokenKind, $match, $token[2]]];
$numTokens = 1;
$len = $tokenLen;
while ($matchLen > $len) {
$nextToken = $tokens[$i + $numTokens];
$nextTokenText = \is_array($nextToken) ? $nextToken[1] : $nextToken;
$nextTokenLen = \strlen($nextTokenText);
$numTokens++;
if ($matchLen < $len + $nextTokenLen) {
// Split trailing characters into a partial token.
assert(is_array($nextToken), "Partial token should be an array token");
$partialText = substr($nextTokenText, $matchLen - $len);
$newTokens[] = [$nextToken[0], $partialText, $nextToken[2]];
break;
}
$len += $nextTokenLen;
}
array_splice($tokens, $i, $numTokens, $newTokens);
$c -= $numTokens - \count($newTokens);
$codeOffset += $matchLen;
}
return $tokens;
}
private function resolveIntegerOrFloatToken(string $str): int
{
$str = str_replace('_', '', $str);
if (stripos($str, '0b') === 0) {
$num = bindec($str);
} elseif (stripos($str, '0x') === 0) {
$num = hexdec($str);
} elseif (stripos($str, '0') === 0 && ctype_digit($str)) {
$num = octdec($str);
} else {
$num = +$str;
}
return is_float($num) ? T_DNUMBER : T_LNUMBER;
}
}

View File

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

View File

@ -22,7 +22,7 @@ class Arg extends NodeAbstract
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = []) { public function __construct(Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->value = $value; $this->value = $value;
$this->byRef = $byRef; $this->byRef = $byRef;
$this->unpack = $unpack; $this->unpack = $unpack;

View File

@ -22,7 +22,7 @@ class Const_ extends NodeAbstract
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, Expr $value, array $attributes = []) { public function __construct($name, Expr $value, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->name = \is_string($name) ? new Identifier($name) : $name; $this->name = \is_string($name) ? new Identifier($name) : $name;
$this->value = $value; $this->value = $value;
} }

View File

@ -19,7 +19,7 @@ class ArrayDimFetch extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, Expr $dim = null, array $attributes = []) { public function __construct(Expr $var, Expr $dim = null, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
$this->dim = $dim; $this->dim = $dim;
} }

View File

@ -12,6 +12,8 @@ class ArrayItem extends Expr
public $value; public $value;
/** @var bool Whether to assign by reference */ /** @var bool Whether to assign by reference */
public $byRef; public $byRef;
/** @var bool Whether to unpack the argument */
public $unpack;
/** /**
* Constructs an array item node. * Constructs an array item node.
@ -21,17 +23,18 @@ class ArrayItem extends Expr
* @param bool $byRef Whether to assign by reference * @param bool $byRef Whether to assign by reference
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $value, Expr $key = null, bool $byRef = false, array $attributes = []) { public function __construct(Expr $value, Expr $key = null, bool $byRef = false, array $attributes = [], bool $unpack = false) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->key = $key; $this->key = $key;
$this->value = $value; $this->value = $value;
$this->byRef = $byRef; $this->byRef = $byRef;
$this->unpack = $unpack;
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['key', 'value', 'byRef']; return ['key', 'value', 'byRef', 'unpack'];
} }
public function getType() : string { public function getType() : string {
return 'Expr_ArrayItem'; return 'Expr_ArrayItem';
} }

View File

@ -20,7 +20,7 @@ class Array_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $items = [], array $attributes = []) { public function __construct(array $items = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->items = $items; $this->items = $items;
} }

View File

@ -0,0 +1,71 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\FunctionLike;
class ArrowFunction extends Expr implements FunctionLike
{
/** @var bool */
public $static;
/** @var bool */
public $byRef;
/** @var Node\Param[] */
public $params = [];
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType */
public $returnType;
/** @var Expr */
public $expr;
/**
* @param array $subNodes Array of the following optional subnodes:
* 'static' => false : Whether the closure is static
* 'byRef' => false : Whether to return by reference
* 'params' => array() : Parameters
* 'returnType' => null : Return type
* 'expr' => Expr : Expression body
* @param array $attributes Additional attributes
*/
public function __construct(array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->static = $subNodes['static'] ?? false;
$this->byRef = $subNodes['byRef'] ?? false;
$this->params = $subNodes['params'] ?? [];
$returnType = $subNodes['returnType'] ?? null;
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
$this->expr = $subNodes['expr'] ?? null;
}
public function getSubNodeNames() : array {
return ['static', 'byRef', 'params', 'returnType', 'expr'];
}
public function returnsByRef() : bool {
return $this->byRef;
}
public function getParams() : array {
return $this->params;
}
public function getReturnType() {
return $this->returnType;
}
/**
* @return Node\Stmt\Return_[]
*/
public function getStmts() : array {
return [new Node\Stmt\Return_($this->expr)];
}
public function getType() : string {
return 'Expr_ArrowFunction';
}
}

View File

@ -19,7 +19,7 @@ class Assign extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, Expr $expr, array $attributes = []) { public function __construct(Expr $var, Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -19,7 +19,7 @@ abstract class AssignOp extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, Expr $expr, array $attributes = []) { public function __construct(Expr $var, Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -0,0 +1,12 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Coalesce extends AssignOp
{
public function getType() : string {
return 'Expr_AssignOp_Coalesce';
}
}

View File

@ -19,7 +19,7 @@ class AssignRef extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, Expr $expr, array $attributes = []) { public function __construct(Expr $var, Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -19,7 +19,7 @@ abstract class BinaryOp extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $left, Expr $right, array $attributes = []) { public function __construct(Expr $left, Expr $right, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->left = $left; $this->left = $left;
$this->right = $right; $this->right = $right;
} }

View File

@ -16,7 +16,7 @@ class BitwiseNot extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -16,7 +16,7 @@ class BooleanNot extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -16,7 +16,7 @@ abstract class Cast extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -6,6 +6,11 @@ use PhpParser\Node\Expr\Cast;
class Double extends Cast class Double extends Cast
{ {
// For use in "kind" attribute
const KIND_DOUBLE = 1; // "double" syntax
const KIND_FLOAT = 2; // "float" syntax
const KIND_REAL = 3; // "real" syntax
public function getType() : string { public function getType() : string {
return 'Expr_Cast_Double'; return 'Expr_Cast_Double';
} }

View File

@ -21,7 +21,7 @@ class ClassConstFetch extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($class, $name, array $attributes = []) { public function __construct($class, $name, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->class = $class; $this->class = $class;
$this->name = \is_string($name) ? new Identifier($name) : $name; $this->name = \is_string($name) ? new Identifier($name) : $name;
} }

View File

@ -16,7 +16,7 @@ class Clone_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -16,7 +16,7 @@ class Closure extends Expr implements FunctionLike
public $params; public $params;
/** @var ClosureUse[] use()s */ /** @var ClosureUse[] use()s */
public $uses; public $uses;
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */ /** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
public $returnType; public $returnType;
/** @var Node\Stmt[] Statements */ /** @var Node\Stmt[] Statements */
public $stmts; public $stmts;
@ -34,7 +34,7 @@ class Closure extends Expr implements FunctionLike
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $subNodes = [], array $attributes = []) { public function __construct(array $subNodes = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->static = $subNodes['static'] ?? false; $this->static = $subNodes['static'] ?? false;
$this->byRef = $subNodes['byRef'] ?? false; $this->byRef = $subNodes['byRef'] ?? false;
$this->params = $subNodes['params'] ?? []; $this->params = $subNodes['params'] ?? [];

View File

@ -19,7 +19,7 @@ class ClosureUse extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr\Variable $var, bool $byRef = false, array $attributes = []) { public function __construct(Expr\Variable $var, bool $byRef = false, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
$this->byRef = $byRef; $this->byRef = $byRef;
} }

View File

@ -17,7 +17,7 @@ class ConstFetch extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Name $name, array $attributes = []) { public function __construct(Name $name, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->name = $name; $this->name = $name;
} }

View File

@ -16,7 +16,7 @@ class Empty_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -18,7 +18,7 @@ class Error extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $attributes = []) { public function __construct(array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {

View File

@ -16,7 +16,7 @@ class ErrorSuppress extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -16,7 +16,7 @@ class Eval_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -20,7 +20,7 @@ class Exit_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr = null, array $attributes = []) { public function __construct(Expr $expr = null, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -20,7 +20,7 @@ class FuncCall extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $args = [], array $attributes = []) { public function __construct($name, array $args = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->name = $name; $this->name = $name;
$this->args = $args; $this->args = $args;
} }

View File

@ -24,7 +24,7 @@ class Include_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, int $type, array $attributes = []) { public function __construct(Expr $expr, int $type, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
$this->type = $type; $this->type = $type;
} }

View File

@ -20,7 +20,7 @@ class Instanceof_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, $class, array $attributes = []) { public function __construct(Expr $expr, $class, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
$this->class = $class; $this->class = $class;
} }

View File

@ -16,7 +16,7 @@ class Isset_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $vars, array $attributes = []) { public function __construct(array $vars, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->vars = $vars; $this->vars = $vars;
} }

View File

@ -16,7 +16,7 @@ class List_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $items, array $attributes = []) { public function __construct(array $items, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->items = $items; $this->items = $items;
} }

View File

@ -24,7 +24,7 @@ class MethodCall extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) { public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
$this->name = \is_string($name) ? new Identifier($name) : $name; $this->name = \is_string($name) ? new Identifier($name) : $name;
$this->args = $args; $this->args = $args;

View File

@ -20,7 +20,7 @@ class New_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($class, array $args = [], array $attributes = []) { public function __construct($class, array $args = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->class = $class; $this->class = $class;
$this->args = $args; $this->args = $args;
} }

View File

@ -16,7 +16,7 @@ class PostDec extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, array $attributes = []) { public function __construct(Expr $var, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
} }

View File

@ -16,7 +16,7 @@ class PostInc extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, array $attributes = []) { public function __construct(Expr $var, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
} }

View File

@ -16,7 +16,7 @@ class PreDec extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, array $attributes = []) { public function __construct(Expr $var, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
} }

View File

@ -16,7 +16,7 @@ class PreInc extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, array $attributes = []) { public function __construct(Expr $var, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
} }

View File

@ -16,7 +16,7 @@ class Print_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -20,7 +20,7 @@ class PropertyFetch extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $var, $name, array $attributes = []) { public function __construct(Expr $var, $name, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
$this->name = \is_string($name) ? new Identifier($name) : $name; $this->name = \is_string($name) ? new Identifier($name) : $name;
} }

View File

@ -16,7 +16,7 @@ class ShellExec extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $parts, array $attributes = []) { public function __construct(array $parts, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->parts = $parts; $this->parts = $parts;
} }

View File

@ -24,7 +24,7 @@ class StaticCall extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($class, $name, array $args = [], array $attributes = []) { public function __construct($class, $name, array $args = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->class = $class; $this->class = $class;
$this->name = \is_string($name) ? new Identifier($name) : $name; $this->name = \is_string($name) ? new Identifier($name) : $name;
$this->args = $args; $this->args = $args;

View File

@ -21,7 +21,7 @@ class StaticPropertyFetch extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($class, $name, array $attributes = []) { public function __construct($class, $name, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->class = $class; $this->class = $class;
$this->name = \is_string($name) ? new VarLikeIdentifier($name) : $name; $this->name = \is_string($name) ? new VarLikeIdentifier($name) : $name;
} }

View File

@ -22,7 +22,7 @@ class Ternary extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $cond, $if, Expr $else, array $attributes = []) { public function __construct(Expr $cond, $if, Expr $else, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->cond = $cond; $this->cond = $cond;
$this->if = $if; $this->if = $if;
$this->else = $else; $this->else = $else;

View File

@ -16,7 +16,7 @@ class UnaryMinus extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -16,7 +16,7 @@ class UnaryPlus extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -16,7 +16,7 @@ class Variable extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $attributes = []) { public function __construct($name, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->name = $name; $this->name = $name;
} }

View File

@ -16,7 +16,7 @@ class YieldFrom extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $expr, array $attributes = []) { public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

View File

@ -19,7 +19,7 @@ class Yield_ extends Expr
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Expr $value = null, Expr $key = null, array $attributes = []) { public function __construct(Expr $value = null, Expr $key = null, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->key = $key; $this->key = $key;
$this->value = $value; $this->value = $value;
} }

View File

@ -23,7 +23,7 @@ interface FunctionLike extends Node
/** /**
* Get the declared return type or null * Get the declared return type or null
* *
* @return null|Identifier|Node\Name|Node\NullableType * @return null|Identifier|Node\Name|Node\NullableType|Node\UnionType
*/ */
public function getReturnType(); public function getReturnType();

View File

@ -25,7 +25,7 @@ class Identifier extends NodeAbstract
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(string $name, array $attributes = []) { public function __construct(string $name, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->name = $name; $this->name = $name;
} }

View File

@ -6,9 +6,7 @@ use PhpParser\NodeAbstract;
class Name extends NodeAbstract class Name extends NodeAbstract
{ {
/** /** @var string[] Parts of the name */
* @var string[] Parts of the name
*/
public $parts; public $parts;
private static $specialClassNames = [ private static $specialClassNames = [
@ -24,7 +22,7 @@ class Name extends NodeAbstract
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $attributes = []) { public function __construct($name, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->parts = self::prepareName($name); $this->parts = self::prepareName($name);
} }
@ -237,7 +235,7 @@ class Name extends NodeAbstract
'Expected string, array of parts or Name instance' 'Expected string, array of parts or Name instance'
); );
} }
public function getType() : string { public function getType() : string {
return 'Name'; return 'Name';
} }

View File

@ -16,7 +16,7 @@ class NullableType extends NodeAbstract
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($type, array $attributes = []) { public function __construct($type, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->type = \is_string($type) ? new Identifier($type) : $type; $this->type = \is_string($type) ? new Identifier($type) : $type;
} }

View File

@ -6,7 +6,7 @@ use PhpParser\NodeAbstract;
class Param extends NodeAbstract class Param extends NodeAbstract
{ {
/** @var null|Identifier|Name|NullableType Typehint */ /** @var null|Identifier|Name|NullableType|UnionType Type declaration */
public $type; public $type;
/** @var bool Whether parameter is passed by reference */ /** @var bool Whether parameter is passed by reference */
public $byRef; public $byRef;
@ -20,18 +20,18 @@ class Param extends NodeAbstract
/** /**
* Constructs a parameter node. * Constructs a parameter node.
* *
* @param Expr\Variable|Expr\Error $var Parameter variable * @param Expr\Variable|Expr\Error $var Parameter variable
* @param null|Expr $default Default value * @param null|Expr $default Default value
* @param null|string|Name|NullableType $type Typehint * @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
* @param bool $byRef Whether is passed by reference * @param bool $byRef Whether is passed by reference
* @param bool $variadic Whether this is a variadic argument * @param bool $variadic Whether this is a variadic argument
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct( public function __construct(
$var, Expr $default = null, $type = null, $var, Expr $default = null, $type = null,
bool $byRef = false, bool $variadic = false, array $attributes = [] bool $byRef = false, bool $variadic = false, array $attributes = []
) { ) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->type = \is_string($type) ? new Identifier($type) : $type; $this->type = \is_string($type) ? new Identifier($type) : $type;
$this->byRef = $byRef; $this->byRef = $byRef;
$this->variadic = $variadic; $this->variadic = $variadic;
@ -42,7 +42,7 @@ class Param extends NodeAbstract
public function getSubNodeNames() : array { public function getSubNodeNames() : array {
return ['type', 'byRef', 'variadic', 'var', 'default']; return ['type', 'byRef', 'variadic', 'var', 'default'];
} }
public function getType() : string { public function getType() : string {
return 'Param'; return 'Param';
} }

View File

@ -16,7 +16,7 @@ class DNumber extends Scalar
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(float $value, array $attributes = []) { public function __construct(float $value, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->value = $value; $this->value = $value;
} }
@ -34,6 +34,8 @@ class DNumber extends Scalar
* @return float The parsed number * @return float The parsed number
*/ */
public static function parse(string $str) : float { public static function parse(string $str) : float {
$str = str_replace('_', '', $str);
// if string contains any of .eE just cast it to float // if string contains any of .eE just cast it to float
if (false !== strpbrk($str, '.eE')) { if (false !== strpbrk($str, '.eE')) {
return (float) $str; return (float) $str;

View File

@ -17,7 +17,7 @@ class Encapsed extends Scalar
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $parts, array $attributes = []) { public function __construct(array $parts, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->parts = $parts; $this->parts = $parts;
} }

View File

@ -16,7 +16,7 @@ class EncapsedStringPart extends Scalar
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(string $value, array $attributes = []) { public function __construct(string $value, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->value = $value; $this->value = $value;
} }

View File

@ -23,7 +23,7 @@ class LNumber extends Scalar
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(int $value, array $attributes = []) { public function __construct(int $value, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->value = $value; $this->value = $value;
} }
@ -41,6 +41,8 @@ class LNumber extends Scalar
* @return LNumber The constructed LNumber, including kind attribute * @return LNumber The constructed LNumber, including kind attribute
*/ */
public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber { public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber {
$str = str_replace('_', '', $str);
if ('0' !== $str[0] || '0' === $str) { if ('0' !== $str[0] || '0' === $str) {
$attributes['kind'] = LNumber::KIND_DEC; $attributes['kind'] = LNumber::KIND_DEC;
return new LNumber((int) $str, $attributes); return new LNumber((int) $str, $attributes);

View File

@ -12,7 +12,7 @@ abstract class MagicConst extends Scalar
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $attributes = []) { public function __construct(array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
} }
public function getSubNodeNames() : array { public function getSubNodeNames() : array {

View File

@ -34,7 +34,7 @@ class String_ extends Scalar
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(string $value, array $attributes = []) { public function __construct(string $value, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->value = $value; $this->value = $value;
} }
@ -100,7 +100,7 @@ class String_ extends Scalar
if (isset(self::$replacements[$str])) { if (isset(self::$replacements[$str])) {
return self::$replacements[$str]; return self::$replacements[$str];
} elseif ('x' === $str[0] || 'X' === $str[0]) { } elseif ('x' === $str[0] || 'X' === $str[0]) {
return chr(hexdec($str)); return chr(hexdec(substr($str, 1)));
} elseif ('u' === $str[0]) { } elseif ('u' === $str[0]) {
return self::codePointToUtf8(hexdec($matches[2])); return self::codePointToUtf8(hexdec($matches[2]));
} else { } else {
@ -134,7 +134,7 @@ class String_ extends Scalar
} }
throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large'); throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large');
} }
public function getType() : string { public function getType() : string {
return 'Scalar_String'; return 'Scalar_String';
} }

View File

@ -16,7 +16,7 @@ class Break_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Node\Expr $num = null, array $attributes = []) { public function __construct(Node\Expr $num = null, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->num = $num; $this->num = $num;
} }

View File

@ -19,7 +19,7 @@ class Case_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($cond, array $stmts = [], array $attributes = []) { public function __construct($cond, array $stmts = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->cond = $cond; $this->cond = $cond;
$this->stmts = $stmts; $this->stmts = $stmts;
} }

View File

@ -25,7 +25,7 @@ class Catch_ extends Node\Stmt
public function __construct( public function __construct(
array $types, Expr\Variable $var, array $stmts = [], array $attributes = [] array $types, Expr\Variable $var, array $stmts = [], array $attributes = []
) { ) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->types = $types; $this->types = $types;
$this->var = $var; $this->var = $var;
$this->stmts = $stmts; $this->stmts = $stmts;

View File

@ -19,7 +19,7 @@ class ClassConst extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $consts, int $flags = 0, array $attributes = []) { public function __construct(array $consts, int $flags = 0, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->flags = $flags; $this->flags = $flags;
$this->consts = $consts; $this->consts = $consts;
} }

View File

@ -14,6 +14,45 @@ abstract class ClassLike extends Node\Stmt
/** @var Node\Stmt[] Statements */ /** @var Node\Stmt[] Statements */
public $stmts; public $stmts;
/**
* @return TraitUse[]
*/
public function getTraitUses() : array {
$traitUses = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof TraitUse) {
$traitUses[] = $stmt;
}
}
return $traitUses;
}
/**
* @return ClassConst[]
*/
public function getConstants() : array {
$constants = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof ClassConst) {
$constants[] = $stmt;
}
}
return $constants;
}
/**
* @return Property[]
*/
public function getProperties() : array {
$properties = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof Property) {
$properties[] = $stmt;
}
}
return $properties;
}
/** /**
* Gets all methods defined directly in this class/interface/trait * Gets all methods defined directly in this class/interface/trait
* *

View File

@ -15,7 +15,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
public $name; public $name;
/** @var Node\Param[] Parameters */ /** @var Node\Param[] Parameters */
public $params; public $params;
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */ /** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
public $returnType; public $returnType;
/** @var Node\Stmt[]|null Statements */ /** @var Node\Stmt[]|null Statements */
public $stmts; public $stmts;
@ -51,7 +51,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $subNodes = [], array $attributes = []) { public function __construct($name, array $subNodes = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0;
$this->byRef = $subNodes['byRef'] ?? false; $this->byRef = $subNodes['byRef'] ?? false;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->name = \is_string($name) ? new Node\Identifier($name) : $name;

View File

@ -35,7 +35,7 @@ class Class_ extends ClassLike
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($name, array $subNodes = [], array $attributes = []) { public function __construct($name, array $subNodes = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->extends = $subNodes['extends'] ?? null; $this->extends = $subNodes['extends'] ?? null;
@ -98,7 +98,7 @@ class Class_ extends ClassLike
throw new Error('Cannot use the final modifier on an abstract class member'); throw new Error('Cannot use the final modifier on an abstract class member');
} }
} }
public function getType() : string { public function getType() : string {
return 'Stmt_Class'; return 'Stmt_Class';
} }

View File

@ -16,7 +16,7 @@ class Const_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $consts, array $attributes = []) { public function __construct(array $consts, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->consts = $consts; $this->consts = $consts;
} }

View File

@ -16,7 +16,7 @@ class Continue_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Node\Expr $num = null, array $attributes = []) { public function __construct(Node\Expr $num = null, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->num = $num; $this->num = $num;
} }

View File

@ -19,7 +19,7 @@ class DeclareDeclare extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct($key, Node\Expr $value, array $attributes = []) { public function __construct($key, Node\Expr $value, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->key = \is_string($key) ? new Node\Identifier($key) : $key; $this->key = \is_string($key) ? new Node\Identifier($key) : $key;
$this->value = $value; $this->value = $value;
} }

View File

@ -19,7 +19,7 @@ class Declare_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $declares, array $stmts = null, array $attributes = []) { public function __construct(array $declares, array $stmts = null, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->declares = $declares; $this->declares = $declares;
$this->stmts = $stmts; $this->stmts = $stmts;
} }

View File

@ -19,7 +19,7 @@ class Do_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->cond = $cond; $this->cond = $cond;
$this->stmts = $stmts; $this->stmts = $stmts;
} }

View File

@ -16,7 +16,7 @@ class Echo_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $exprs, array $attributes = []) { public function __construct(array $exprs, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->exprs = $exprs; $this->exprs = $exprs;
} }

View File

@ -19,7 +19,7 @@ class ElseIf_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->cond = $cond; $this->cond = $cond;
$this->stmts = $stmts; $this->stmts = $stmts;
} }

View File

@ -16,7 +16,7 @@ class Else_ extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(array $stmts = [], array $attributes = []) { public function __construct(array $stmts = [], array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->stmts = $stmts; $this->stmts = $stmts;
} }

View File

@ -19,7 +19,7 @@ class Expression extends Node\Stmt
* @param array $attributes Additional attributes * @param array $attributes Additional attributes
*/ */
public function __construct(Node\Expr $expr, array $attributes = []) { public function __construct(Node\Expr $expr, array $attributes = []) {
parent::__construct($attributes); $this->attributes = $attributes;
$this->expr = $expr; $this->expr = $expr;
} }

Some files were not shown because too many files have changed in this diff Show More