mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-13 18:36:36 +02:00
Compare commits
104 Commits
v4.0.0alph
...
v4.0.0
Author | SHA1 | Date | |
---|---|---|---|
9c18e3db49 | |||
ae52aadb43 | |||
9cea94000a | |||
bb87e28e7d | |||
7484acb88b | |||
bc5ba47b28 | |||
b9996315a6 | |||
de3470190c | |||
1c3eabb000 | |||
8d1e86b47f | |||
ff10cc9d08 | |||
14454326e9 | |||
3a45c1a121 | |||
ad9c42b66a | |||
08215e7646 | |||
c18bb27723 | |||
dd0adcc96c | |||
e4505de346 | |||
a513ccabb7 | |||
d817818b5d | |||
6a273c9fbd | |||
c2d3ecad35 | |||
e57b3a0978 | |||
1cdb280a30 | |||
d01fafcb40 | |||
67df02c844 | |||
b85b6b3519 | |||
94c715d97e | |||
4dacbb8d39 | |||
aa685e711a | |||
edafeb85c4 | |||
68d07c4662 | |||
8fae99aafe | |||
c7ada124d0 | |||
f6617e6d25 | |||
2499534729 | |||
e0a2043089 | |||
e6e52abae7 | |||
7f72c84122 | |||
fc8ac71e76 | |||
a8968caa5b | |||
5285df8f22 | |||
4366aa2fb0 | |||
bf7d811cda | |||
248b29ecf6 | |||
bcb45d31eb | |||
3bc31488ce | |||
92b39e3d1f | |||
1c7fd314d1 | |||
fb8175567e | |||
63abf9cb3f | |||
ceb4932ca8 | |||
aa289c9694 | |||
579f4ce846 | |||
8d3cb5f57b | |||
de82a40d04 | |||
a86151f24f | |||
e6e8791848 | |||
83b958763f | |||
4dbb02c57b | |||
4fcdac40d1 | |||
04feb90d79 | |||
dc3ace55c3 | |||
e5453f0d46 | |||
b507fa43da | |||
336a49b428 | |||
fa174b093f | |||
94ca9a7ab9 | |||
0bb74e03aa | |||
8d3f48ab75 | |||
1c11626f0a | |||
05e2cd287e | |||
bac91b426e | |||
a75164c77e | |||
c59e75f873 | |||
4392a7b164 | |||
a659240dc2 | |||
68e9b91e9c | |||
7162b36f2d | |||
73be07672b | |||
e2c0c598a3 | |||
66f88cfa07 | |||
9b6a2577fa | |||
457fe049a8 | |||
6a2e1ae440 | |||
47c973b3aa | |||
56bc8ebb9b | |||
361398bc0d | |||
08131e7ff2 | |||
fd6e8d7ea8 | |||
0ba710affa | |||
72231abe6d | |||
7254040998 | |||
57bf378412 | |||
b58157f024 | |||
ab596db285 | |||
7f92edf3a1 | |||
837579a40c | |||
b241a121a3 | |||
5054a68bfb | |||
d16f050d74 | |||
5900d78cc9 | |||
776275361a | |||
651341d53b |
@ -9,6 +9,7 @@ cache:
|
|||||||
php:
|
php:
|
||||||
- 7.0
|
- 7.0
|
||||||
- 7.1
|
- 7.1
|
||||||
|
- 7.2
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
89
CHANGELOG.md
89
CHANGELOG.md
@ -1,8 +1,55 @@
|
|||||||
Version 4.0.0-dev
|
Version 4.0.1-dev
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Nothing yet.
|
Nothing yet.
|
||||||
|
|
||||||
|
Version 4.0.0 (2018-02-28)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
* No significant code changes since the beta 1 release.
|
||||||
|
|
||||||
|
Version 4.0.0-beta1 (2018-01-27)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* In formatting-preserving pretty printer: Fixed indentation when inserting into lists. (#466)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* In formatting-preserving pretty printer: Improved formatting of elements inserted into multi-line
|
||||||
|
arrays.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
* The `Autoloader` class has been removed. It is now required to use the Composer autoloader.
|
||||||
|
|
||||||
|
Version 4.0.0-alpha3 (2017-12-26)
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* In the formatting-preserving pretty printer:
|
||||||
|
* Fixed comment indentation.
|
||||||
|
* Fixed handling of inline HTML in the fallback case.
|
||||||
|
* Fixed insertion into list nodes that require creation of a code block.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Added support for inserting at the start of list nodes in formatting-preserving pretty printer.
|
||||||
|
|
||||||
|
Version 4.0.0-alpha2 (2017-11-10)
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* In the formatting-preserving pretty printer:
|
||||||
|
* Added support for changing modifiers.
|
||||||
|
* Added support for anonymous classes.
|
||||||
|
* Added support for removing from list nodes.
|
||||||
|
* Improved support for changing comments.
|
||||||
|
* Added start token offsets to comments.
|
||||||
|
|
||||||
Version 4.0.0-alpha1 (2017-10-18)
|
Version 4.0.0-alpha1 (2017-10-18)
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
@ -22,7 +69,7 @@ Version 4.0.0-alpha1 (2017-10-18)
|
|||||||
AST traversal. This facilitates use in other context, such as class names in doc comments.
|
AST traversal. This facilitates use in other context, such as class names in doc comments.
|
||||||
Additionally it provides an API for getting the shortest representation of a name.
|
Additionally it provides an API for getting the shortest representation of a name.
|
||||||
* Added `Node::setAttributes()` method.
|
* Added `Node::setAttributes()` method.
|
||||||
* Added `JsonDecoder`. This allows convertion JSON back into an AST.
|
* Added `JsonDecoder`. This allows conversion JSON back into an AST.
|
||||||
* Added `Name` methods `toLowerString()` and `isSpecialClassName()`.
|
* Added `Name` methods `toLowerString()` and `isSpecialClassName()`.
|
||||||
* Added `Identifier` and `VarLikeIdentifier` nodes, which are used in place of simple strings in
|
* Added `Identifier` and `VarLikeIdentifier` nodes, which are used in place of simple strings in
|
||||||
many places.
|
many places.
|
||||||
@ -66,8 +113,31 @@ Version 4.0.0-alpha1 (2017-10-18)
|
|||||||
* The `BuilderAbstract` class has been removed. It's functionality is moved into `BuilderHelpers`.
|
* The `BuilderAbstract` class has been removed. It's functionality is moved into `BuilderHelpers`.
|
||||||
However, this is an internal class and should not be used directly.
|
However, this is an internal class and should not be used directly.
|
||||||
|
|
||||||
Version 3.1.2-dev
|
Version 3.1.5 (2018-02-28)
|
||||||
-----------------
|
--------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed duplicate comment assignment in switch statements. (#469)
|
||||||
|
* Improve compatibility with PHP-Scoper. (#477)
|
||||||
|
|
||||||
|
Version 3.1.4 (2018-01-25)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed pretty printing of `-(-$x)` and `+(+$x)`. (#459)
|
||||||
|
|
||||||
|
Version 3.1.3 (2017-12-26)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Improve compatibility with php-scoper, by supporting prefixed namespaces in
|
||||||
|
`NodeAbstract::getType()`.
|
||||||
|
|
||||||
|
Version 3.1.2 (2017-11-04)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -77,6 +147,7 @@ Version 3.1.2-dev
|
|||||||
|
|
||||||
* Added `kind` attribute for `Stmt\Namespace_` node, which is one of `KIND_SEMICOLON` or
|
* Added `kind` attribute for `Stmt\Namespace_` node, which is one of `KIND_SEMICOLON` or
|
||||||
`KIND_BRACED`. (#417)
|
`KIND_BRACED`. (#417)
|
||||||
|
* Added `setDocComment()` method to namespace builder. (#437)
|
||||||
|
|
||||||
Version 3.1.1 (2017-09-02)
|
Version 3.1.1 (2017-09-02)
|
||||||
--------------------------
|
--------------------------
|
||||||
@ -94,7 +165,7 @@ Version 3.1.0 (2017-07-28)
|
|||||||
* [PHP 7.2] Added support for trailing comma in group use statements.
|
* [PHP 7.2] Added support for trailing comma in group use statements.
|
||||||
* [PHP 7.2] Added support for `object` type. This means `object` types will now be represented as a
|
* [PHP 7.2] Added support for `object` type. This means `object` types will now be represented as a
|
||||||
builtin type (a simple `"object"` string), rather than a class `Name`.
|
builtin type (a simple `"object"` string), rather than a class `Name`.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Floating-point numbers are now printed correctly if the LC_NUMERIC locale uses a comma as decimal
|
* Floating-point numbers are now printed correctly if the LC_NUMERIC locale uses a comma as decimal
|
||||||
@ -214,7 +285,7 @@ This release primarily improves our support for error recovery.
|
|||||||
`NameResolver::__construct()`.
|
`NameResolver::__construct()`.
|
||||||
* The `NameResolver` now adds a `namespacedName` attribute on name nodes that cannot be statically
|
* The `NameResolver` now adds a `namespacedName` attribute on name nodes that cannot be statically
|
||||||
resolved (unqualified unaliased function or constant names in namespaces).
|
resolved (unqualified unaliased function or constant names in namespaces).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Fixed attribute assignment for `GroupUse` prefix and variables in interpolated strings.
|
* Fixed attribute assignment for `GroupUse` prefix and variables in interpolated strings.
|
||||||
@ -304,7 +375,7 @@ Additionally the following changes were made:
|
|||||||
takes an array of subnodes. Unlike classes/interfaces, traits can only have a `stmts` subnode.
|
takes an array of subnodes. Unlike classes/interfaces, traits can only have a `stmts` subnode.
|
||||||
* The `NodeDumper` now prints class/method/property/constant modifiers, as well as the include and
|
* The `NodeDumper` now prints class/method/property/constant modifiers, as well as the include and
|
||||||
use type in a textual representation, instead of only showing the number.
|
use type in a textual representation, instead of only showing the number.
|
||||||
* All methods on `PrettyPrinter\Standard` are now protected. Previoulsy most of them were public.
|
* All methods on `PrettyPrinter\Standard` are now protected. Previously most of them were public.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
@ -342,7 +413,7 @@ Version 2.1.0 (2016-04-19)
|
|||||||
* Added `kind` attribute to `Expr\Exit` to distinguish between `exit` and `die`.
|
* Added `kind` attribute to `Expr\Exit` to distinguish between `exit` and `die`.
|
||||||
* Added `kind` attribute to `Scalar\LNumber` to distinguish between decimal, binary, octal and
|
* Added `kind` attribute to `Scalar\LNumber` to distinguish between decimal, binary, octal and
|
||||||
hexadecimal numbers.
|
hexadecimal numbers.
|
||||||
* Added `kind` attribtue to `Expr\Array` to distinguish between `array()` and `[]`.
|
* Added `kind` attribute to `Expr\Array` to distinguish between `array()` and `[]`.
|
||||||
* Added `kind` attribute to `Scalar\String` and `Scalar\Encapsed` to distinguish between
|
* Added `kind` attribute to `Scalar\String` and `Scalar\Encapsed` to distinguish between
|
||||||
single-quoted, double-quoted, heredoc and nowdoc string.
|
single-quoted, double-quoted, heredoc and nowdoc string.
|
||||||
* Added `docLabel` attribute to `Scalar\String` and `Scalar\Encapsed`, if it is a heredoc or
|
* Added `docLabel` attribute to `Scalar\String` and `Scalar\Encapsed`, if it is a heredoc or
|
||||||
@ -458,4 +529,4 @@ A more detailed description of backwards incompatible changes can be found in th
|
|||||||
|
|
||||||
**This changelog only includes changes from the 2.0 series. For older changes see the
|
**This changelog only includes changes from the 2.0 series. For older changes see the
|
||||||
[1.x series changelog](https://github.com/nikic/PHP-Parser/blob/1.x/CHANGELOG.md) and the
|
[1.x series changelog](https://github.com/nikic/PHP-Parser/blob/1.x/CHANGELOG.md) and the
|
||||||
[0.9 series changelog](https://github.com/nikic/PHP-Parser/blob/0.9/CHANGELOG.md).**
|
[0.9 series changelog](https://github.com/nikic/PHP-Parser/blob/0.9/CHANGELOG.md).**
|
||||||
|
4
LICENSE
4
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2011 by Nikita Popov.
|
Copyright (c) 2011-2018 by Nikita Popov.
|
||||||
|
|
||||||
Some rights reserved.
|
Some rights reserved.
|
||||||
|
|
||||||
@ -28,4 +28,4 @@ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
37
README.md
37
README.md
@ -6,9 +6,9 @@ PHP Parser
|
|||||||
This is a PHP 5.2 to PHP 7.2 parser written in PHP. Its purpose is to simplify static code analysis and
|
This is a PHP 5.2 to PHP 7.2 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||||
manipulation.
|
manipulation.
|
||||||
|
|
||||||
[**Documentation for version 3.x**][doc_3_x] (stable; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).
|
[**Documentation for version 4.x**][doc_master] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 7.2).
|
||||||
|
|
||||||
[Documentation for version 4.x][doc_master] (development; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 7.2).
|
[Documentation for version 3.x][doc_3_x] (stable; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
@ -183,16 +183,43 @@ Documentation
|
|||||||
|
|
||||||
1. [Introduction](doc/0_Introduction.markdown)
|
1. [Introduction](doc/0_Introduction.markdown)
|
||||||
2. [Usage of basic components](doc/2_Usage_of_basic_components.markdown)
|
2. [Usage of basic components](doc/2_Usage_of_basic_components.markdown)
|
||||||
3. [Other node tree representations](doc/3_Other_node_tree_representations.markdown)
|
|
||||||
4. [Code generation](doc/4_Code_generation.markdown)
|
|
||||||
5. [Frequently asked questions](doc/5_FAQ.markdown)
|
|
||||||
|
|
||||||
Component documentation:
|
Component documentation:
|
||||||
|
|
||||||
|
* [Walking the AST](doc/component/Walking_the_AST.markdown)
|
||||||
|
* Node visitors
|
||||||
|
* Modifying the AST from a visitor
|
||||||
|
* Short-circuiting traversals
|
||||||
|
* Interleaved visitors
|
||||||
|
* Simple node finding API
|
||||||
|
* Parent and sibling references
|
||||||
* [Name resolution](doc/component/Name_resolution.markdown)
|
* [Name resolution](doc/component/Name_resolution.markdown)
|
||||||
|
* Name resolver options
|
||||||
|
* Name resolution context
|
||||||
* [Pretty printing](doc/component/Pretty_printing.markdown)
|
* [Pretty printing](doc/component/Pretty_printing.markdown)
|
||||||
|
* Converting AST back to PHP code
|
||||||
|
* Customizing formatting
|
||||||
|
* Formatting-preserving code transformations
|
||||||
|
* [AST builders](component/AST_builders.markdown)
|
||||||
|
* Fluent builders for AST nodes
|
||||||
* [Lexer](doc/component/Lexer.markdown)
|
* [Lexer](doc/component/Lexer.markdown)
|
||||||
|
* Lexer options
|
||||||
|
* Token and file positions for nodes
|
||||||
|
* Custom attributes
|
||||||
* [Error handling](doc/component/Error_handling.markdown)
|
* [Error handling](doc/component/Error_handling.markdown)
|
||||||
|
* Column information for errors
|
||||||
|
* Error recovery (parsing of syntactically incorrect code)
|
||||||
|
* [Constant expression evaluation](component/Constant_expression_evaluation.markdown)
|
||||||
|
* Evaluating constant/property/etc initializers
|
||||||
|
* Handling errors and unsupported expressions
|
||||||
|
* [JSON representation](component/JSON_representation.markdown)
|
||||||
|
* JSON encoding and decoding of ASTs
|
||||||
|
* [Performance](doc/component/Performance.markdown)
|
||||||
|
* Disabling XDebug
|
||||||
|
* Reusing objects
|
||||||
|
* Garbage collection impact
|
||||||
|
* [Frequently asked questions](component/FAQ.markdown)
|
||||||
|
* Parent and sibling references
|
||||||
|
|
||||||
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
|
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
|
||||||
[doc_master]: https://github.com/nikic/PHP-Parser/tree/master/doc
|
[doc_master]: https://github.com/nikic/PHP-Parser/tree/master/doc
|
||||||
|
@ -4,7 +4,7 @@ Upgrading from PHP-Parser 2.x to 3.0
|
|||||||
The backwards-incompatible changes in this release may be summarized as follows:
|
The backwards-incompatible changes in this release may be summarized as follows:
|
||||||
|
|
||||||
* The specific details of the node representation have changed in some cases, primarily to
|
* The specific details of the node representation have changed in some cases, primarily to
|
||||||
accomodate new PHP 7.1 features.
|
accommodate new PHP 7.1 features.
|
||||||
* There have been significant changes to the error recovery implementation. This may affect you,
|
* There have been significant changes to the error recovery implementation. This may affect you,
|
||||||
if you used the error recovery mode or have a custom lexer implementation.
|
if you used the error recovery mode or have a custom lexer implementation.
|
||||||
* A number of deprecated methods were removed.
|
* A number of deprecated methods were removed.
|
||||||
@ -115,7 +115,7 @@ public function startLexing($code);
|
|||||||
public function startLexing($code, ErrorHandler $errorHandler = null);
|
public function startLexing($code, ErrorHandler $errorHandler = null);
|
||||||
```
|
```
|
||||||
|
|
||||||
If you use a custom lexer with overriden `startLexing()` method, it needs to be changed to accept
|
If you use a custom lexer with overridden `startLexing()` method, it needs to be changed to accept
|
||||||
the extra parameter. The value should be passed on to the parent method.
|
the extra parameter. The value should be passed on to the parent method.
|
||||||
|
|
||||||
#### Error checks in node constructors
|
#### Error checks in node constructors
|
||||||
@ -148,7 +148,7 @@ The following methods, arguments or options have been removed:
|
|||||||
namespace into fully qualified names. For example `foo()` in the global namespace resolves to
|
namespace into fully qualified names. For example `foo()` in the global namespace resolves to
|
||||||
`\foo()`. For names where no static resolution is possible, a `namespacedName` attribute is
|
`\foo()`. For names where no static resolution is possible, a `namespacedName` attribute is
|
||||||
added now, containing the namespaced variant of the name.
|
added now, containing the namespaced variant of the name.
|
||||||
* All methods on `PrettyPrinter\Standard` are now protected. Previoulsy most of them were public.
|
* All methods on `PrettyPrinter\Standard` are now protected. Previously most of them were public.
|
||||||
The pretty printer should only be invoked using the `prettyPrint()`, `prettyPrintFile()` and
|
The pretty printer should only be invoked using the `prettyPrint()`, `prettyPrintFile()` and
|
||||||
`prettyPrintExpr()` methods.
|
`prettyPrintExpr()` methods.
|
||||||
* The node dumper now prints numeric values that act as enums/flags in a string representation.
|
* The node dumper now prints numeric values that act as enums/flags in a string representation.
|
||||||
|
@ -6,7 +6,7 @@ Upgrading from PHP-Parser 3.x to 4.0
|
|||||||
PHP-Parser now requires PHP 7.0 or newer to run. It is however still possible to *parse* PHP 5.2-5.6
|
PHP-Parser now requires PHP 7.0 or newer to run. It is however still possible to *parse* PHP 5.2-5.6
|
||||||
source code, while running on a newer version.
|
source code, while running on a newer version.
|
||||||
|
|
||||||
Because HHVM does not support PHP 7, HHVM is no longer supported.
|
HHVM is no longer actively supported.
|
||||||
|
|
||||||
### Changes to the node structure
|
### Changes to the node structure
|
||||||
|
|
||||||
@ -52,12 +52,12 @@ Because HHVM does not support PHP 7, HHVM is no longer supported.
|
|||||||
* The `alias` subnode of `UseUse` is now `null` if no explicit alias is given. As such,
|
* The `alias` subnode of `UseUse` is now `null` if no explicit alias is given. As such,
|
||||||
`use Foo\Bar` and `use Foo\Bar as Bar` are now represented differently. The `getAlias()` method
|
`use Foo\Bar` and `use Foo\Bar as Bar` are now represented differently. The `getAlias()` method
|
||||||
can be used to get the effective alias, even if it is not explicitly given.
|
can be used to get the effective alias, even if it is not explicitly given.
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
* The indenentation handling in the pretty printer has been changed (this is only relevant if you
|
* The indentation handling in the pretty printer has been changed (this is only relevant if you
|
||||||
extend the pretty printer). Previously indentation was automatic, and parts were excluded using
|
extend the pretty printer). Previously indentation was automatic, and parts were excluded using
|
||||||
`pNoindent()`. Now no-indent is the default and newlins that require indentation should use
|
`pNoindent()`. Now no-indent is the default and newlines that require indentation should use
|
||||||
`$this->nl`.
|
`$this->nl`.
|
||||||
|
|
||||||
### Removed functionality
|
### Removed functionality
|
||||||
@ -73,4 +73,5 @@ Because HHVM does not support PHP 7, HHVM is no longer supported.
|
|||||||
* The XML serializer has been removed. As such, the classes `Serializer\XML`, and
|
* The XML serializer has been removed. As such, the classes `Serializer\XML`, and
|
||||||
`Unserializer\XML`, as well as the interfaces `Serializer` and `Unserializer` no longer exist.
|
`Unserializer\XML`, as well as the interfaces `Serializer` and `Unserializer` no longer exist.
|
||||||
* The `BuilderAbstract` class has been removed. It's functionality is moved into `BuilderHelpers`.
|
* The `BuilderAbstract` class has been removed. It's functionality is moved into `BuilderHelpers`.
|
||||||
However, this is an internal class and should not be used directly.
|
However, this is an internal class and should not be used directly.
|
||||||
|
* The `Autoloader` class has been removed in favor of relying on the Composer autoloader.
|
@ -81,6 +81,9 @@ foreach ($files as $file) {
|
|||||||
} elseif ('pretty-print' === $operation) {
|
} elseif ('pretty-print' === $operation) {
|
||||||
echo "==> Pretty print:\n";
|
echo "==> Pretty print:\n";
|
||||||
echo $prettyPrinter->prettyPrintFile($stmts), "\n";
|
echo $prettyPrinter->prettyPrintFile($stmts), "\n";
|
||||||
|
} elseif ('json-dump' === $operation) {
|
||||||
|
echo "==> JSON dump:\n";
|
||||||
|
echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";
|
||||||
} elseif ('var-dump' === $operation) {
|
} elseif ('var-dump' === $operation) {
|
||||||
echo "==> var_dump():\n";
|
echo "==> var_dump():\n";
|
||||||
var_dump($stmts);
|
var_dump($stmts);
|
||||||
@ -112,6 +115,7 @@ Operations is a list of the following options (--dump by default):
|
|||||||
|
|
||||||
-d, --dump Dump nodes using NodeDumper
|
-d, --dump Dump nodes using NodeDumper
|
||||||
-p, --pretty-print Pretty print file using PrettyPrinter\Standard
|
-p, --pretty-print Pretty print file using PrettyPrinter\Standard
|
||||||
|
-j, --json-dump Print json_encode() result
|
||||||
--var-dump var_dump() nodes (for exact structure)
|
--var-dump var_dump() nodes (for exact structure)
|
||||||
-N, --resolve-names Resolve names using NodeVisitor\NameResolver
|
-N, --resolve-names Resolve names using NodeVisitor\NameResolver
|
||||||
-c, --with-column-info Show column-numbers for errors (if available)
|
-c, --with-column-info Show column-numbers for errors (if available)
|
||||||
@ -155,8 +159,9 @@ function parseArgs($args) {
|
|||||||
case '-p':
|
case '-p':
|
||||||
$operations[] = 'pretty-print';
|
$operations[] = 'pretty-print';
|
||||||
break;
|
break;
|
||||||
case '--serialize-xml':
|
case '--json-dump':
|
||||||
$operations[] = 'serialize-xml';
|
case '-j':
|
||||||
|
$operations[] = 'json-dump';
|
||||||
break;
|
break;
|
||||||
case '--var-dump':
|
case '--var-dump':
|
||||||
$operations[] = 'var-dump';
|
$operations[] = 'var-dump';
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
"ext-tokenizer": "*"
|
"ext-tokenizer": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^6"
|
"phpunit/phpunit": "^6.5 || ^7.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
This project is a PHP 5.2 to PHP 7.1 parser **written in PHP itself**.
|
This project is a PHP 5.2 to PHP 7.2 parser **written in PHP itself**.
|
||||||
|
|
||||||
What is this for?
|
What is this for?
|
||||||
-----------------
|
-----------------
|
||||||
@ -14,7 +14,7 @@ There are other ways of processing source code. One that PHP supports natively i
|
|||||||
token stream generated by [`token_get_all`][2]. The token stream is much more low level than
|
token stream generated by [`token_get_all`][2]. The token stream is much more low level than
|
||||||
the AST and thus has different applications: It allows to also analyze the exact formatting of
|
the AST and thus has different applications: It allows to also analyze the exact formatting of
|
||||||
a file. On the other hand the token stream is much harder to deal with for more complex analysis.
|
a file. On the other hand the token stream is much harder to deal with for more complex analysis.
|
||||||
For example an AST abstracts away the fact that in PHP variables can be written as `$foo`, but also
|
For example, an AST abstracts away the fact that, in PHP, variables can be written as `$foo`, but also
|
||||||
as `$$bar`, `${'foobar'}` or even `${!${''}=barfoo()}`. You don't have to worry about recognizing
|
as `$$bar`, `${'foobar'}` or even `${!${''}=barfoo()}`. You don't have to worry about recognizing
|
||||||
all the different syntaxes from a stream of tokens.
|
all the different syntaxes from a stream of tokens.
|
||||||
|
|
||||||
@ -26,17 +26,17 @@ 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-5.6 and PHP 7.
|
The parser supports parsing PHP 5.2-7.2.
|
||||||
|
|
||||||
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.1 source code running on PHP 5.5, for example. This emulation is somewhat
|
This allows to parse PHP 7.2 source code running on PHP 5.5, 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?
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
The parser produces an [Abstract Syntax Tree][1] (AST) also known as a node tree. How this looks like
|
The parser produces an [Abstract Syntax Tree][1] (AST) also known as a node tree. How this looks
|
||||||
can best be seen in an example. The program `<?php echo 'Hi', 'World';` will give you a node tree
|
can best be seen in an example. The program `<?php echo 'Hi', 'World';` will give you a node tree
|
||||||
roughly looking like this:
|
roughly looking like this:
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ Kind | Behavior
|
|||||||
`ParserFactory::ONLY_PHP7` | Parse code as PHP 7.
|
`ParserFactory::ONLY_PHP7` | Parse code as PHP 7.
|
||||||
`ParserFactory::ONLY_PHP5` | Parse code as PHP 5.
|
`ParserFactory::ONLY_PHP5` | Parse code as PHP 5.
|
||||||
|
|
||||||
Unless you have strong reason to use something else, `PREFER_PHP7` is a reasonable default.
|
Unless you have a strong reason to use something else, `PREFER_PHP7` is a reasonable default.
|
||||||
|
|
||||||
The `create()` method optionally accepts a `Lexer` instance as the second argument. Some use cases
|
The `create()` method optionally accepts a `Lexer` instance as the second argument. Some use cases
|
||||||
that require customized lexers are discussed in the [lexer documentation](component/Lexer.markdown).
|
that require customized lexers are discussed in the [lexer documentation](component/Lexer.markdown).
|
||||||
@ -50,10 +50,18 @@ Subsequently you can pass PHP code (including the opening `<?php` tag) to the `p
|
|||||||
create a syntax tree. If a syntax error is encountered, an `PhpParser\Error` exception will be thrown:
|
create a syntax tree. If a syntax error is encountered, an `PhpParser\Error` exception will be thrown:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
<?php
|
||||||
use PhpParser\Error;
|
use PhpParser\Error;
|
||||||
use PhpParser\ParserFactory;
|
use PhpParser\ParserFactory;
|
||||||
|
|
||||||
$code = '<?php // some code';
|
$code = <<<'CODE'
|
||||||
|
<?php
|
||||||
|
function printLine($msg) {
|
||||||
|
echo $msg, "\n";
|
||||||
|
}
|
||||||
|
printLine('Hello World!!!');
|
||||||
|
CODE;
|
||||||
|
|
||||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -66,27 +74,68 @@ try {
|
|||||||
|
|
||||||
A parser instance can be reused to parse multiple files.
|
A parser instance can be reused to parse multiple files.
|
||||||
|
|
||||||
Node tree
|
Node dumping
|
||||||
---------
|
------------
|
||||||
|
|
||||||
If you use the above code with `$code = "<?php echo 'Hi ', hi\\getTarget();"` the parser will
|
To dump the abstact syntax tree in human readable form, a `NodeDumper` can be used:
|
||||||
generate a node tree looking like this:
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
use PhpParser\NodeDumper;
|
||||||
|
|
||||||
|
$nodeDumper = new NodeDumper;
|
||||||
|
echo $nodeDumper->dump($stmts), "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
For the sample code from the previous section, this will produce the following output:
|
||||||
|
|
||||||
```
|
```
|
||||||
array(
|
array(
|
||||||
0: Stmt_Echo(
|
0: Stmt_Function(
|
||||||
exprs: array(
|
byRef: false
|
||||||
0: Scalar_String(
|
name: Identifier(
|
||||||
value: Hi
|
name: printLine
|
||||||
|
)
|
||||||
|
params: array(
|
||||||
|
0: Param(
|
||||||
|
type: null
|
||||||
|
byRef: false
|
||||||
|
variadic: false
|
||||||
|
var: Expr_Variable(
|
||||||
|
name: msg
|
||||||
|
)
|
||||||
|
default: null
|
||||||
)
|
)
|
||||||
1: Expr_FuncCall(
|
)
|
||||||
name: Name(
|
returnType: null
|
||||||
parts: array(
|
stmts: array(
|
||||||
0: hi
|
0: Stmt_Echo(
|
||||||
1: getTarget
|
exprs: array(
|
||||||
|
0: Expr_Variable(
|
||||||
|
name: msg
|
||||||
|
)
|
||||||
|
1: Scalar_String(
|
||||||
|
value:
|
||||||
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
args: array(
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
parts: array(
|
||||||
|
0: printLine
|
||||||
|
)
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
value: Scalar_String(
|
||||||
|
value: Hello World!!!
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -94,10 +143,30 @@ array(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Thus `$stmts` will contain an array with only one node, with this node being an instance of
|
You can also use the `php-parse` script to obtain such a node dump by calling it either with a file
|
||||||
`PhpParser\Node\Stmt\Echo_`.
|
name or code string:
|
||||||
|
|
||||||
As PHP is a large language there are approximately 140 different nodes. In order to make work
|
```sh
|
||||||
|
vendor/bin/php-parse file.php
|
||||||
|
vendor/bin/php-parse "<?php foo();"
|
||||||
|
```
|
||||||
|
|
||||||
|
This can be very helpful if you want to quickly check how certain syntax is represented in the AST.
|
||||||
|
|
||||||
|
Node tree structure
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Looking at the node dump above, you can see that `$stmts` for this example code is an array of two
|
||||||
|
nodes, a `Stmt_Function` and a `Stmt_Expression`. The corresponding class names are:
|
||||||
|
|
||||||
|
* `Stmt_Function -> PhpParser\Node\Stmt\Function_`
|
||||||
|
* `Stmt_Expression -> PhpParser\Node\Stmt\Expression`
|
||||||
|
|
||||||
|
The additional `_` at the end of the first class name is necessary, because `Function` is a
|
||||||
|
reserved keyword. Many node class names in this library have a trailing `_` to avoid clashing with
|
||||||
|
a keyword.
|
||||||
|
|
||||||
|
As PHP is a large language there are approximately 140 different nodes. In order to make working
|
||||||
with them easier they are grouped into three categories:
|
with them easier they are grouped into three categories:
|
||||||
|
|
||||||
* `PhpParser\Node\Stmt`s are statement nodes, i.e. language constructs that do not return
|
* `PhpParser\Node\Stmt`s are statement nodes, i.e. language constructs that do not return
|
||||||
@ -113,8 +182,9 @@ with them easier they are grouped into three categories:
|
|||||||
* There are some nodes not in either of these groups, for example names (`PhpParser\Node\Name`)
|
* There are some nodes not in either of these groups, for example names (`PhpParser\Node\Name`)
|
||||||
and call arguments (`PhpParser\Node\Arg`).
|
and call arguments (`PhpParser\Node\Arg`).
|
||||||
|
|
||||||
Some node class names have a trailing `_`. This is used whenever the class name would otherwise clash
|
The `Node\Stmt\Expression` node is somewhat confusing in that it contains both the terms "statement"
|
||||||
with a PHP keyword.
|
and "expression". This node distinguishes `expr`, which is a `Node\Expr`, from `expr;`, which is
|
||||||
|
an "expression statement" represented by `Node\Stmt\Expression` and containing `expr` as a sub-node.
|
||||||
|
|
||||||
Every node has a (possibly zero) number of subnodes. You can access subnodes by writing
|
Every node has a (possibly zero) number of subnodes. You can access subnodes by writing
|
||||||
`$node->subNodeName`. The `Stmt\Echo_` node has only one subnode `exprs`. So in order to access it
|
`$node->subNodeName`. The `Stmt\Echo_` node has only one subnode `exprs`. So in order to access it
|
||||||
@ -173,7 +243,7 @@ try {
|
|||||||
|
|
||||||
The above code will output:
|
The above code will output:
|
||||||
|
|
||||||
<?php echo 'Hello ', hi\getTarget();
|
echo 'Hello ', hi\getTarget();
|
||||||
|
|
||||||
As you can see the source code was first parsed using `PhpParser\Parser->parse()`, then changed and then
|
As you can see the source code was first parsed using `PhpParser\Parser->parse()`, then changed and then
|
||||||
again converted to code using `PhpParser\PrettyPrinter\Standard->prettyPrint()`.
|
again converted to code using `PhpParser\PrettyPrinter\Standard->prettyPrint()`.
|
||||||
@ -184,6 +254,8 @@ single expression using `prettyPrintExpr()`.
|
|||||||
The `prettyPrintFile()` method can be used to print an entire file. This will include the opening `<?php` tag
|
The `prettyPrintFile()` method can be used to print an entire file. This will include the opening `<?php` tag
|
||||||
and handle inline HTML as the first/last statement more gracefully.
|
and handle inline HTML as the first/last statement more gracefully.
|
||||||
|
|
||||||
|
> Read more: [Pretty printing documentation](component/Pretty_printing.markdown)
|
||||||
|
|
||||||
Node traversation
|
Node traversation
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
@ -278,10 +350,12 @@ be `array(A, X, Y, Z, C)`.
|
|||||||
Instead of manually implementing the `NodeVisitor` interface you can also extend the `NodeVisitorAbstract`
|
Instead of manually implementing the `NodeVisitor` interface you can also extend the `NodeVisitorAbstract`
|
||||||
class, which will define empty default implementations for all the above methods.
|
class, which will define empty default implementations for all the above methods.
|
||||||
|
|
||||||
|
> Read more: [Walking the AST](component/Walking_the_AST.markdown)
|
||||||
|
|
||||||
The NameResolver node visitor
|
The NameResolver node visitor
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
One visitor is already bundled with the package: `PhpParser\NodeVisitor\NameResolver`. This visitor
|
One visitor that is already bundled with the package is `PhpParser\NodeVisitor\NameResolver`. This visitor
|
||||||
helps you work with namespaced code by trying to resolve most names to fully qualified ones.
|
helps you work with namespaced code by trying to resolve most names to fully qualified ones.
|
||||||
|
|
||||||
For example, consider the following code:
|
For example, consider the following code:
|
||||||
@ -292,7 +366,7 @@ For example, consider the following code:
|
|||||||
In order to know that `B\C` really is `A\C` you would need to track aliases and namespaces yourself.
|
In order to know that `B\C` really is `A\C` you would need to track aliases and namespaces yourself.
|
||||||
The `NameResolver` takes care of that and resolves names as far as possible.
|
The `NameResolver` takes care of that and resolves names as far as possible.
|
||||||
|
|
||||||
After running it most names will be fully qualified. The only names that will stay unqualified are
|
After running it, most names will be fully qualified. The only names that will stay unqualified are
|
||||||
unqualified function and constant names. These are resolved at runtime and thus the visitor can't
|
unqualified function and constant names. These are resolved at runtime and thus the visitor can't
|
||||||
know which function they are referring to. In most cases this is a non-issue as the global functions
|
know which function they are referring to. In most cases this is a non-issue as the global functions
|
||||||
are meant.
|
are meant.
|
||||||
@ -300,6 +374,8 @@ are meant.
|
|||||||
Also the `NameResolver` adds a `namespacedName` subnode to class, function and constant declarations
|
Also the `NameResolver` adds a `namespacedName` subnode to class, function and constant declarations
|
||||||
that contains the namespaced name instead of only the shortname that is available via `name`.
|
that contains the namespaced name instead of only the shortname that is available via `name`.
|
||||||
|
|
||||||
|
> Read more: [Name resolution documentation](component/Name_resolution.markdown)
|
||||||
|
|
||||||
Example: Converting namespaced code to pseudo namespaces
|
Example: Converting namespaced code to pseudo namespaces
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
@ -429,7 +505,7 @@ class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract
|
|||||||
// returning an array merges is into the parent array
|
// returning an array merges is into the parent array
|
||||||
return $node->stmts;
|
return $node->stmts;
|
||||||
} elseif ($node instanceof Stmt\Use_) {
|
} elseif ($node instanceof Stmt\Use_) {
|
||||||
// return use nodes altogether
|
// remove use nodes altogether
|
||||||
return NodeTraverser::REMOVE_NODE;
|
return NodeTraverser::REMOVE_NODE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,228 +0,0 @@
|
|||||||
Other node tree representations
|
|
||||||
===============================
|
|
||||||
|
|
||||||
It is possible to convert the AST into several textual representations, which serve different uses.
|
|
||||||
|
|
||||||
Simple serialization
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
It is possible to serialize the node tree using `serialize()` and also unserialize it using
|
|
||||||
`unserialize()`. The output is not human readable and not easily processable from anything
|
|
||||||
but PHP, but it is compact and generates quickly. The main application thus is in caching.
|
|
||||||
|
|
||||||
Human readable dumping
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Furthermore it is possible to dump nodes into a human readable format using the `dump` method of
|
|
||||||
`PhpParser\NodeDumper`. This can be used for debugging.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$code = <<<'CODE'
|
|
||||||
<?php
|
|
||||||
|
|
||||||
function printLine($msg) {
|
|
||||||
echo $msg, "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
printLine('Hello World!!!');
|
|
||||||
CODE;
|
|
||||||
|
|
||||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7);
|
|
||||||
$nodeDumper = new PhpParser\NodeDumper;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$stmts = $parser->parse($code);
|
|
||||||
|
|
||||||
echo $nodeDumper->dump($stmts), "\n";
|
|
||||||
} catch (PhpParser\Error $e) {
|
|
||||||
echo 'Parse Error: ', $e->getMessage();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The above script will have an output looking roughly like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
array(
|
|
||||||
0: Stmt_Function(
|
|
||||||
byRef: false
|
|
||||||
params: array(
|
|
||||||
0: Param(
|
|
||||||
name: msg
|
|
||||||
default: null
|
|
||||||
type: null
|
|
||||||
byRef: false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
stmts: array(
|
|
||||||
0: Stmt_Echo(
|
|
||||||
exprs: array(
|
|
||||||
0: Expr_Variable(
|
|
||||||
name: msg
|
|
||||||
)
|
|
||||||
1: Scalar_String(
|
|
||||||
value:
|
|
||||||
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
name: printLine
|
|
||||||
)
|
|
||||||
1: Expr_FuncCall(
|
|
||||||
name: Name(
|
|
||||||
parts: array(
|
|
||||||
0: printLine
|
|
||||||
)
|
|
||||||
)
|
|
||||||
args: array(
|
|
||||||
0: Arg(
|
|
||||||
value: Scalar_String(
|
|
||||||
value: Hello World!!!
|
|
||||||
)
|
|
||||||
byRef: false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
JSON encoding
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Nodes (and comments) implement the `JsonSerializable` interface. As such, it is possible to JSON
|
|
||||||
encode the AST directly using `json_encode()`:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$code = <<<'CODE'
|
|
||||||
<?php
|
|
||||||
|
|
||||||
function printLine($msg) {
|
|
||||||
echo $msg, "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
printLine('Hello World!!!');
|
|
||||||
CODE;
|
|
||||||
|
|
||||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$stmts = $parser->parse($code);
|
|
||||||
|
|
||||||
echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";
|
|
||||||
} catch (PhpParser\Error $e) {
|
|
||||||
echo 'Parse Error: ', $e->getMessage();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This will result in the following output (which includes attributes):
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"nodeType": "Stmt_Function",
|
|
||||||
"byRef": false,
|
|
||||||
"name": "printLine",
|
|
||||||
"params": [
|
|
||||||
{
|
|
||||||
"nodeType": "Param",
|
|
||||||
"type": null,
|
|
||||||
"byRef": false,
|
|
||||||
"variadic": false,
|
|
||||||
"name": "msg",
|
|
||||||
"default": null,
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 3,
|
|
||||||
"endLine": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"returnType": null,
|
|
||||||
"stmts": [
|
|
||||||
{
|
|
||||||
"nodeType": "Stmt_Echo",
|
|
||||||
"exprs": [
|
|
||||||
{
|
|
||||||
"nodeType": "Expr_Variable",
|
|
||||||
"name": "msg",
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 4,
|
|
||||||
"endLine": 4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeType": "Scalar_String",
|
|
||||||
"value": "\n",
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 4,
|
|
||||||
"endLine": 4,
|
|
||||||
"kind": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 4,
|
|
||||||
"endLine": 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 3,
|
|
||||||
"endLine": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeType": "Expr_FuncCall",
|
|
||||||
"name": {
|
|
||||||
"nodeType": "Name",
|
|
||||||
"parts": [
|
|
||||||
"printLine"
|
|
||||||
],
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 7,
|
|
||||||
"endLine": 7
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"args": [
|
|
||||||
{
|
|
||||||
"nodeType": "Arg",
|
|
||||||
"value": {
|
|
||||||
"nodeType": "Scalar_String",
|
|
||||||
"value": "Hello World!!!",
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 7,
|
|
||||||
"endLine": 7,
|
|
||||||
"kind": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"byRef": false,
|
|
||||||
"unpack": false,
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 7,
|
|
||||||
"endLine": 7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"attributes": {
|
|
||||||
"startLine": 7,
|
|
||||||
"endLine": 7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
The JSON representation may be converted back into a node tree using the `JsonDecoder`:
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$nodeDecoder = new PhpParser\NodeDecoder();
|
|
||||||
$ast = $nodeDecoder->decode($json);
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that not all ASTs can be represented using JSON. In particular:
|
|
||||||
|
|
||||||
* JSON only supports UTF-8 strings.
|
|
||||||
* JSON does not support non-finite floating-point numbers. This can occur if the original source
|
|
||||||
code contains non-representable floating-pointing literals such as `1e1000`.
|
|
||||||
|
|
||||||
If the node tree is not representable in JSON, the initial `json_encode()` call will fail.
|
|
@ -6,13 +6,17 @@ Guide
|
|||||||
|
|
||||||
1. [Introduction](0_Introduction.markdown)
|
1. [Introduction](0_Introduction.markdown)
|
||||||
2. [Usage of basic components](2_Usage_of_basic_components.markdown)
|
2. [Usage of basic components](2_Usage_of_basic_components.markdown)
|
||||||
3. [Other node tree representations](3_Other_node_tree_representations.markdown)
|
|
||||||
4. [Code generation](4_Code_generation.markdown)
|
|
||||||
5. [Frequently asked questions](5_FAQ.markdown)
|
|
||||||
|
|
||||||
Component documentation
|
Component documentation
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
* [Walking the AST](component/Walking_the_AST.markdown)
|
||||||
|
* Node visitors
|
||||||
|
* Modifying the AST from a visitor
|
||||||
|
* Short-circuiting traversals
|
||||||
|
* Interleaved visitors
|
||||||
|
* Simple node finding API
|
||||||
|
* Parent and sibling references
|
||||||
* [Name resolution](component/Name_resolution.markdown)
|
* [Name resolution](component/Name_resolution.markdown)
|
||||||
* Name resolver options
|
* Name resolver options
|
||||||
* Name resolution context
|
* Name resolution context
|
||||||
@ -20,6 +24,8 @@ Component documentation
|
|||||||
* Converting AST back to PHP code
|
* Converting AST back to PHP code
|
||||||
* Customizing formatting
|
* Customizing formatting
|
||||||
* Formatting-preserving code transformations
|
* Formatting-preserving code transformations
|
||||||
|
* [AST builders](component/AST_builders.markdown)
|
||||||
|
* Fluent builders for AST nodes
|
||||||
* [Lexer](component/Lexer.markdown)
|
* [Lexer](component/Lexer.markdown)
|
||||||
* Lexer options
|
* Lexer options
|
||||||
* Token and file positions for nodes
|
* Token and file positions for nodes
|
||||||
@ -27,3 +33,14 @@ Component documentation
|
|||||||
* [Error handling](component/Error_handling.markdown)
|
* [Error handling](component/Error_handling.markdown)
|
||||||
* Column information for errors
|
* Column information for errors
|
||||||
* Error recovery (parsing of syntactically incorrect code)
|
* Error recovery (parsing of syntactically incorrect code)
|
||||||
|
* [Constant expression evaluation](component/Constant_expression_evaluation.markdown)
|
||||||
|
* Evaluating constant/property/etc initializers
|
||||||
|
* Handling errors and unsupported expressions
|
||||||
|
* [JSON representation](component/JSON_representation.markdown)
|
||||||
|
* JSON encoding and decoding of ASTs
|
||||||
|
* [Performance](component/Performance.markdown)
|
||||||
|
* Disabling XDebug
|
||||||
|
* Reusing objects
|
||||||
|
* Garbage collection impact
|
||||||
|
* [Frequently asked questions](component/FAQ.markdown)
|
||||||
|
* Parent and sibling references
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
Code generation
|
AST builders
|
||||||
===============
|
============
|
||||||
|
|
||||||
It is also possible to generate code using the parser, by first creating an Abstract Syntax Tree and then using the
|
When PHP-Parser is used to generate (or modify) code by first creating an Abstract Syntax Tree and
|
||||||
pretty printer to convert it to PHP code. To simplify code generation, the project comes with builders which allow
|
then using the [pretty printer](Pretty_printing.markdown) to convert it to PHP code, it can often
|
||||||
creating node trees using a fluid interface, instead of instantiating all nodes manually. Builders are available for
|
be tedious to manually construct AST nodes. The project provides a number of utilities to simplify
|
||||||
|
the construction of common AST nodes.
|
||||||
|
|
||||||
|
Fluent builders
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The library comes with a number of builders, which allow creating node trees using a fluent
|
||||||
|
interface. Builders are created using the `BuilderFactory` and the final constructed node is
|
||||||
|
accessed through `getNode()`. Fluent builders are available for
|
||||||
the following syntactic elements:
|
the following syntactic elements:
|
||||||
|
|
||||||
* namespaces and use statements
|
* namespaces and use statements
|
||||||
@ -82,3 +90,17 @@ abstract class SomeOtherClass extends SomeClass implements A\Few, \Interfaces
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Additional helper methods
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The `BuilderFactory` also provides a number of additional helper methods, which directly return
|
||||||
|
nodes. The following methods are currently available:
|
||||||
|
|
||||||
|
* `val($value)`: Creates an AST node for a literal value like `42` or `[1, 2, 3]`.
|
||||||
|
* `args(array $args)`: Creates an array of function/method arguments, including the required `Arg`
|
||||||
|
wrappers. Also converts literals to AST nodes.
|
||||||
|
* `concat(...$exprs)`: Create a tree of `BinaryOp\Concat` nodes for the given expressions.
|
||||||
|
|
||||||
|
These methods may be expanded on an as-needed basis. Please open an issue or PR if a common
|
||||||
|
operation is missing.
|
115
doc/component/Constant_expression_evaluation.markdown
Normal file
115
doc/component/Constant_expression_evaluation.markdown
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
Constant expression evaluation
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Initializers for constants, properties, parameters, etc. have limited support for expressions. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
class Test {
|
||||||
|
const SECONDS_IN_HOUR = 60 * 60;
|
||||||
|
const SECONDS_IN_DAY = 24 * self::SECONDS_IN_HOUR;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
PHP-Parser supports evaluation of such constant expressions through the `ConstExprEvaluator` class:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpParser\{ConstExprEvaluator, ConstExprEvaluationException};
|
||||||
|
|
||||||
|
$evalutator = new ConstExprEvaluator();
|
||||||
|
try {
|
||||||
|
$value = $evalutator->evaluateSilently($someExpr);
|
||||||
|
} catch (ConstExprEvaluationException $e) {
|
||||||
|
// Either the expression contains unsupported expression types,
|
||||||
|
// or an error occurred during evaluation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Error handling
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The constant evaluator provides two methods, `evaluateDirectly()` and `evaluateSilently()`, which
|
||||||
|
differ in error behavior. `evaluateDirectly()` will evaluate the expression as PHP would, including
|
||||||
|
any generated warnings or Errors. `evaluateSilently()` will instead convert warnings and Errors into
|
||||||
|
a `ConstExprEvaluationException`. For example:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpParser\{ConstExprEvaluator, ConstExprEvaluationException};
|
||||||
|
use PhpParser\Node\{Expr, Scalar};
|
||||||
|
|
||||||
|
$evaluator = new ConstExprEvaluator();
|
||||||
|
|
||||||
|
// 10 / 0
|
||||||
|
$expr = new Expr\BinaryOp\Div(new Scalar\LNumber(10), new Scalar\LNumber(0));
|
||||||
|
|
||||||
|
var_dump($evaluator->evaluateDirectly($expr)); // float(INF)
|
||||||
|
// Warning: Division by zero
|
||||||
|
|
||||||
|
try {
|
||||||
|
$evaluator->evaluateSilently($expr);
|
||||||
|
} catch (ConstExprEvaluationException $e) {
|
||||||
|
var_dump($e->getPrevious()->getMessage()); // Division by zero
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For the purposes of static analysis, you will likely want to use `evaluateSilently()` and leave
|
||||||
|
erroring expressions unevaluated.
|
||||||
|
|
||||||
|
Unsupported expressions and evaluator fallback
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
The constant expression evaluator supports all expression types that are permitted in constant
|
||||||
|
expressions, apart from the following:
|
||||||
|
|
||||||
|
* `Scalar\MagicConst\*`
|
||||||
|
* `Expr\ConstFetch` (only null/false/true are handled)
|
||||||
|
* `Expr\ClassConstFetch`
|
||||||
|
|
||||||
|
Handling these expression types requires non-local information, such as which global constants are
|
||||||
|
defined. By default, the evaluator will throw a `ConstExprEvaluationException` when it encounters
|
||||||
|
an unsupported expression type.
|
||||||
|
|
||||||
|
It is possible to override this behavior and support resolution for these expression types by
|
||||||
|
specifying an evaluation fallback function:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpParser\{ConstExprEvaluator, ConstExprEvaluationException};
|
||||||
|
use PhpParser\Node\Expr;
|
||||||
|
|
||||||
|
$evalutator = new ConstExprEvaluator(function(Expr $expr) {
|
||||||
|
if ($expr instanceof Expr\ConstFetch) {
|
||||||
|
return fetchConstantSomehow($expr);
|
||||||
|
}
|
||||||
|
if ($expr instanceof Expr\ClassConstFetch) {
|
||||||
|
return fetchClassConstantSomehow($expr);
|
||||||
|
}
|
||||||
|
// etc.
|
||||||
|
throw new ConstExprEvaluationException(
|
||||||
|
"Expression of type {$expr->getType()} cannot be evaluated");
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
$evalutator->evaluateSilently($someExpr);
|
||||||
|
} catch (ConstExprEvaluationException $e) {
|
||||||
|
// Handle exception
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Implementers are advised to ensure that evaluation of indirect constant references cannot lead to
|
||||||
|
infinite recursion. For example, the following code could lead to infinite recursion if constant
|
||||||
|
lookup is implemented naively.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
class Test {
|
||||||
|
const A = self::B;
|
||||||
|
const B = self::A;
|
||||||
|
}
|
||||||
|
```
|
@ -27,7 +27,7 @@ try {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Before using column information its availability needs to be checked with `$e->hasColumnInfo()`, as the precise
|
Before using column information, its availability needs to be checked with `$e->hasColumnInfo()`, as the precise
|
||||||
location of an error cannot always be determined. The methods for retrieving column information also have to be passed
|
location of an error cannot always be determined. The methods for retrieving column information also have to be passed
|
||||||
the source code of the parsed file. An example for printing an error:
|
the source code of the parsed file. An example for printing an error:
|
||||||
|
|
||||||
|
131
doc/component/JSON_representation.markdown
Normal file
131
doc/component/JSON_representation.markdown
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
JSON representation
|
||||||
|
===================
|
||||||
|
|
||||||
|
Nodes (and comments) implement the `JsonSerializable` interface. As such, it is possible to JSON
|
||||||
|
encode the AST directly using `json_encode()`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpParser\ParserFactory;
|
||||||
|
|
||||||
|
$code = <<<'CODE'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @param string $msg */
|
||||||
|
function printLine($msg) {
|
||||||
|
echo $msg, "\n";
|
||||||
|
}
|
||||||
|
CODE;
|
||||||
|
|
||||||
|
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmts = $parser->parse($code);
|
||||||
|
|
||||||
|
echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";
|
||||||
|
} catch (PhpParser\Error $e) {
|
||||||
|
echo 'Parse Error: ', $e->getMessage();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will result in the following output (which includes attributes):
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"nodeType": "Stmt_Function",
|
||||||
|
"byRef": false,
|
||||||
|
"name": {
|
||||||
|
"nodeType": "Identifier",
|
||||||
|
"name": "printLine",
|
||||||
|
"attributes": {
|
||||||
|
"startLine": 4,
|
||||||
|
"endLine": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"nodeType": "Param",
|
||||||
|
"type": null,
|
||||||
|
"byRef": false,
|
||||||
|
"variadic": false,
|
||||||
|
"var": {
|
||||||
|
"nodeType": "Expr_Variable",
|
||||||
|
"name": "msg",
|
||||||
|
"attributes": {
|
||||||
|
"startLine": 4,
|
||||||
|
"endLine": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": null,
|
||||||
|
"attributes": {
|
||||||
|
"startLine": 4,
|
||||||
|
"endLine": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"returnType": null,
|
||||||
|
"stmts": [
|
||||||
|
{
|
||||||
|
"nodeType": "Stmt_Echo",
|
||||||
|
"exprs": [
|
||||||
|
{
|
||||||
|
"nodeType": "Expr_Variable",
|
||||||
|
"name": "msg",
|
||||||
|
"attributes": {
|
||||||
|
"startLine": 5,
|
||||||
|
"endLine": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nodeType": "Scalar_String",
|
||||||
|
"value": "\n",
|
||||||
|
"attributes": {
|
||||||
|
"startLine": 5,
|
||||||
|
"endLine": 5,
|
||||||
|
"kind": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributes": {
|
||||||
|
"startLine": 5,
|
||||||
|
"endLine": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributes": {
|
||||||
|
"startLine": 4,
|
||||||
|
"comments": [
|
||||||
|
{
|
||||||
|
"nodeType": "Comment_Doc",
|
||||||
|
"text": "\/** @param string $msg *\/",
|
||||||
|
"line": 3,
|
||||||
|
"filePos": 9,
|
||||||
|
"tokenPos": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"endLine": 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The JSON representation may be converted back into an AST using the `JsonDecoder`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$nodeDecoder = new PhpParser\NodeDecoder();
|
||||||
|
$ast = $nodeDecoder->decode($json);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that not all ASTs can be represented using JSON. In particular:
|
||||||
|
|
||||||
|
* JSON only supports UTF-8 strings.
|
||||||
|
* JSON does not support non-finite floating-point numbers. This can occur if the original source
|
||||||
|
code contains non-representable floating-pointing literals such as `1e1000`.
|
||||||
|
|
||||||
|
If the node tree is not representable in JSON, the initial `json_encode()` call will fail.
|
||||||
|
|
||||||
|
From the command line, a JSON dump can be obtained using `vendor/bin/php-parse -j file.php`.
|
@ -107,9 +107,9 @@ function handleHaltCompiler(): string;
|
|||||||
function getNextToken(string &$value = null, array &$startAttributes = null, array &$endAttributes = null): int;
|
function getNextToken(string &$value = null, array &$startAttributes = null, array &$endAttributes = null): int;
|
||||||
```
|
```
|
||||||
|
|
||||||
The `startLexing()` method is invoked with the source code that is to be lexed (including the opening tag) whenever the
|
The `startLexing()` method is invoked whenever the `parse()` method of the parser is called and is passed the source
|
||||||
`parse()` method of the parser is called. It can be used to reset state or preprocess the source code or tokens. The
|
code that is to be lexed (including the opening tag). It can be used to reset state or preprocess the source code or tokens. The
|
||||||
passes `ErrorHandler` should be used to report lexing errors.
|
passed `ErrorHandler` should be used to report lexing errors.
|
||||||
|
|
||||||
The `getTokens()` method returns the current token array, in the usual `token_get_all()` format. This method is not
|
The `getTokens()` method returns the current token array, in the usual `token_get_all()` format. This method is not
|
||||||
used by the parser (which uses `getNextToken()`), but is useful in combination with the token position attributes.
|
used by the parser (which uses `getNextToken()`), but is useful in combination with the token position attributes.
|
||||||
|
65
doc/component/Performance.markdown
Normal file
65
doc/component/Performance.markdown
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
Performance
|
||||||
|
===========
|
||||||
|
|
||||||
|
Parsing is computationally expensive task, to which the PHP language is not very well suited.
|
||||||
|
Nonetheless, there are a few things you can do to improve the performance of this library, which are
|
||||||
|
described in the following.
|
||||||
|
|
||||||
|
Xdebug
|
||||||
|
------
|
||||||
|
|
||||||
|
Running PHP with XDebug adds a lot of overhead, especially for code that performs many method calls.
|
||||||
|
Just by loading XDebug (without enabling profiling or other more intrusive XDebug features), you
|
||||||
|
can expect that code using PHP-Parser will be approximately *five times slower*.
|
||||||
|
|
||||||
|
As such, you should make sure that XDebug is not loaded when using this library. Note that setting
|
||||||
|
the `xdebug.default_enable=0` ini option does *not* disable XDebug. The *only* way to disable
|
||||||
|
XDebug is to not load the extension in the first place.
|
||||||
|
|
||||||
|
If you are building a command-line utility for use by developers (who often have XDebug enabled),
|
||||||
|
you may want to consider automatically restarting PHP with XDebug unloaded. See the composer
|
||||||
|
[XdebugHandler](https://github.com/composer/composer/blob/master/src/Composer/XdebugHandler.php)
|
||||||
|
for an implementation of such functionality.
|
||||||
|
|
||||||
|
If you do run with XDebug, you may need to increase the `xdebug.max_nesting_level` option to a
|
||||||
|
higher level, such as 3000. While the parser itself is recursion free, most other code working on
|
||||||
|
the AST uses recursion and will generate an error if the value of this option is too low.
|
||||||
|
|
||||||
|
Assertions
|
||||||
|
----------
|
||||||
|
|
||||||
|
Assertions should be disabled in a production context by setting `zend.assertions=-1` (or
|
||||||
|
`zend.assertions=0` if set at runtime). The library currently doesn't make heavy use of assertions,
|
||||||
|
but they are used in an increasing number of places.
|
||||||
|
|
||||||
|
Object reuse
|
||||||
|
------------
|
||||||
|
|
||||||
|
Many objects in this project are designed for reuse. For example, one `Parser` object can be used to
|
||||||
|
parse multiple files.
|
||||||
|
|
||||||
|
When possible, objects should be reused rather than being newly instantiated for every use. Some
|
||||||
|
objects have expensive initialization procedures, which will be unnecessarily repeated if the object
|
||||||
|
is not reused. (Currently two objects with particularly expensive setup are lexers and pretty
|
||||||
|
printers, though the details might change between versions of this library.)
|
||||||
|
|
||||||
|
Garbage collection
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A limitation in PHP's cyclic garbage collector may lead to major performance degradation when the
|
||||||
|
active working set exceeds 10000 objects (or arrays). Especially when parsing very large files this
|
||||||
|
limit is significantly exceeded and PHP will spend the majority of time performing unnecessary
|
||||||
|
garbage collection attempts.
|
||||||
|
|
||||||
|
Without GC, parsing time is roughly linear in the input size. With GC, this degenerates to quadratic
|
||||||
|
runtime for large files. While the specifics may differ, as a rough guideline you may expect a 2.5x
|
||||||
|
GC overhead for 500KB files and a 5x overhead for 1MB files.
|
||||||
|
|
||||||
|
Because this a limitation in PHP's implementation, there is no easy way to work around this. If
|
||||||
|
possible, you should avoid parsing very large files, as they will impact overall execution time
|
||||||
|
disproportionally (and are usually generated anyway).
|
||||||
|
|
||||||
|
Of course, you can also try to (temporarily) disable GC. By design the AST generated by PHP-Parser
|
||||||
|
is cycle-free, so the AST itself will never cause leaks with GC disabled. However, other code
|
||||||
|
(including for example the parser object itself) may hold cycles, so disabling of GC should be
|
||||||
|
approached with care.
|
@ -17,7 +17,7 @@ $stmts = $parser->parse($code);
|
|||||||
// MODIFY $stmts here
|
// MODIFY $stmts here
|
||||||
|
|
||||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||||
$newCode = $prettyPrinter->prettyPrintFile();
|
$newCode = $prettyPrinter->prettyPrintFile($stmts);
|
||||||
```
|
```
|
||||||
|
|
||||||
The pretty printer has three basic printing methods: `prettyPrint()`, `prettyPrintFile()` and
|
The pretty printer has three basic printing methods: `prettyPrint()`, `prettyPrintFile()` and
|
||||||
@ -50,14 +50,13 @@ Formatting-preserving pretty printing
|
|||||||
|
|
||||||
For automated code refactoring, migration and similar, you will usually only want to modify a small
|
For automated code refactoring, migration and similar, you will usually only want to modify a small
|
||||||
portion of the code and leave the remainder alone. The basic pretty printer is not suitable for
|
portion of the code and leave the remainder alone. The basic pretty printer is not suitable for
|
||||||
this, because it will also reformat parts of the code, which have not been modified.
|
this, because it will also reformat parts of the code which have not been modified.
|
||||||
|
|
||||||
Since PHP-Parser 4.0 an experimental formatting-preserving pretty-printing mode is available, which
|
Since PHP-Parser 4.0, an experimental formatting-preserving pretty-printing mode is available, which
|
||||||
attempts to preserve the formatting of code, those AST nodes have not changed, and only reformat
|
attempts to preserve the formatting of code (those AST nodes that have not changed) and only reformat
|
||||||
code which has been modified or newly inserted.
|
code which has been modified or newly inserted.
|
||||||
|
|
||||||
Use of the formatting-preservation functionality currently requires some additional preparatory
|
Use of the formatting-preservation functionality requires some additional preparatory steps:
|
||||||
steps:
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use PhpParser\{Lexer, NodeTraverser, NodeVisitor, Parser, PrettyPrinter};
|
use PhpParser\{Lexer, NodeTraverser, NodeVisitor, Parser, PrettyPrinter};
|
||||||
@ -86,7 +85,12 @@ $newStmts = $traverser->traverse($oldStmts);
|
|||||||
$newCode = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);
|
$newCode = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you make use of the name resolution functionality, you will likely want to disable the
|
||||||
|
`replaceNodes` option. This will add resolved names as attributes, instead of directlying modifying
|
||||||
|
the AST and causing spurious changes to the pretty printed code. For more information, see the
|
||||||
|
[name resolution documentation](Name_resolution.markdown).
|
||||||
|
|
||||||
This functionality is experimental and not yet fully implemented. It should not provide incorrect
|
This functionality is experimental and not yet fully implemented. It should not provide incorrect
|
||||||
code, but it may sometimes reformat more code than necessary. Open issues are tracked in
|
code, but it may sometimes reformat more code than necessary. Open issues are tracked in
|
||||||
[issue #344](https://github.com/nikic/PHP-Parser/issues/344). If you encounter problems while using
|
[issue #344](https://github.com/nikic/PHP-Parser/issues/344). If you encounter problems while using
|
||||||
this functionality, please open an issue, so we know what to prioritize.
|
this functionality, please open an issue, so we know what to prioritize.
|
||||||
|
335
doc/component/Walking_the_AST.markdown
Normal file
335
doc/component/Walking_the_AST.markdown
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
Walking the AST
|
||||||
|
===============
|
||||||
|
|
||||||
|
The most common way to work with the AST is by using a node traverser and one or more node visitors.
|
||||||
|
As a basic example, the following code changes all literal integers in the AST into strings (e.g.,
|
||||||
|
`42` becomes `'42'`.)
|
||||||
|
|
||||||
|
```php
|
||||||
|
use PhpParser\{Node, NodeTraverser, NodeVisitorAbstract};
|
||||||
|
|
||||||
|
$traverser = new NodeTraverser;
|
||||||
|
$traverser->addVisitor(new class extends NodeVisitorAbstract {
|
||||||
|
public function leaveNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Scalar\LNumber) {
|
||||||
|
return new Node\Scalar\String_((string) $node->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$stmts = ...;
|
||||||
|
$modifiedStmts = $traverser->traverse($stmts);
|
||||||
|
```
|
||||||
|
|
||||||
|
Node visitors
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Each node visitor implements an interface with following four methods:
|
||||||
|
|
||||||
|
```php
|
||||||
|
interface NodeVisitor {
|
||||||
|
public function beforeTraverse(array $nodes);
|
||||||
|
public function enterNode(Node $node);
|
||||||
|
public function leaveNode(Node $node);
|
||||||
|
public function afterTraverse(array $nodes);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `beforeTraverse()` and `afterTraverse()` methods are called before and after the traversal
|
||||||
|
respectively, and are passed the entire AST. They can be used to perform any necessary state
|
||||||
|
setup or cleanup.
|
||||||
|
|
||||||
|
The `enterNode()` method is called when a node is first encountered, before its children are
|
||||||
|
processed ("preorder"). The `leaveNode()` method is called after all children have been visited
|
||||||
|
("postorder").
|
||||||
|
|
||||||
|
For example, if we have the following excerpt of an AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
parts: array(
|
||||||
|
0: printLine
|
||||||
|
)
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
value: Scalar_String(
|
||||||
|
value: Hello World!!!
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
then the enter/leave methods will be called in the following order:
|
||||||
|
|
||||||
|
```
|
||||||
|
enterNode(Expr_FuncCall)
|
||||||
|
enterNode(Name)
|
||||||
|
leaveNode(Name)
|
||||||
|
enterNode(Arg)
|
||||||
|
enterNode(Scalar_String)
|
||||||
|
leaveNode(Scalar_String)
|
||||||
|
leaveNode(Arg)
|
||||||
|
leaveNode(Expr_FuncCall)
|
||||||
|
```
|
||||||
|
|
||||||
|
A common pattern is that `enterNode` is used to collect some information and then `leaveNode`
|
||||||
|
performs modifications based on that. At the time when `leaveNode` is called, all the code inside
|
||||||
|
the node will have already been visited and necessary information collected.
|
||||||
|
|
||||||
|
As you usually do not want to implement all four methods, it is recommended that you extend
|
||||||
|
`NodeVisitorAbstract` instead of implementing the interface directly. The abstract class provides
|
||||||
|
empty default implementations.
|
||||||
|
|
||||||
|
Modifying the AST
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
There are a number of ways in which the AST can be modified from inside a node visitor. The first
|
||||||
|
and simplest is to simply change AST properties inside the visitor:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function leaveNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Scalar\LNumber) {
|
||||||
|
// increment all integer literals
|
||||||
|
$node->value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The second is to replace a node entirely by returning a new node:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function leaveNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Expr\BinaryOp\BooleanAnd) {
|
||||||
|
// Convert all $a && $b expressions into !($a && $b)
|
||||||
|
return new Node\Expr\BooleanNot($node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Doing this is supported both inside enterNode and leaveNode. However, you have to be mindful about
|
||||||
|
where you perform the replacement: If a node is replaced in enterNode, then the recursive traversal
|
||||||
|
will also consider the children of the new node. If you aren't careful, this can lead to infinite
|
||||||
|
recursion. For example, let's take the previous code sample and use enterNode instead:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function enterNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Expr\BinaryOp\BooleanAnd) {
|
||||||
|
// Convert all $a && $b expressions into !($a && $b)
|
||||||
|
return new Node\Expr\BooleanNot($node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now `$a && $b` will be replaced by `!($a && $b)`. Then the traverser will go into the first (and
|
||||||
|
only) child of `!($a && $b)`, which is `$a && $b`. The transformation applies again and we end up
|
||||||
|
with `!!($a && $b)`. This will continue until PHP hits the memory limit.
|
||||||
|
|
||||||
|
Finally, two special replacement types are supported only by leaveNode. The first is removal of a
|
||||||
|
node:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function leaveNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Stmt\Return_) {
|
||||||
|
// Remove all return statements
|
||||||
|
return NodeTraverser::REMOVE_NODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Node removal only works if the parent structure is an array. This means that usually it only makes
|
||||||
|
sense to remove nodes of type `Node\Stmt`, as they always occur inside statement lists (and a few
|
||||||
|
more node types like `Arg` or `Expr\ArrayItem`, which are also always part of lists).
|
||||||
|
|
||||||
|
On the other hand, removing a `Node\Expr` does not make sense: If you have `$a * $b`, there is no
|
||||||
|
meaningful way in which the `$a` part could be removed. If you want to remove an expression, you
|
||||||
|
generally want to remove it together with a surrounding expression statement:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function leaveNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Stmt\Expression
|
||||||
|
&& $node->expr instanceof Node\Expr\FuncCall
|
||||||
|
&& $node->expr->name instanceof Node\Name
|
||||||
|
&& $node->expr->name->toString() === 'var_dump'
|
||||||
|
) {
|
||||||
|
return NodeTraverser::REMOVE_NODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This example will remove all calls to `var_dump()` which occur as expression statements. This means
|
||||||
|
that `var_dump($a);` will be removed, but `if (var_dump($a))` will not be removed (and there is no
|
||||||
|
obvious way in which it can be removed).
|
||||||
|
|
||||||
|
Next to removing nodes, it is also possible to replace one node with multiple nodes. Again, this
|
||||||
|
only works inside leaveNode and only if the parent structure is an array.
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function leaveNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Stmt\Return_ && $node->expr !== null) {
|
||||||
|
// Convert "return foo();" into "$retval = foo(); return $retval;"
|
||||||
|
$var = new Node\Expr\Variable('retval');
|
||||||
|
return [
|
||||||
|
new Node\Stmt\Expression(new Node\Expr\Assign($var, $node->expr)),
|
||||||
|
new Node\Stmt\Return_($var),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Short-circuiting traversal
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
An AST can easily contain thousands of nodes, and traversing over all of them may be slow,
|
||||||
|
especially if you have more than one visitor. In some cases, it is possible to avoid a full
|
||||||
|
traversal.
|
||||||
|
|
||||||
|
If you are looking for all class declarations in a file (and assuming you're not interested in
|
||||||
|
anonymous classes), you know that once you've seen a class declaration, there is no point in also
|
||||||
|
checking all it's child nodes, because PHP does not allow nesting classes. In this case, you can
|
||||||
|
instruct the traverser to not recurse into the class node:
|
||||||
|
|
||||||
|
```
|
||||||
|
private $classes = [];
|
||||||
|
public function enterNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Stmt\Class_) {
|
||||||
|
$this->classes[] = $node;
|
||||||
|
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Of course, this option is only available in enterNode, because it's already too late by the time
|
||||||
|
leaveNode is reached.
|
||||||
|
|
||||||
|
If you are only looking for one specific node, it is also possible to abort the traversal entirely
|
||||||
|
after finding it. For example, if you are looking for the node of a class with a certain name (and
|
||||||
|
discounting exotic cases like conditionally defining a class two times), you can stop traversal
|
||||||
|
once you found it:
|
||||||
|
|
||||||
|
```
|
||||||
|
private $class = null;
|
||||||
|
public function enterNode(Node $node) {
|
||||||
|
if ($node instanceof Node\Stmt\Class_ &&
|
||||||
|
$node->namespaceName->toString() === 'Foo\Bar\Baz'
|
||||||
|
) {
|
||||||
|
$this->class = $node;
|
||||||
|
return NodeTraverser::STOP_TRAVERSAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This works both in enterNode and leaveNode. Note that this particular case can also be more easily
|
||||||
|
handled using a NodeFinder, which will be introduced below.
|
||||||
|
|
||||||
|
Multiple visitors
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A single traverser can be used with multiple visitors:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$traverser = new NodeTraverser;
|
||||||
|
$traverser->addVisitor($visitorA);
|
||||||
|
$traverser->addVisitor($visitorB);
|
||||||
|
$stmts = $traverser->traverser($stmts);
|
||||||
|
```
|
||||||
|
|
||||||
|
It is important to understand that if a traverser is run with multiple visitors, the visitors will
|
||||||
|
be interleaved. Given the following AST excerpt
|
||||||
|
|
||||||
|
```
|
||||||
|
Stmt_Return(
|
||||||
|
expr: Expr_Variable(
|
||||||
|
name: foobar
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
the following method calls will be performed:
|
||||||
|
|
||||||
|
```
|
||||||
|
$visitorA->enterNode(Stmt_Return)
|
||||||
|
$visitorB->enterNode(Stmt_Return)
|
||||||
|
$visitorA->enterNode(Expr_Variable)
|
||||||
|
$visitorB->enterNode(Expr_Variable)
|
||||||
|
$visitorA->leaveNode(Expr_Variable)
|
||||||
|
$visitorB->leaveNode(Expr_Variable)
|
||||||
|
$visitorA->leaveNode(Stmt_Return)
|
||||||
|
$visitorB->leaveNode(Stmt_Return)
|
||||||
|
```
|
||||||
|
|
||||||
|
That is, when visiting a node, enterNode and leaveNode will always be called for all visitors.
|
||||||
|
Running multiple visitors in parallel improves performance, as the AST only has to be traversed
|
||||||
|
once. However, it is not always possible to write visitors in a way that allows interleaved
|
||||||
|
execution. In this case, you can always fall back to performing multiple traversals:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$traverserA = new NodeTraverser;
|
||||||
|
$traverserA->addVisitor($visitorA);
|
||||||
|
$traverserB = new NodeTraverser;
|
||||||
|
$traverserB->addVisitor($visitorB);
|
||||||
|
$stmts = $traverserA->traverser($stmts);
|
||||||
|
$stmts = $traverserB->traverser($stmts);
|
||||||
|
```
|
||||||
|
|
||||||
|
When using multiple visitors, it is important to understand how they interact with the various
|
||||||
|
special enterNode/leaveNode return values:
|
||||||
|
|
||||||
|
* If *any* visitor returns `DONT_TRAVERSE_CHILDREN`, the children will be skipped for *all*
|
||||||
|
visitors.
|
||||||
|
* If *any* visitor returns `STOP_TRAVERSAL`, traversal is stopped for *all* visitors.
|
||||||
|
* If a visitor returns a replacement node, subsequent visitors will be passed the replacement node,
|
||||||
|
not the original one.
|
||||||
|
* If a visitor returns `REMOVE_NODE`, subsequent visitors will not see this node.
|
||||||
|
* If a visitor returns an array of replacement nodes, subsequent visitors will see neither the node
|
||||||
|
that was replaced, nor the replacement nodes.
|
||||||
|
|
||||||
|
Simple node finding
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
While the node visitor mechanism is very flexible, creating a node visitor can be overly cumbersome
|
||||||
|
for minor tasks. For this reason a `NodeFinder` is provided, which can find AST nodes that either
|
||||||
|
satisfy a certain callback, or which are instanced of a certain node type. A couple of examples are
|
||||||
|
shown in the following:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use PhpParser\{Node, NodeFinder};
|
||||||
|
|
||||||
|
$nodeFinder = new NodeFinder;
|
||||||
|
|
||||||
|
// Find all class nodes.
|
||||||
|
$classes = $nodeFinder->findInstanceOf($stmts, Node\Stmt\Class_::class);
|
||||||
|
|
||||||
|
// Find all classes that extend another class
|
||||||
|
$extendingClasses = $nodeFinder->findInstanceOf($stmts, function(Node $node) {
|
||||||
|
return $node instanceof Node\Stmt\Class_
|
||||||
|
&& $node->extends !== null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find first class occuring in the AST. Returns null if no class exists.
|
||||||
|
$class = $nodeFinder->findFirstInstanceOf($stmts, Node\Stmt\Class_::class);
|
||||||
|
|
||||||
|
// Find first class that has name $name
|
||||||
|
$class = $nodeFinder->findFirst($stmts, function(Node $node) use ($name) {
|
||||||
|
return $node instanceof Node\Stmt\Class_
|
||||||
|
&& $node->resolvedName->toString() === $name;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Internally, the `NodeFinder` also uses a node traverser. It only simplifies the interface for a
|
||||||
|
common use case.
|
||||||
|
|
||||||
|
Parent and sibling references
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The node visitor mechanism is somewhat rigid, in that it prescribes an order in which nodes should
|
||||||
|
be accessed: From parents to children. However, it can often be convenient to operate in the
|
||||||
|
reverse direction: When working on a node, you might want to check if the parent node satisfies a
|
||||||
|
certain property.
|
||||||
|
|
||||||
|
PHP-Parser does not add parent (or sibling) references to nodes by itself, but you can easily
|
||||||
|
emulate this with a visitor. See the [FAQ](FAQ.markdown) for more information.
|
@ -11,7 +11,7 @@ What do all those files mean?
|
|||||||
.phpy pseudo language
|
.phpy pseudo language
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
The `.y` file is a normal grammer in `kmyacc` (`yacc`) style, with some transformations
|
The `.y` file is a normal grammar in `kmyacc` (`yacc`) style, with some transformations
|
||||||
applied to it:
|
applied to it:
|
||||||
|
|
||||||
* Nodes are created using the syntax `Name[..., ...]`. This is transformed into
|
* Nodes are created using the syntax `Name[..., ...]`. This is transformed into
|
||||||
|
@ -32,8 +32,8 @@ class #(-p) extends \PhpParser\ParserAbstract
|
|||||||
protected $defaultAction = #(YYDEFAULT);
|
protected $defaultAction = #(YYDEFAULT);
|
||||||
protected $unexpectedTokenRule = #(YYUNEXPECTED);
|
protected $unexpectedTokenRule = #(YYUNEXPECTED);
|
||||||
|
|
||||||
protected $YY2TBLSTATE = #(YY2TBLSTATE);
|
protected $YY2TBLSTATE = #(YY2TBLSTATE);
|
||||||
protected $YYNLSTATES = #(YYNLSTATES);
|
protected $numNonLeafStates = #(YYNLSTATES);
|
||||||
|
|
||||||
protected $symbolToName = array(
|
protected $symbolToName = array(
|
||||||
#listvar terminals
|
#listvar terminals
|
||||||
|
@ -44,6 +44,10 @@ identifier:
|
|||||||
T_STRING { $$ = Node\Identifier[$1]; }
|
T_STRING { $$ = Node\Identifier[$1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
reserved_non_modifiers_identifier:
|
||||||
|
reserved_non_modifiers { $$ = Node\Identifier[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
namespace_name_parts:
|
namespace_name_parts:
|
||||||
T_STRING { init($1); }
|
T_STRING { init($1); }
|
||||||
| namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); }
|
| namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); }
|
||||||
@ -331,8 +335,8 @@ case_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
case:
|
case:
|
||||||
T_CASE expr case_separator inner_statement_list { $$ = Stmt\Case_[$2, $4]; }
|
T_CASE expr case_separator inner_statement_list_ex { $$ = Stmt\Case_[$2, $4]; }
|
||||||
| T_DEFAULT case_separator inner_statement_list { $$ = Stmt\Case_[null, $3]; }
|
| T_DEFAULT case_separator inner_statement_list_ex { $$ = Stmt\Case_[null, $3]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
case_separator:
|
case_separator:
|
||||||
@ -484,7 +488,7 @@ trait_adaptation:
|
|||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
|
||||||
| trait_method_reference T_AS identifier ';'
|
| trait_method_reference T_AS identifier ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||||
| trait_method_reference T_AS reserved_non_modifiers ';'
|
| trait_method_reference T_AS reserved_non_modifiers_identifier ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -44,6 +44,10 @@ identifier:
|
|||||||
T_STRING { $$ = Node\Identifier[$1]; }
|
T_STRING { $$ = Node\Identifier[$1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
reserved_non_modifiers_identifier:
|
||||||
|
reserved_non_modifiers { $$ = Node\Identifier[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
namespace_name_parts:
|
namespace_name_parts:
|
||||||
T_STRING { init($1); }
|
T_STRING { init($1); }
|
||||||
| namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); }
|
| namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); }
|
||||||
@ -379,8 +383,8 @@ case_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
case:
|
case:
|
||||||
T_CASE expr case_separator inner_statement_list { $$ = Stmt\Case_[$2, $4]; }
|
T_CASE expr case_separator inner_statement_list_ex { $$ = Stmt\Case_[$2, $4]; }
|
||||||
| T_DEFAULT case_separator inner_statement_list { $$ = Stmt\Case_[null, $3]; }
|
| T_DEFAULT case_separator inner_statement_list_ex { $$ = Stmt\Case_[null, $3]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
case_separator:
|
case_separator:
|
||||||
@ -544,7 +548,7 @@ trait_adaptation:
|
|||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
|
||||||
| trait_method_reference T_AS identifier ';'
|
| trait_method_reference T_AS identifier ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||||
| trait_method_reference T_AS reserved_non_modifiers ';'
|
| trait_method_reference T_AS reserved_non_modifiers_identifier ';'
|
||||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -933,7 +937,7 @@ list_expr_element:
|
|||||||
|
|
||||||
array_pair_list:
|
array_pair_list:
|
||||||
inner_array_pair_list
|
inner_array_pair_list
|
||||||
{ $$ = $1; $end = count($$)-1; if ($$[$end] === null) unset($$[$end]); }
|
{ $$ = $1; $end = count($$)-1; if ($$[$end] === null) array_pop($$); }
|
||||||
;
|
;
|
||||||
|
|
||||||
inner_array_pair_list:
|
inner_array_pair_list:
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace PhpParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class Autoloader
|
|
||||||
{
|
|
||||||
/** @var bool Whether the autoloader has been registered. */
|
|
||||||
private static $registered = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers PhpParser\Autoloader as an SPL autoloader.
|
|
||||||
*
|
|
||||||
* @param bool $prepend Whether to prepend the autoloader instead of appending
|
|
||||||
*/
|
|
||||||
static public function register(bool $prepend = false) {
|
|
||||||
if (self::$registered === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spl_autoload_register([__CLASS__, 'autoload'], true, $prepend);
|
|
||||||
self::$registered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles autoloading of classes.
|
|
||||||
*
|
|
||||||
* @param string $class A class name.
|
|
||||||
*/
|
|
||||||
static public function autoload(string $class) {
|
|
||||||
if (0 === strpos($class, 'PhpParser\\')) {
|
|
||||||
$fileName = __DIR__ . strtr(substr($class, 9), '\\', '/') . '.php';
|
|
||||||
if (file_exists($fileName)) {
|
|
||||||
require $fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,4 +10,4 @@ interface Builder
|
|||||||
* @return Node The built node
|
* @return Node The built node
|
||||||
*/
|
*/
|
||||||
public function getNode() : Node;
|
public function getNode() : Node;
|
||||||
}
|
}
|
||||||
|
@ -90,18 +90,18 @@ class Class_ extends Declaration
|
|||||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||||
|
|
||||||
$targets = [
|
$targets = [
|
||||||
'Stmt_TraitUse' => &$this->uses,
|
Stmt\TraitUse::class => &$this->uses,
|
||||||
'Stmt_ClassConst' => &$this->constants,
|
Stmt\ClassConst::class => &$this->constants,
|
||||||
'Stmt_Property' => &$this->properties,
|
Stmt\Property::class => &$this->properties,
|
||||||
'Stmt_ClassMethod' => &$this->methods,
|
Stmt\ClassMethod::class => &$this->methods,
|
||||||
];
|
];
|
||||||
|
|
||||||
$type = $stmt->getType();
|
$class = \get_class($stmt);
|
||||||
if (!isset($targets[$type])) {
|
if (!isset($targets[$class])) {
|
||||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $type));
|
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
$targets[$type][] = $stmt;
|
$targets[$class][] = $stmt;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -119,4 +119,4 @@ class Class_ extends Declaration
|
|||||||
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
|
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
|
||||||
], $this->attributes);
|
], $this->attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,4 +40,4 @@ abstract class Declaration implements PhpParser\Builder
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,20 +48,14 @@ class Interface_ extends Declaration
|
|||||||
public function addStmt($stmt) {
|
public function addStmt($stmt) {
|
||||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||||
|
|
||||||
$type = $stmt->getType();
|
if ($stmt instanceof Stmt\ClassConst) {
|
||||||
switch ($type) {
|
$this->constants[] = $stmt;
|
||||||
case 'Stmt_ClassConst':
|
} elseif ($stmt instanceof Stmt\ClassMethod) {
|
||||||
$this->constants[] = $stmt;
|
// we erase all statements in the body of an interface method
|
||||||
break;
|
$stmt->stmts = null;
|
||||||
|
$this->methods[] = $stmt;
|
||||||
case 'Stmt_ClassMethod':
|
} else {
|
||||||
// we erase all statements in the body of an interface method
|
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||||
$stmt->stmts = null;
|
|
||||||
$this->methods[] = $stmt;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -78,4 +72,4 @@ class Interface_ extends Declaration
|
|||||||
'stmts' => array_merge($this->constants, $this->methods),
|
'stmts' => array_merge($this->constants, $this->methods),
|
||||||
], $this->attributes);
|
], $this->attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use PhpParser\BuilderHelpers;
|
|||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Stmt;
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
class Namespace_ implements PhpParser\Builder
|
class Namespace_ extends Declaration
|
||||||
{
|
{
|
||||||
private $name;
|
private $name;
|
||||||
private $stmts = [];
|
private $stmts = [];
|
||||||
@ -34,27 +34,12 @@ class Namespace_ implements PhpParser\Builder
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds multiple statements.
|
|
||||||
*
|
|
||||||
* @param array $stmts The statements to add
|
|
||||||
*
|
|
||||||
* @return $this The builder instance (for fluid interface)
|
|
||||||
*/
|
|
||||||
public function addStmts(array $stmts) {
|
|
||||||
foreach ($stmts as $stmt) {
|
|
||||||
$this->addStmt($stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the built node.
|
* Returns the built node.
|
||||||
*
|
*
|
||||||
* @return Node The built node
|
* @return Node The built node
|
||||||
*/
|
*/
|
||||||
public function getNode() : Node {
|
public function getNode() : Node {
|
||||||
return new Stmt\Namespace_($this->name, $this->stmts);
|
return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,4 +109,4 @@ class Property implements PhpParser\Builder
|
|||||||
$this->attributes
|
$this->attributes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,9 @@ class Trait_ extends Declaration
|
|||||||
|
|
||||||
if ($stmt instanceof Stmt\Property) {
|
if ($stmt instanceof Stmt\Property) {
|
||||||
$this->properties[] = $stmt;
|
$this->properties[] = $stmt;
|
||||||
} else if ($stmt instanceof Stmt\ClassMethod) {
|
} elseif ($stmt instanceof Stmt\ClassMethod) {
|
||||||
$this->methods[] = $stmt;
|
$this->methods[] = $stmt;
|
||||||
} else if ($stmt instanceof Stmt\TraitUse) {
|
} elseif ($stmt instanceof Stmt\TraitUse) {
|
||||||
$this->uses[] = $stmt;
|
$this->uses[] = $stmt;
|
||||||
} else {
|
} else {
|
||||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||||
|
@ -7,7 +7,8 @@ use PhpParser\BuilderHelpers;
|
|||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Stmt;
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
class Use_ implements Builder {
|
class Use_ implements Builder
|
||||||
|
{
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $type;
|
protected $type;
|
||||||
protected $alias = null;
|
protected $alias = null;
|
||||||
|
@ -14,7 +14,8 @@ use PhpParser\Node\Stmt;
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
final class BuilderHelpers {
|
final class BuilderHelpers
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Normalizes a node: Converts builder objects to nodes.
|
* Normalizes a node: Converts builder objects to nodes.
|
||||||
*
|
*
|
||||||
@ -184,7 +185,7 @@ final class BuilderHelpers {
|
|||||||
public static function normalizeDocComment($docComment) : Comment\Doc {
|
public static function normalizeDocComment($docComment) : Comment\Doc {
|
||||||
if ($docComment instanceof Comment\Doc) {
|
if ($docComment instanceof Comment\Doc) {
|
||||||
return $docComment;
|
return $docComment;
|
||||||
} else if (is_string($docComment)) {
|
} elseif (is_string($docComment)) {
|
||||||
return new Comment\Doc($docComment);
|
return new Comment\Doc($docComment);
|
||||||
} else {
|
} else {
|
||||||
throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
|
throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
|
||||||
|
@ -7,18 +7,23 @@ class Comment implements \JsonSerializable
|
|||||||
protected $text;
|
protected $text;
|
||||||
protected $line;
|
protected $line;
|
||||||
protected $filePos;
|
protected $filePos;
|
||||||
|
protected $tokenPos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a comment node.
|
* Constructs a comment node.
|
||||||
*
|
*
|
||||||
* @param string $text Comment text (including comment delimiters like /*)
|
* @param string $text Comment text (including comment delimiters like /*)
|
||||||
* @param int $startLine Line number the comment started on
|
* @param int $startLine Line number the comment started on
|
||||||
* @param int $startFilePos File offset the comment started on
|
* @param int $startFilePos File offset the comment started on
|
||||||
|
* @param int $startTokenPos Token offset the comment started on
|
||||||
*/
|
*/
|
||||||
public function __construct(string $text, int $startLine = -1, int $startFilePos = -1) {
|
public function __construct(
|
||||||
|
string $text, int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1
|
||||||
|
) {
|
||||||
$this->text = $text;
|
$this->text = $text;
|
||||||
$this->line = $startLine;
|
$this->line = $startLine;
|
||||||
$this->filePos = $startFilePos;
|
$this->filePos = $startFilePos;
|
||||||
|
$this->tokenPos = $startTokenPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,6 +53,15 @@ class Comment implements \JsonSerializable
|
|||||||
return $this->filePos;
|
return $this->filePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the token offset the comment started on.
|
||||||
|
*
|
||||||
|
* @return int Token offset
|
||||||
|
*/
|
||||||
|
public function getTokenPos() : int {
|
||||||
|
return $this->tokenPos;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the comment text.
|
* Gets the comment text.
|
||||||
*
|
*
|
||||||
@ -124,7 +138,7 @@ class Comment implements \JsonSerializable
|
|||||||
*/
|
*/
|
||||||
private function getShortestWhitespacePrefixLen(string $str) : int {
|
private function getShortestWhitespacePrefixLen(string $str) : int {
|
||||||
$lines = explode("\n", $str);
|
$lines = explode("\n", $str);
|
||||||
$shortestPrefixLen = INF;
|
$shortestPrefixLen = \INF;
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
preg_match('(^\s*)', $line, $matches);
|
preg_match('(^\s*)', $line, $matches);
|
||||||
$prefixLen = strlen($matches[0]);
|
$prefixLen = strlen($matches[0]);
|
||||||
@ -147,6 +161,7 @@ class Comment implements \JsonSerializable
|
|||||||
'text' => $this->text,
|
'text' => $this->text,
|
||||||
'line' => $this->line,
|
'line' => $this->line,
|
||||||
'filePos' => $this->filePos,
|
'filePos' => $this->filePos,
|
||||||
|
'tokenPos' => $this->tokenPos,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,4 @@ namespace PhpParser\Comment;
|
|||||||
|
|
||||||
class Doc extends \PhpParser\Comment
|
class Doc extends \PhpParser\Comment
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
namespace PhpParser;
|
namespace PhpParser;
|
||||||
|
|
||||||
class ConstExprEvaluationException extends \Exception {}
|
class ConstExprEvaluationException extends \Exception
|
||||||
|
{}
|
||||||
|
@ -20,15 +20,12 @@ use PhpParser\Node\Scalar;
|
|||||||
*
|
*
|
||||||
* The fallback evaluator should throw ConstExprEvaluationException for nodes it cannot evaluate.
|
* The fallback evaluator should throw ConstExprEvaluationException for nodes it cannot evaluate.
|
||||||
*
|
*
|
||||||
* The evaluation is performed as PHP would perform it, and as such may generate notices, warnings
|
* The evaluation is dependent on runtime configuration in two respects: Firstly, floating
|
||||||
* or Errors. For example, if the expression `1%0` is evaluated, an ArithmeticError is thrown. It is
|
|
||||||
* left to the consumer to handle these as appropriate.
|
|
||||||
*
|
|
||||||
* The evaluation is also dependent on runtime configuration in two respects: Firstly, floating
|
|
||||||
* point to string conversions are affected by the precision ini setting. Secondly, they are also
|
* point to string conversions are affected by the precision ini setting. Secondly, they are also
|
||||||
* affected by the LC_NUMERIC locale.
|
* affected by the LC_NUMERIC locale.
|
||||||
*/
|
*/
|
||||||
class ConstExprEvaluator {
|
class ConstExprEvaluator
|
||||||
|
{
|
||||||
private $fallbackEvaluator;
|
private $fallbackEvaluator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,7 +45,10 @@ class ConstExprEvaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates a constant expression into a PHP value.
|
* Silently evaluates a constant expression into a PHP value.
|
||||||
|
*
|
||||||
|
* Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException.
|
||||||
|
* The original source of the exception is available through getPrevious().
|
||||||
*
|
*
|
||||||
* If some part of the expression cannot be evaluated, the fallback evaluator passed to the
|
* If some part of the expression cannot be evaluated, the fallback evaluator passed to the
|
||||||
* constructor will be invoked. By default, if no fallback is provided, an exception of type
|
* constructor will be invoked. By default, if no fallback is provided, an exception of type
|
||||||
@ -58,9 +58,49 @@ class ConstExprEvaluator {
|
|||||||
*
|
*
|
||||||
* @param Expr $expr Constant expression to evaluate
|
* @param Expr $expr Constant expression to evaluate
|
||||||
* @return mixed Result of evaluation
|
* @return mixed Result of evaluation
|
||||||
|
*
|
||||||
|
* @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred
|
||||||
|
*/
|
||||||
|
public function evaluateSilently(Expr $expr) {
|
||||||
|
set_error_handler(function($num, $str, $file, $line) {
|
||||||
|
throw new \ErrorException($str, 0, $num, $file, $line);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $this->evaluate($expr);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
if (!$e instanceof ConstExprEvaluationException) {
|
||||||
|
$e = new ConstExprEvaluationException(
|
||||||
|
"An error occurred during constant expression evaluation", 0, $e);
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directly evaluates a constant expression into a PHP value.
|
||||||
|
*
|
||||||
|
* May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these
|
||||||
|
* into a ConstExprEvaluationException.
|
||||||
|
*
|
||||||
|
* If some part of the expression cannot be evaluated, the fallback evaluator passed to the
|
||||||
|
* constructor will be invoked. By default, if no fallback is provided, an exception of type
|
||||||
|
* ConstExprEvaluationException is thrown.
|
||||||
|
*
|
||||||
|
* See class doc comment for caveats and limitations.
|
||||||
|
*
|
||||||
|
* @param Expr $expr Constant expression to evaluate
|
||||||
|
* @return mixed Result of evaluation
|
||||||
|
*
|
||||||
* @throws ConstExprEvaluationException if the expression cannot be evaluated
|
* @throws ConstExprEvaluationException if the expression cannot be evaluated
|
||||||
*/
|
*/
|
||||||
public function evaluate(Expr $expr) {
|
public function evaluateDirectly(Expr $expr) {
|
||||||
|
return $this->evaluate($expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function evaluate(Expr $expr) {
|
||||||
if ($expr instanceof Scalar\LNumber
|
if ($expr instanceof Scalar\LNumber
|
||||||
|| $expr instanceof Scalar\DNumber
|
|| $expr instanceof Scalar\DNumber
|
||||||
|| $expr instanceof Scalar\String_
|
|| $expr instanceof Scalar\String_
|
||||||
@ -183,4 +223,4 @@ class ConstExprEvaluator {
|
|||||||
|
|
||||||
return ($this->fallbackEvaluator)($expr);
|
return ($this->fallbackEvaluator)($expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,6 @@ class Error extends \RuntimeException
|
|||||||
return $this->attributes['endLine'] ?? -1;
|
return $this->attributes['endLine'] ?? -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the attributes of the node/token the error occurred at.
|
* Gets the attributes of the node/token the error occurred at.
|
||||||
*
|
*
|
||||||
@ -62,7 +61,7 @@ class Error extends \RuntimeException
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the attributes of the node/token the error occured at.
|
* Sets the attributes of the node/token the error occurred at.
|
||||||
*
|
*
|
||||||
* @param array $attributes
|
* @param array $attributes
|
||||||
*/
|
*/
|
||||||
@ -99,7 +98,7 @@ class Error extends \RuntimeException
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function hasColumnInfo() : bool {
|
public function hasColumnInfo() : bool {
|
||||||
return isset($this->attributes['startFilePos']) && isset($this->attributes['endFilePos']);
|
return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,4 +10,4 @@ interface ErrorHandler
|
|||||||
* @param Error $error The error that needs to be handled
|
* @param Error $error The error that needs to be handled
|
||||||
*/
|
*/
|
||||||
public function handleError(Error $error);
|
public function handleError(Error $error);
|
||||||
}
|
}
|
||||||
|
@ -43,4 +43,4 @@ class Collecting implements ErrorHandler
|
|||||||
public function clearErrors() {
|
public function clearErrors() {
|
||||||
$this->errors = [];
|
$this->errors = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,4 +15,4 @@ class Throwing implements ErrorHandler
|
|||||||
public function handleError(Error $error) {
|
public function handleError(Error $error) {
|
||||||
throw $error;
|
throw $error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?php
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
namespace PhpParser\Internal;
|
namespace PhpParser\Internal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
class DiffElem {
|
class DiffElem
|
||||||
|
{
|
||||||
const TYPE_KEEP = 0;
|
const TYPE_KEEP = 0;
|
||||||
const TYPE_REMOVE = 1;
|
const TYPE_REMOVE = 1;
|
||||||
const TYPE_ADD = 2;
|
const TYPE_ADD = 2;
|
||||||
@ -23,4 +24,4 @@ class DiffElem {
|
|||||||
$this->old = $old;
|
$this->old = $old;
|
||||||
$this->new = $new;
|
$this->new = $new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?php
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
namespace PhpParser\Internal;
|
namespace PhpParser\Internal;
|
||||||
|
|
||||||
@ -10,13 +10,14 @@ namespace PhpParser\Internal;
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
class Differ {
|
class Differ
|
||||||
|
{
|
||||||
private $isEqual;
|
private $isEqual;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create differ over the given equality relation.
|
* Create differ over the given equality relation.
|
||||||
*
|
*
|
||||||
* @param callable $isEqual Equality relation with signature function($a, $b): bool
|
* @param callable $isEqual Equality relation with signature function($a, $b) : bool
|
||||||
*/
|
*/
|
||||||
public function __construct(callable $isEqual) {
|
public function __construct(callable $isEqual) {
|
||||||
$this->isEqual = $isEqual;
|
$this->isEqual = $isEqual;
|
||||||
@ -160,4 +161,4 @@ class Differ {
|
|||||||
}
|
}
|
||||||
return $newDiff;
|
return $newDiff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
lib/PhpParser/Internal/PrintableNewAnonClassNode.php
Normal file
56
lib/PhpParser/Internal/PrintableNewAnonClassNode.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Internal;
|
||||||
|
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Expr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node is used internally by the format-preserving pretty printer to print anonymous classes.
|
||||||
|
*
|
||||||
|
* The normal anonymous class structure violates assumptions about the order of token offsets.
|
||||||
|
* Namely, the constructor arguments are part of the Expr\New_ node and follow the class node, even
|
||||||
|
* though they are actually interleaved with them. This special node type is used temporarily to
|
||||||
|
* restore a sane token offset order.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class PrintableNewAnonClassNode extends Expr
|
||||||
|
{
|
||||||
|
/** @var Node\Arg[] Arguments */
|
||||||
|
public $args;
|
||||||
|
/** @var null|Node\Name Name of extended class */
|
||||||
|
public $extends;
|
||||||
|
/** @var Node\Name[] Names of implemented interfaces */
|
||||||
|
public $implements;
|
||||||
|
/** @var Node\Stmt[] Statements */
|
||||||
|
public $stmts;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
array $args, Node\Name $extends = null, array $implements, array $stmts, array $attributes
|
||||||
|
) {
|
||||||
|
parent::__construct($attributes);
|
||||||
|
$this->args = $args;
|
||||||
|
$this->extends = $extends;
|
||||||
|
$this->implements = $implements;
|
||||||
|
$this->stmts = $stmts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fromNewNode(Expr\New_ $newNode) {
|
||||||
|
$class = $newNode->class;
|
||||||
|
assert($class instanceof Node\Stmt\Class_);
|
||||||
|
assert($class->name === null);
|
||||||
|
return new self(
|
||||||
|
$newNode->args, $class->extends, $class->implements,
|
||||||
|
$class->stmts, $newNode->getAttributes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_PrintableNewAnonClass';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubNodeNames() : array {
|
||||||
|
return ['args', 'extends', 'implements', 'stmts'];
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
namespace PhpParser;
|
namespace PhpParser\Internal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides operations on token streams, for use by pretty printer.
|
* Provides operations on token streams, for use by pretty printer.
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
class TokenStream {
|
class TokenStream
|
||||||
|
{
|
||||||
/** @var array Tokens (in token_get_all format) */
|
/** @var array Tokens (in token_get_all format) */
|
||||||
private $tokens;
|
private $tokens;
|
||||||
|
/** @var int[] Map from position to indentation */
|
||||||
|
private $indentMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create token stream instance.
|
* Create token stream instance.
|
||||||
@ -18,6 +21,7 @@ class TokenStream {
|
|||||||
*/
|
*/
|
||||||
public function __construct(array $tokens) {
|
public function __construct(array $tokens) {
|
||||||
$this->tokens = $tokens;
|
$this->tokens = $tokens;
|
||||||
|
$this->indentMap = $this->calcIndentMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,8 +68,8 @@ class TokenStream {
|
|||||||
if ($tokenType === $expectedTokenType) {
|
if ($tokenType === $expectedTokenType) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ($tokenType !== T_WHITESPACE
|
if ($tokenType !== \T_WHITESPACE
|
||||||
&& $tokenType !== T_COMMENT && $tokenType !== T_DOC_COMMENT) {
|
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,8 +94,8 @@ class TokenStream {
|
|||||||
if ($tokenType === $expectedTokenType) {
|
if ($tokenType === $expectedTokenType) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ($tokenType !== T_WHITESPACE
|
if ($tokenType !== \T_WHITESPACE
|
||||||
&& $tokenType !== T_COMMENT && $tokenType !== T_DOC_COMMENT) {
|
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +106,7 @@ class TokenStream {
|
|||||||
$tokens = $this->tokens;
|
$tokens = $this->tokens;
|
||||||
|
|
||||||
$pos = $this->skipLeftWhitespace($pos);
|
$pos = $this->skipLeftWhitespace($pos);
|
||||||
if ($skipTokenType === T_WHITESPACE) {
|
if ($skipTokenType === \T_WHITESPACE) {
|
||||||
return $pos;
|
return $pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +123,7 @@ class TokenStream {
|
|||||||
$tokens = $this->tokens;
|
$tokens = $this->tokens;
|
||||||
|
|
||||||
$pos = $this->skipRightWhitespace($pos);
|
$pos = $this->skipRightWhitespace($pos);
|
||||||
if ($skipTokenType === T_WHITESPACE) {
|
if ($skipTokenType === \T_WHITESPACE) {
|
||||||
return $pos;
|
return $pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +146,7 @@ class TokenStream {
|
|||||||
$tokens = $this->tokens;
|
$tokens = $this->tokens;
|
||||||
for (; $pos >= 0; $pos--) {
|
for (; $pos >= 0; $pos--) {
|
||||||
$type = $tokens[$pos][0];
|
$type = $tokens[$pos][0];
|
||||||
if ($type !== T_WHITESPACE && $type !== T_COMMENT && $type !== T_DOC_COMMENT) {
|
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,7 +163,7 @@ class TokenStream {
|
|||||||
$tokens = $this->tokens;
|
$tokens = $this->tokens;
|
||||||
for ($count = \count($tokens); $pos < $count; $pos++) {
|
for ($count = \count($tokens); $pos < $count; $pos++) {
|
||||||
$type = $tokens[$pos][0];
|
$type = $tokens[$pos][0];
|
||||||
if ($type !== T_WHITESPACE && $type !== T_COMMENT && $type !== T_DOC_COMMENT) {
|
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,24 +189,7 @@ class TokenStream {
|
|||||||
* @return int Indentation depth (in spaces)
|
* @return int Indentation depth (in spaces)
|
||||||
*/
|
*/
|
||||||
public function getIndentationBefore(int $pos) : int {
|
public function getIndentationBefore(int $pos) : int {
|
||||||
$tokens = $this->tokens;
|
return $this->indentMap[$pos];
|
||||||
$indent = 0;
|
|
||||||
$pos--;
|
|
||||||
for (; $pos >= 0; $pos--) {
|
|
||||||
if ($tokens[$pos][0] !== T_WHITESPACE) {
|
|
||||||
$indent = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$content = $tokens[$pos][1];
|
|
||||||
$newlinePos = \strrpos($content, "\n");
|
|
||||||
if (false !== $newlinePos) {
|
|
||||||
$indent += \strlen($content) - $newlinePos - 1;
|
|
||||||
return $indent;
|
|
||||||
}
|
|
||||||
|
|
||||||
$indent += \strlen($content);
|
|
||||||
}
|
|
||||||
return $indent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -222,13 +209,13 @@ class TokenStream {
|
|||||||
if (\is_array($token)) {
|
if (\is_array($token)) {
|
||||||
$type = $token[0];
|
$type = $token[0];
|
||||||
$content = $token[1];
|
$content = $token[1];
|
||||||
if ($type === T_CONSTANT_ENCAPSED_STRING || $type === T_ENCAPSED_AND_WHITESPACE) {
|
if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) {
|
||||||
$result .= $content;
|
$result .= $content;
|
||||||
} else {
|
} else {
|
||||||
// TODO Handle non-space indentation
|
// TODO Handle non-space indentation
|
||||||
if ($indent < 0) {
|
if ($indent < 0) {
|
||||||
$result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content);
|
$result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content);
|
||||||
} else if ($indent > 0) {
|
} elseif ($indent > 0) {
|
||||||
$result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content);
|
$result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content);
|
||||||
} else {
|
} else {
|
||||||
$result .= $content;
|
$result .= $content;
|
||||||
@ -240,4 +227,30 @@ class TokenStream {
|
|||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Precalculate the indentation at every token position.
|
||||||
|
*
|
||||||
|
* @return int[] Token position to indentation map
|
||||||
|
*/
|
||||||
|
private function calcIndentMap() {
|
||||||
|
$indentMap = [];
|
||||||
|
$indent = 0;
|
||||||
|
foreach ($this->tokens as $token) {
|
||||||
|
$indentMap[] = $indent;
|
||||||
|
|
||||||
|
if ($token[0] === \T_WHITESPACE) {
|
||||||
|
$content = $token[1];
|
||||||
|
$newlinePos = \strrpos($content, "\n");
|
||||||
|
if (false !== $newlinePos) {
|
||||||
|
$indent = \strlen($content) - $newlinePos - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a sentinel for one past end of the file
|
||||||
|
$indentMap[] = $indent;
|
||||||
|
|
||||||
|
return $indentMap;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace PhpParser;
|
namespace PhpParser;
|
||||||
|
|
||||||
class JsonDecoder {
|
class JsonDecoder
|
||||||
|
{
|
||||||
/** @var \ReflectionClass[] Node type to reflection class map */
|
/** @var \ReflectionClass[] Node type to reflection class map */
|
||||||
private $reflectionClassCache;
|
private $reflectionClassCache;
|
||||||
|
|
||||||
@ -71,7 +72,9 @@ class JsonDecoder {
|
|||||||
throw new \RuntimeException('Comment must have text');
|
throw new \RuntimeException('Comment must have text');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new $className($value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1);
|
return new $className(
|
||||||
|
$value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass {
|
private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass {
|
||||||
@ -95,4 +98,4 @@ class JsonDecoder {
|
|||||||
|
|
||||||
throw new \RuntimeException("Unknown node type \"$nodeType\"");
|
throw new \RuntimeException("Unknown node type \"$nodeType\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class Lexer
|
|||||||
// 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], 1
|
||||||
);
|
);
|
||||||
|
|
||||||
// the usedAttributes member is a map of the used attribute names to a dummy
|
// the usedAttributes member is a map of the used attribute names to a dummy
|
||||||
@ -112,7 +112,7 @@ class Lexer
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function isUnterminatedComment($token) : bool {
|
private function isUnterminatedComment($token) : bool {
|
||||||
return ($token[0] === T_COMMENT || $token[0] === T_DOC_COMMENT)
|
return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT)
|
||||||
&& substr($token[1], 0, 2) === '/*'
|
&& substr($token[1], 0, 2) === '/*'
|
||||||
&& substr($token[1], -2) !== '*/';
|
&& substr($token[1], -2) !== '*/';
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ class Lexer
|
|||||||
$nextFilePos = strpos($this->code, $tokenValue, $filePos);
|
$nextFilePos = strpos($this->code, $tokenValue, $filePos);
|
||||||
$this->handleInvalidCharacterRange(
|
$this->handleInvalidCharacterRange(
|
||||||
$filePos, $nextFilePos, $line, $errorHandler);
|
$filePos, $nextFilePos, $line, $errorHandler);
|
||||||
$filePos = (int)$nextFilePos;
|
$filePos = (int) $nextFilePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
$filePos += $tokenLen;
|
$filePos += $tokenLen;
|
||||||
@ -173,7 +173,7 @@ class Lexer
|
|||||||
|
|
||||||
// Emulate the PHP behavior
|
// Emulate the PHP behavior
|
||||||
$isDocComment = isset($comment[3]) && $comment[3] === '*';
|
$isDocComment = isset($comment[3]) && $comment[3] === '*';
|
||||||
$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(
|
$this->handleInvalidCharacterRange(
|
||||||
@ -253,20 +253,20 @@ class Lexer
|
|||||||
} elseif (!isset($this->dropTokens[$token[0]])) {
|
} elseif (!isset($this->dropTokens[$token[0]])) {
|
||||||
$value = $token[1];
|
$value = $token[1];
|
||||||
$id = $this->tokenMap[$token[0]];
|
$id = $this->tokenMap[$token[0]];
|
||||||
if (T_CLOSE_TAG === $token[0]) {
|
if (\T_CLOSE_TAG === $token[0]) {
|
||||||
$this->prevCloseTagHasNewline = false !== strpos($token[1], "\n");
|
$this->prevCloseTagHasNewline = false !== strpos($token[1], "\n");
|
||||||
} else if (T_INLINE_HTML === $token[0]) {
|
} elseif (\T_INLINE_HTML === $token[0]) {
|
||||||
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
|
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->line += substr_count($value, "\n");
|
$this->line += substr_count($value, "\n");
|
||||||
$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 (isset($this->usedAttributes['comments'])) {
|
||||||
$comment = T_DOC_COMMENT === $token[0]
|
$comment = \T_DOC_COMMENT === $token[0]
|
||||||
? new Comment\Doc($token[1], $this->line, $this->filePos)
|
? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos)
|
||||||
: new Comment($token[1], $this->line, $this->filePos);
|
: new Comment($token[1], $this->line, $this->filePos, $this->pos);
|
||||||
$startAttributes['comments'][] = $comment;
|
$startAttributes['comments'][] = $comment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,20 +344,20 @@ class Lexer
|
|||||||
// 256 is the minimum possible token number, as everything below
|
// 256 is the minimum possible token number, as everything below
|
||||||
// it is an ASCII value
|
// it is an ASCII value
|
||||||
for ($i = 256; $i < 1000; ++$i) {
|
for ($i = 256; $i < 1000; ++$i) {
|
||||||
if (T_DOUBLE_COLON === $i) {
|
if (\T_DOUBLE_COLON === $i) {
|
||||||
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
|
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
|
||||||
$tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
|
$tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
|
||||||
} elseif(T_OPEN_TAG_WITH_ECHO === $i) {
|
} elseif(\T_OPEN_TAG_WITH_ECHO === $i) {
|
||||||
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
|
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
|
||||||
$tokenMap[$i] = Tokens::T_ECHO;
|
$tokenMap[$i] = Tokens::T_ECHO;
|
||||||
} elseif(T_CLOSE_TAG === $i) {
|
} elseif(\T_CLOSE_TAG === $i) {
|
||||||
// T_CLOSE_TAG is equivalent to ';'
|
// T_CLOSE_TAG is equivalent to ';'
|
||||||
$tokenMap[$i] = ord(';');
|
$tokenMap[$i] = ord(';');
|
||||||
} elseif ('UNKNOWN' !== $name = token_name($i)) {
|
} elseif ('UNKNOWN' !== $name = token_name($i)) {
|
||||||
if ('T_HASHBANG' === $name) {
|
if ('T_HASHBANG' === $name) {
|
||||||
// HHVM uses a special token for #! hashbang lines
|
// HHVM uses a special token for #! hashbang lines
|
||||||
$tokenMap[$i] = Tokens::T_INLINE_HTML;
|
$tokenMap[$i] = Tokens::T_INLINE_HTML;
|
||||||
} else if (defined($name = 'PhpParser\Parser\Tokens::' . $name)) {
|
} elseif (defined($name = Tokens::class . '::' . $name)) {
|
||||||
// Other tokens can be mapped directly
|
// Other tokens can be mapped directly
|
||||||
$tokenMap[$i] = constant($name);
|
$tokenMap[$i] = constant($name);
|
||||||
}
|
}
|
||||||
@ -366,11 +366,11 @@ class Lexer
|
|||||||
|
|
||||||
// HHVM uses a special token for numbers that overflow to double
|
// HHVM uses a special token for numbers that overflow to double
|
||||||
if (defined('T_ONUMBER')) {
|
if (defined('T_ONUMBER')) {
|
||||||
$tokenMap[T_ONUMBER] = Tokens::T_DNUMBER;
|
$tokenMap[\T_ONUMBER] = Tokens::T_DNUMBER;
|
||||||
}
|
}
|
||||||
// HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant
|
// HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant
|
||||||
if (defined('T_COMPILER_HALT_OFFSET')) {
|
if (defined('T_COMPILER_HALT_OFFSET')) {
|
||||||
$tokenMap[T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
|
$tokenMap[\T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tokenMap;
|
return $tokenMap;
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
namespace PhpParser\Lexer;
|
namespace PhpParser\Lexer;
|
||||||
|
|
||||||
|
|
||||||
class Emulative extends \PhpParser\Lexer
|
class Emulative extends \PhpParser\Lexer
|
||||||
{
|
{
|
||||||
/* No features requiring emulation have been added in PHP > 7.0 */
|
/* No features requiring emulation have been added in PHP > 7.0 */
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ use PhpParser\Node\Name;
|
|||||||
use PhpParser\Node\Name\FullyQualified;
|
use PhpParser\Node\Name\FullyQualified;
|
||||||
use PhpParser\Node\Stmt;
|
use PhpParser\Node\Stmt;
|
||||||
|
|
||||||
class NameContext {
|
class NameContext
|
||||||
|
{
|
||||||
/** @var null|Name Current namespace */
|
/** @var null|Name Current namespace */
|
||||||
protected $namespace;
|
protected $namespace;
|
||||||
|
|
||||||
@ -214,7 +215,7 @@ class NameContext {
|
|||||||
|
|
||||||
// Find shortest name
|
// Find shortest name
|
||||||
$shortestName = null;
|
$shortestName = null;
|
||||||
$shortestLength = INF;
|
$shortestLength = \INF;
|
||||||
foreach ($possibleNames as $possibleName) {
|
foreach ($possibleNames as $possibleName) {
|
||||||
$length = strlen($possibleName->toCodeString());
|
$length = strlen($possibleName->toCodeString());
|
||||||
if ($length < $shortestLength) {
|
if ($length < $shortestLength) {
|
||||||
@ -281,4 +282,4 @@ class NameContext {
|
|||||||
$shortName = substr($name, $nsSep + 1);
|
$shortName = substr($name, $nsSep + 1);
|
||||||
return strtolower($ns) . '\\' . $shortName;
|
return strtolower($ns) . '\\' . $shortName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ interface Node
|
|||||||
*
|
*
|
||||||
* @return Comment[]
|
* @return Comment[]
|
||||||
*/
|
*/
|
||||||
public function getComments(): array;
|
public function getComments() : array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the doc comment of the node.
|
* Gets the doc comment of the node.
|
||||||
|
@ -31,4 +31,8 @@ class Arg extends NodeAbstract
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['value', 'byRef', 'unpack'];
|
return ['value', 'byRef', 'unpack'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Arg';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,4 +30,8 @@ class Const_ extends NodeAbstract
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['name', 'value'];
|
return ['name', 'value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Const';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,4 @@ use PhpParser\NodeAbstract;
|
|||||||
|
|
||||||
abstract class Expr extends NodeAbstract
|
abstract class Expr extends NodeAbstract
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,8 @@ class ArrayDimFetch extends Expr
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['var', 'dim'];
|
return ['var', 'dim'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_ArrayDimFetch';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,4 +31,8 @@ class ArrayItem extends Expr
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['key', 'value', 'byRef'];
|
return ['key', 'value', 'byRef'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_ArrayItem';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,8 @@ class Array_ extends Expr
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['items'];
|
return ['items'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_Array';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,8 @@ class Assign extends Expr
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['var', 'expr'];
|
return ['var', 'expr'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_Assign';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class BitwiseAnd extends AssignOp
|
class BitwiseAnd extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_BitwiseAnd';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class BitwiseOr extends AssignOp
|
class BitwiseOr extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_BitwiseOr';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class BitwiseXor extends AssignOp
|
class BitwiseXor extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_BitwiseXor';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class Concat extends AssignOp
|
class Concat extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_Concat';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class Div extends AssignOp
|
class Div extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_Div';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class Minus extends AssignOp
|
class Minus extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_Minus';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class Mod extends AssignOp
|
class Mod extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_Mod';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class Mul extends AssignOp
|
class Mul extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_Mul';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class Plus extends AssignOp
|
class Plus extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_Plus';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class Pow extends AssignOp
|
class Pow extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_Pow';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class ShiftLeft extends AssignOp
|
class ShiftLeft extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_ShiftLeft';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\AssignOp;
|
|||||||
|
|
||||||
class ShiftRight extends AssignOp
|
class ShiftRight extends AssignOp
|
||||||
{
|
{
|
||||||
}
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignOp_ShiftRight';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -27,4 +27,8 @@ class AssignRef extends Expr
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['var', 'expr'];
|
return ['var', 'expr'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_AssignRef';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,4 +9,8 @@ class BitwiseAnd extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '&';
|
return '&';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_BitwiseAnd';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class BitwiseOr extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '|';
|
return '|';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_BitwiseOr';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class BitwiseXor extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '^';
|
return '^';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_BitwiseXor';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class BooleanAnd extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '&&';
|
return '&&';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_BooleanAnd';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class BooleanOr extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '||';
|
return '||';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_BooleanOr';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Coalesce extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '??';
|
return '??';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Coalesce';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Concat extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '.';
|
return '.';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Concat';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Div extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '/';
|
return '/';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Div';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Equal extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '==';
|
return '==';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Equal';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Greater extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '>';
|
return '>';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Greater';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class GreaterOrEqual extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '>=';
|
return '>=';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_GreaterOrEqual';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Identical extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '===';
|
return '===';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Identical';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class LogicalAnd extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return 'and';
|
return 'and';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_LogicalAnd';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class LogicalOr extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return 'or';
|
return 'or';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_LogicalOr';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class LogicalXor extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return 'xor';
|
return 'xor';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_LogicalXor';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Minus extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Minus';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Mod extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '%';
|
return '%';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Mod';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Mul extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '*';
|
return '*';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Mul';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class NotEqual extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '!=';
|
return '!=';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_NotEqual';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class NotIdentical extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '!==';
|
return '!==';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_NotIdentical';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Plus extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '+';
|
return '+';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Plus';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Pow extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '**';
|
return '**';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Pow';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class ShiftLeft extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '<<';
|
return '<<';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_ShiftLeft';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class ShiftRight extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '>>';
|
return '>>';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_ShiftRight';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Smaller extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '<';
|
return '<';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Smaller';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class SmallerOrEqual extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '<=';
|
return '<=';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_SmallerOrEqual';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,4 +9,8 @@ class Spaceship extends BinaryOp
|
|||||||
public function getOperatorSigil() : string {
|
public function getOperatorSigil() : string {
|
||||||
return '<=>';
|
return '<=>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BinaryOp_Spaceship';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,4 +23,8 @@ class BitwiseNot extends Expr
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['expr'];
|
return ['expr'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'Expr_BitwiseNot';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user