mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-06-18 22:02:07 +02:00
Compare commits
177 Commits
1.x
...
v3.0.0beta
Author | SHA1 | Date | |
---|---|---|---|
f5d334d9bf | |||
f03823cde5 | |||
4dd659edad | |||
1ab24d26ee | |||
a7120116b0 | |||
83f34e7fa4 | |||
d0cfb98133 | |||
13f7321def | |||
46495abb49 | |||
f7cb00d6d3 | |||
7dae6c7a6b | |||
2b209aaaf0 | |||
977cbab8e7 | |||
09086fbe0a | |||
ec614c95dd | |||
21b18eb294 | |||
faa09884db | |||
b740076ab1 | |||
c9fea2ef67 | |||
818ef2e692 | |||
5f97b12576 | |||
1dea9111a2 | |||
174e6c3cab | |||
eefcfeed23 | |||
18129480ae | |||
1b1ff8995b | |||
867ae5148d | |||
72e91845e4 | |||
537b59d4d1 | |||
7ff12b8fcb | |||
574665b45b | |||
437890d386 | |||
1edf72c040 | |||
7a54aca468 | |||
81f7da3b23 | |||
5ea2a76d80 | |||
5044fce1ff | |||
038e11da4b | |||
225804c147 | |||
96cbd48df6 | |||
e45e31c218 | |||
371c783344 | |||
47b254ea51 | |||
912c0bb9c9 | |||
9897fa8819 | |||
5a6e7dd452 | |||
90eb1165d1 | |||
cf9b9e2afa | |||
e7869b9f14 | |||
4c7ad7e194 | |||
f8a40b3f24 | |||
82bb6627c9 | |||
d1dd9f5aec | |||
e7f0860d85 | |||
954051f240 | |||
39f93f09f9 | |||
2d0c3b70f8 | |||
4252ffa43e | |||
8cacc85913 | |||
588e6a4d4c | |||
fa6a17755a | |||
52cb5ecec7 | |||
1565a2815d | |||
14de71898e | |||
5fa8493675 | |||
b31a973fa7 | |||
9ac3592190 | |||
35011d2e4d | |||
60f01bdd86 | |||
15a2388d75 | |||
68b4c0388a | |||
eb4bfe1366 | |||
e7ca4b7b04 | |||
fc36239be5 | |||
f493219c7d | |||
aa199120c7 | |||
ae30f97af6 | |||
47c342a3e4 | |||
7eac2cfd8b | |||
a0c216bf4b | |||
06d9ba42de | |||
573c7c20c4 | |||
d5cbf79f2f | |||
ce5be709d5 | |||
9829bf69cd | |||
c8282e6e76 | |||
a73aa7eec1 | |||
1fe8f09caa | |||
47509cf927 | |||
5b96a11a1f | |||
7faa1dcab9 | |||
65af37f7b0 | |||
d6361136e1 | |||
e05ef23743 | |||
73a9d494fb | |||
94f10d3c50 | |||
6dffb72ce0 | |||
eb73441032 | |||
9a6a147369 | |||
5c2cc50455 | |||
58eb1ea7c3 | |||
719ca71d4a | |||
c542e5d86a | |||
a9074c7444 | |||
b2f26d30ee | |||
75cd4ab7a5 | |||
33602889c1 | |||
98d28d7aa0 | |||
e4b837e0c4 | |||
2b9c5a62cb | |||
99e89743bd | |||
39a039fa42 | |||
fcf23101dd | |||
c8898df3dd | |||
b2961915a6 | |||
6f3fd7834a | |||
eecaf1e93b | |||
0d4239ef56 | |||
e3a9356178 | |||
5118e21c6e | |||
40455b5c18 | |||
fe6755ff4c | |||
f57d217e91 | |||
e0a75ededa | |||
2496cd38ad | |||
965e53a164 | |||
dc85742034 | |||
3bb874fcec | |||
feb82eed33 | |||
0ef15c111a | |||
0265c28e6e | |||
5bd8cb84de | |||
ebeeae19a6 | |||
49324ea412 | |||
21e51c8cf6 | |||
e7a2abb03b | |||
8090531acd | |||
49253c5dbb | |||
c5ac17711d | |||
90ab32f046 | |||
7434a682e5 | |||
0fbb5f90a1 | |||
5e6627c895 | |||
f872fa9b0b | |||
a5db176903 | |||
9c9a8cddce | |||
f2b7a31509 | |||
d8312a09a3 | |||
813c9f1545 | |||
ef9a154d09 | |||
0da72fad00 | |||
71fa7c6674 | |||
f3f24e03ae | |||
bc21514ecf | |||
04e05907c3 | |||
61e060694d | |||
602b9807eb | |||
f372a4c4ab | |||
d18dcc0c7f | |||
74c57eef0e | |||
fdbddc4b8c | |||
ca3b44bf60 | |||
dca46febc9 | |||
1a1bd1448d | |||
9620f79cdc | |||
583b560f71 | |||
db3dafd64d | |||
5038dcc251 | |||
a0e7d5e0aa | |||
51ec2a25fe | |||
5d1e3be7d4 | |||
9d42e4a2e2 | |||
cc75dd3612 | |||
5f621c5adc | |||
29b9015f51 | |||
e6619f5514 | |||
6b4a17b3e0 |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
grammar/kmyacc.exe
|
||||
grammar/y.output
|
23
.travis.yml
23
.travis.yml
@ -1,14 +1,31 @@
|
||||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- nightly
|
||||
- hhvm
|
||||
|
||||
install:
|
||||
- if [ $TRAVIS_PHP_VERSION = '5.6' ]; then composer require satooshi/php-coveralls '~1.0'; fi
|
||||
- composer install --prefer-dist
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 7.0
|
||||
- php: nightly
|
||||
fast_finish: true
|
||||
|
||||
script:
|
||||
- if [ $TRAVIS_PHP_VERSION = '5.6' ]; then vendor/bin/phpunit --coverage-clover build/logs/clover.xml; else vendor/bin/phpunit; fi
|
||||
- if [ $TRAVIS_PHP_VERSION = '7.0' ]; then test_old/run-php-src.sh; fi
|
||||
|
||||
after_success:
|
||||
if [ $TRAVIS_PHP_VERSION = '5.6' ]; then php vendor/bin/coveralls; fi
|
||||
|
||||
|
425
CHANGELOG.md
425
CHANGELOG.md
@ -1,253 +1,224 @@
|
||||
Version 1.4.2-dev
|
||||
Version 3.0.0-dev
|
||||
-----------------
|
||||
|
||||
Nothing yet.
|
||||
|
||||
Version 1.4.1 (2015-09-19)
|
||||
Version 3.0.0-beta1 (2016-09-16)
|
||||
--------------------------------
|
||||
### Added
|
||||
|
||||
* [7.1] Function/method and parameter builders now support PHP 7.1 type hints (void, iterable and
|
||||
nullable types).
|
||||
* Nodes and Comments now implement `JsonSerializable`. The node kind is stored in a `nodeType`
|
||||
property.
|
||||
* The `InlineHTML` node now has an `hasLeadingNewline` attribute, that specifies whether the
|
||||
preceding closing tag contained a newline. The pretty printer honors this attribute.
|
||||
* Partial parsing of `$obj->` (with missing property name) is now supported in error recovery mode.
|
||||
* The error recovery mode is now exposed in the `php-parse` script through the `--with-recovery`
|
||||
or `-r` flags.
|
||||
|
||||
The following changes are also part of PHP-Parser 2.1.1:
|
||||
|
||||
* The PHP 7 parser will now generate a parse error for `$var =& new Obj` assignments.
|
||||
* Comments on free-standing code blocks will no be retained as comments on the first statement in
|
||||
the code block.
|
||||
|
||||
Version 3.0.0-alpha1 (2016-07-25)
|
||||
---------------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* [7.1] Added support for `void` and `iterable` types. These will now be represented as strings
|
||||
(instead of `Name` instances) similar to other builtin types.
|
||||
* [7.1] Added support for class constant visibility. The `ClassConst` node now has a `flags` subnode
|
||||
holding the visibility modifier, as well as `isPublic()`, `isProtected()` and `isPrivate()`
|
||||
methods. The constructor changed to accept the additional subnode.
|
||||
* [7.1] Added support for nullable types. These are represented using a new `NullableType` node
|
||||
with a single `type` subnode.
|
||||
* [7.1] Added support for short array destructuring syntax. This means that `Array` nodes may now
|
||||
appear as the left-hand-side of assignments and foreach value targets. Additionally the array
|
||||
items may now contain `null` values if elements are skipped.
|
||||
* [7.1] Added support for keys in list() destructuring. The `List` subnode `vars` has been renamed
|
||||
to `items` and now contains `ArrayItem`s instead of plain variables.
|
||||
* [7.1] Added support for multi-catch. The `Catch` subnode `type` has been renamed to `types` and
|
||||
is now an array of `Name`s.
|
||||
* `Name::slice()` now supports lengths and negative offsets. This brings it in line with
|
||||
`array_slice()` functionality.
|
||||
|
||||
### Changed
|
||||
|
||||
Due to PHP 7.1 support additions described above, the node structure changed as follows:
|
||||
|
||||
* `void` and `iterable` types are now stored as strings if the PHP 7 parser is used.
|
||||
* The `ClassConst` constructor changed to accept an additional `flags` subnode.
|
||||
* The `Array` subnode `items` may now contain `null` elements (destructuring).
|
||||
* The `List` subnode `vars` has been renamed to `items` and now contains `ArrayItem`s instead of
|
||||
plain variables.
|
||||
* The `Catch` subnode `type` has been renamed to `types` and is now an array of `Name`s.
|
||||
|
||||
Additionally the following changes were made:
|
||||
|
||||
* The `type` subnode on `Class`, `ClassMethod` and `Property` has been renamed to `flags`. The
|
||||
`type` subnode has retained for backwards compatibility and is populated to the same value as
|
||||
`flags`. However, writes to `type` will not update `flags`.
|
||||
* The `TryCatch` subnode `finallyStmts` has been replaced with a `finally` subnode that holds an
|
||||
explicit `Finally` node. This allows for more accurate attribute assignment.
|
||||
* The `Trait` constructor now has the same form as the `Class` and `Interface` constructors: It
|
||||
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
|
||||
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.
|
||||
|
||||
### Removed
|
||||
|
||||
* Removed support for running on PHP 5.4. It is however still possible to parse PHP 5.2-5.4 code
|
||||
while running on a newer version.
|
||||
* The deprecated `Comment::setLine()` and `Comment::setText()` methods have been removed.
|
||||
* The deprecated `Name::set()`, `Name::setFirst()` and `Name::setLast()` methods have been removed.
|
||||
|
||||
Version 2.1.1 (2016-09-16)
|
||||
--------------------------
|
||||
|
||||
### Changed
|
||||
|
||||
* The pretty printer will now escape all control characters in the range `\x00-\x1F` inside double
|
||||
quoted strings. If no special escape sequence is available, an octal escape will be used.
|
||||
* The quality of the error recovery has been improved. In particular unterminated expressions should
|
||||
be handled more gracefully.
|
||||
* The PHP 7 parser will now generate a parse error for `$var =& new Obj` assignments.
|
||||
* Comments on free-standing code blocks will no be retained as comments on the first statement in
|
||||
the code block.
|
||||
|
||||
Version 2.1.0 (2016-04-19)
|
||||
--------------------------
|
||||
|
||||
### Fixed
|
||||
|
||||
* Properly support `B""` strings (with uppercase `B`) in a number of places.
|
||||
* Fixed reformatting of indented parts in a certain non-standard comment style.
|
||||
|
||||
### Added
|
||||
|
||||
* Added `dumpComments` option to node dumper, to enable dumping of comments associated with nodes.
|
||||
* Added `Stmt\Nop` node, that is used to collect comments located at the end of a block or at the
|
||||
end of a file (without a following node with which they could otherwise be associated).
|
||||
* 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
|
||||
hexadecimal numbers.
|
||||
* Added `kind` attribtue to `Expr\Array` to distinguish between `array()` and `[]`.
|
||||
* Added `kind` attribute to `Scalar\String` and `Scalar\Encapsed` to distinguish between
|
||||
single-quoted, double-quoted, heredoc and nowdoc string.
|
||||
* Added `docLabel` attribute to `Scalar\String` and `Scalar\Encapsed`, if it is a heredoc or
|
||||
nowdoc string.
|
||||
* Added start file offset information to `Comment` nodes.
|
||||
* Added `setReturnType()` method to function and method builders.
|
||||
* Added `-h` and `--help` options to `php-parse` script.
|
||||
|
||||
### Changed
|
||||
|
||||
* Invalid octal literals now throw a parse error in PHP 7 mode.
|
||||
* The pretty printer takes all the new attributes mentioned in the previous section into account.
|
||||
* The protected `AbstractPrettyPrinter::pComments()` method no longer returns a trailing newline.
|
||||
* The bundled autoloader supports library files being stored in a different directory than
|
||||
`PhpParser` for easier downstream distribution.
|
||||
|
||||
### Deprecated
|
||||
|
||||
* The `Comment::setLine()` and `Comment::setText()` methods have been deprecated. Construct new
|
||||
objects instead.
|
||||
|
||||
### Removed
|
||||
|
||||
* The internal (but public) method `Scalar\LNumber::parse()` has been removed. A non-internal
|
||||
`LNumber::fromString()` method has been added instead.
|
||||
|
||||
Version 2.0.1 (2016-02-28)
|
||||
--------------------------
|
||||
|
||||
### Fixed
|
||||
|
||||
* `declare() {}` and `declare();` are not semantically equivalent and will now result in different
|
||||
ASTs. The format case will have an empty `stmts` array, while the latter will set `stmts` to
|
||||
`null`.
|
||||
* Magic constants are now supported as semi-reserved keywords.
|
||||
* A shebang line like `#!/usr/bin/env php` is now allowed at the start of a namespaced file.
|
||||
Previously this generated an exception.
|
||||
* The `prettyPrintFile()` method will not strip a trailing `?>` from the raw data that follows a
|
||||
`__halt_compiler()` statement.
|
||||
* The `prettyPrintFile()` method will not strip an opening `<?php` if the file starts with a
|
||||
comment followed by InlineHTML.
|
||||
|
||||
Version 2.0.0 (2015-12-04)
|
||||
--------------------------
|
||||
|
||||
### Changed
|
||||
|
||||
* String parts of encapsed strings are now represented using `Scalar\EncapsStringPart` nodes.
|
||||
Previously raw strings were used. This affects the `parts` child of `Scalar\Encaps` and
|
||||
`Expr\ShellExec`. The change has been done to allow assignment of attributes to encapsed string
|
||||
parts.
|
||||
|
||||
Version 2.0.0-beta1 (2015-10-21)
|
||||
--------------------------------
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed issue with too many newlines being stripped at the end of heredoc/nowdoc strings in some
|
||||
cases. (#227)
|
||||
|
||||
Version 1.4.0 (2015-07-14)
|
||||
--------------------------
|
||||
### Changed
|
||||
|
||||
* Update group use support to be in line with recent PHP 7.0 builds.
|
||||
* Renamed `php-parse.php` to `php-parse` and registered it as a composer bin.
|
||||
* Use composer PSR-4 autoloader instead of custom autoloader.
|
||||
* Specify phpunit as a dev dependency.
|
||||
|
||||
### Added
|
||||
|
||||
* Added interface `PhpParser\Node\FunctionLike`, which is implemented by `Stmt\ClassMethod`,
|
||||
`Stmt\Function_` and `Expr\Closure` nodes. This interface provides getters for their common
|
||||
subnodes.
|
||||
* Added `Node\Stmt\ClassLike::getMethod()` to look up a specific method on a class/interface/trait.
|
||||
* Added `shortArraySyntax` option to pretty printer, to print all arrays using short syntax.
|
||||
|
||||
### Fixed
|
||||
Version 2.0.0-alpha1 (2015-07-14)
|
||||
---------------------------------
|
||||
|
||||
* Fixed `isPublic()` return value for implicitly public properties and methods that define and
|
||||
additional modifier like `static` or `abstract`.
|
||||
* Properties are now accepted by the trait builder.
|
||||
* Fixed `__HALT_COMPILER_OFFSET__` support on HHVM.
|
||||
A more detailed description of backwards incompatible changes can be found in the
|
||||
[upgrading guide](UPGRADE-2.0.md).
|
||||
|
||||
Version 1.3.0 (2015-05-02)
|
||||
--------------------------
|
||||
### Removed
|
||||
|
||||
* Removed support for running on PHP 5.3. It is however still possible to parse PHP 5.2 and PHP 5.3
|
||||
code while running on a newer version.
|
||||
* Removed legacy class name aliases. This includes the old non-namespaced class names and the old
|
||||
names for classes that were renamed for PHP 7 compatibility.
|
||||
* Removed support for legacy node format. All nodes must have a `getSubNodeNames()` method now.
|
||||
|
||||
### Added
|
||||
|
||||
* Errors can now store the attributes of the node/token where the error occurred. Previously only the start line was
|
||||
stored.
|
||||
* If file positions are enabled in the lexer, errors can now provide column information if it is available. See
|
||||
[documentation](https://github.com/nikic/PHP-Parser/blob/master/doc/component/Error.markdown#column-information).
|
||||
* The parser now provides an experimental error recovery mode, which can be enabled by disabling the `throwOnError`
|
||||
parser option. In this mode the parser will try to construct a partial AST even if the code is not valid PHP. See
|
||||
[documentation](https://github.com/nikic/PHP-Parser/blob/master/doc/component/Error.markdown#error-recovery).
|
||||
* Added support for PHP 7 `yield from` expression. It is represented by `Expr\YieldFrom`.
|
||||
* Added support for PHP 7 anonymous classes. These are represented by ordinary `Stmt\Class_` nodes with the name set to
|
||||
`null`. Furthermore this implies that `Expr\New_` can now contain a `Stmt\Class_` in its `class` subnode.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed registration of PHP 7 aliases, for the case where the old name was used before the new name.
|
||||
* Fixed handling of precedence when pretty-printing `print` expressions.
|
||||
* Floating point numbers are now pretty-printed with a higher precision.
|
||||
* Checks for special class names like `self` are now case-insensitive.
|
||||
|
||||
Version 1.2.2 (2015-04-03)
|
||||
--------------------------
|
||||
|
||||
* The `NameResolver` now resolves parameter type hints when entering the function/method/closure node. As such other
|
||||
visitors running after it will be able to make use of the resolved names at that point already.
|
||||
* The autoloader no longer sets the `unserialize_callback_func` ini option on registration - this is not necessary and
|
||||
may cause issues when running PhpUnit tests with process isolation.
|
||||
|
||||
Version 1.2.1 (2015-03-24)
|
||||
--------------------------
|
||||
|
||||
* Fixed registration of the aliases introduced in 1.2.0. Previously the old class names could not be used in
|
||||
`instanceof` checks under some circumstances.
|
||||
|
||||
Version 1.2.0 (2015-03-22)
|
||||
--------------------------
|
||||
* Added support for remaining PHP 7 features that were not present in 1.x:
|
||||
* Group use declarations. These are represented using `Stmt\GroupUse` nodes. Furthermore a `type`
|
||||
attribute was added to `Stmt\UseUse` to handle mixed group use declarations.
|
||||
* Uniform variable syntax.
|
||||
* Generalized yield operator.
|
||||
* Scalar type declarations. These are presented using `'bool'`, `'int'`, `'float'` and `'string'`
|
||||
as the type. The PHP 5 parser also accepts these, however they'll be `Name` instances there.
|
||||
* Unicode escape sequences.
|
||||
* Added `PhpParser\ParserFactory` class, which should be used to create parser instances.
|
||||
* Added `Name::concat()` which concatenates two names.
|
||||
* Added `Name->slice()` which takes a subslice of a name.
|
||||
|
||||
### Changed
|
||||
|
||||
* To ensure compatibility with PHP 7, the following node classes have been renamed:
|
||||
|
||||
OLD => NEW
|
||||
PhpParser\Node\Expr\Cast\Bool => PhpParser\Node\Expr\Cast\Bool_
|
||||
PhpParser\Node\Expr\Cast\Int => PhpParser\Node\Expr\Cast\Int_
|
||||
PhpParser\Node\Expr\Cast\Object => PhpParser\Node\Expr\Cast\Object_
|
||||
PhpParser\Node\Expr\Cast\String => PhpParser\Node\Expr\Cast\String_
|
||||
PhpParser\Node\Scalar\String => PhpParser\Node\Scalar\String_
|
||||
|
||||
**The previous class names are still supported as aliases.** However it is strongly encouraged to use the new names
|
||||
in order to make your code compatible with PHP 7.
|
||||
|
||||
* Subnodes are now stored using real properties instead of an array. This improves performance and memory usage of the
|
||||
initial parse and subsequent node tree operations. The `NodeAbstract` class still supports the old way of specifying
|
||||
subnodes, however this is *deprecated*. In any case properties that are assigned to a node after creation will no
|
||||
longer be considered as subnodes.
|
||||
|
||||
* Methods and property declarations will no longer set the `Stmt\Class_::MODIFIER_PUBLIC` flag if no visibility is
|
||||
explicitly given. However the `isPublic()` method will continue to return true. This allows you to distinguish whether
|
||||
a method/property is explicitly or implicitly public and control the pretty printer output more precisely.
|
||||
|
||||
* The `Stmt\Class_`, `Stmt\Interface_` and `Stmt\Trait_` nodes now inherit from `Stmt\ClassLike`, which provides a
|
||||
`getMethods()` method. Previously this method was only available on `Stmt\Class_`.
|
||||
|
||||
* Support including the `bootstrap.php` file multiple times.
|
||||
|
||||
* Make documentation and tests part of the release tarball again.
|
||||
|
||||
* Improve support for HHVM and PHP 7.
|
||||
|
||||
### Added
|
||||
|
||||
* Added support for PHP 7 return type declarations. This adds an additional `returnType` subnode to `Stmt\Function_`,
|
||||
`Stmt\ClassMethod` and `Expr\Closure`.
|
||||
|
||||
* Added support for the PHP 7 null coalesce operator `??`. The operator is represented by `Expr\BinaryOp\Coalesce`.
|
||||
|
||||
* Added support for the PHP 7 spaceship operator `<=>`. The operator is represented by `Expr\BinaryOp\Spaceship`.
|
||||
|
||||
* Added use builder.
|
||||
|
||||
* Added global namespace support to the namespace builder.
|
||||
|
||||
* Added a constructor flag to `NodeTraverser`, which disables cloning of nodes.
|
||||
|
||||
Version 1.1.0 (2015-01-18)
|
||||
--------------------------
|
||||
|
||||
* Methods that do not specify an explicit visibility (e.g. `function method()`) will now have the `MODIFIER_PUBLIC`
|
||||
flag set. This also means that their `isPublic()` method will return true.
|
||||
|
||||
* Declaring a property as abstract or final is now an error.
|
||||
|
||||
* The `Lexer` and `Lexer\Emulative` classes now accept an `$options` array in their constructors. Currently only the
|
||||
`usedAttributes` option is supported, which determines which attributes will be added to AST nodes. In particular
|
||||
it is now possible to add information on the token and file positions corresponding to a node. For more details see
|
||||
the [Lexer component](https://github.com/nikic/PHP-Parser/blob/master/doc/component/Lexer.markdown) documentation.
|
||||
|
||||
* Node visitors can now return `NodeTraverser::DONT_TRAVERSE_CHILDREN` from `enterNode()` in order to skip all children
|
||||
of the current node, for all visitors.
|
||||
|
||||
* Added builders for traits and namespaces.
|
||||
|
||||
* The class, interface, trait, function, method and property builders now support adding doc comments using the
|
||||
`setDocComment()` method.
|
||||
|
||||
* Added support for fully-qualified and namespace-relative names in builders. No longer allow use of name component
|
||||
arrays.
|
||||
|
||||
* Do not add documentation and tests to distribution archive files.
|
||||
|
||||
Version 1.0.2 (2014-11-04)
|
||||
--------------------------
|
||||
|
||||
* The `NameResolver` visitor now also resolves names in trait adaptations (aliases and precedence declarations).
|
||||
|
||||
* Remove stray whitespace when pretty-printing trait adaptations that only change visibility.
|
||||
|
||||
Version 1.0.1 (2014-10-14)
|
||||
--------------------------
|
||||
|
||||
* Disallow `new` expressions without a class name. Previously `new;` was accidentally considered to be valid code.
|
||||
|
||||
* Support T_ONUMBER token used by HHVM.
|
||||
|
||||
* Add ability to directly pass code to the `php-parse.php` script.
|
||||
|
||||
* Prevent truncation of `var_dump()` output in the `php-parse.php` script if XDebug is used.
|
||||
|
||||
Version 1.0.0 (2014-09-12)
|
||||
--------------------------
|
||||
|
||||
* [BC] Removed deprecated `Template` and `TemplateLoader` classes.
|
||||
|
||||
* Fixed XML unserializer to properly work with new namespaced node names.
|
||||
|
||||
Version 1.0.0-beta2 (2014-08-31)
|
||||
--------------------------------
|
||||
|
||||
* [PHP 5.6] Updated support for constant scalar expressions to comply with latest changes. This means that arrays
|
||||
and array dimension fetches are now supported as well.
|
||||
|
||||
* [PHP 5.6] Direct array dereferencing of constants is supported now, i.e. both `FOO[0]` and `Foo::BAR[0]` are valid
|
||||
now.
|
||||
|
||||
* Fixed handling of special class names (`self`, `parent` and `static`) in the name resolver to be case insensitive.
|
||||
Additionally the name resolver now enforces that special class names are only used as unqualified names, e.g. `\self`
|
||||
is considered invalid.
|
||||
|
||||
* The case of references to the `static` class name is now preserved. Previously `static` was always lowercased,
|
||||
regardless of the case used in the source code.
|
||||
|
||||
* The autoloader now only requires a file if it exists. This allows usages like
|
||||
`class_exists('PhpParser\NotExistingClass')`.
|
||||
|
||||
* Added experimental `bin/php-parse.php` script, which is intended to help exploring and debugging the node tree.
|
||||
|
||||
* Separated the parser implemention (in `lib/PhpParser/ParserAbstract.php`) and the generated data (in
|
||||
`lib/PhpParser/Parser.php`). Furthermore the parser now uses meaningful variable names and contains comments
|
||||
explaining their usage.
|
||||
|
||||
Version 1.0.0-beta1 (2014-03-27)
|
||||
--------------------------------
|
||||
|
||||
* [BC] PHP-Parser now requires PHP 5.3 or newer to run. It is however still possible to *parse* PHP 5.2 source code,
|
||||
while running on a newer version.
|
||||
|
||||
* [BC] The library has been moved to use namespaces with the `PhpParser` vendor prefix. However, the old names using
|
||||
underscores are still available as aliases, as such most code should continue running on the new version without
|
||||
further changes.
|
||||
|
||||
However, code performing dispatch operations on `Node::getType()` may be affected by some of the name changes. For
|
||||
example a `+` node will now return type `Expr_BinaryOp_Plus` instead of `Expr_Plus`. In particular this may affect
|
||||
custom pretty printers.
|
||||
|
||||
Due to conflicts with reserved keywords, some class names now end with an underscore, e.g. `PHPParser_Node_Stmt_Class`
|
||||
is now `PhpParser\Node\Stmt\Class_`. (But as usual, the old name is still available)
|
||||
|
||||
* [PHP 5.6] Added support for the power operator `**` (node `Expr\BinaryOp\Pow`) and the compound power assignment
|
||||
operator `**=` (node `Expr\AssignOp\Pow`).
|
||||
|
||||
* [PHP 5.6] Added support for variadic functions: `Param` nodes now have `variadic` as a boolean subnode.
|
||||
|
||||
* [PHP 5.6] Added support for argument unpacking: `Arg` nodes now have `unpack` as a boolean subnode.
|
||||
|
||||
* [PHP 5.6] Added support for aliasing of functions and constants. `Stmt\Use_` nodes now have an integral `type`
|
||||
subnode, which is one of `Stmt\Use_::TYPE_NORMAL` (`use`), `Stmt\Use_::TYPE_FUNCTION` (`use function`) or
|
||||
`Stmt\Use_::TYPE_CONSTANT` (`use const`).
|
||||
|
||||
The `NameResolver` now also supports resolution of such aliases.
|
||||
|
||||
* [PHP 5.6] Added support for constant scalar expressions. This means that certain expressions are now allowed as the
|
||||
initializer for constants, properties, parameters, static variables, etc.
|
||||
|
||||
* [BC] Improved pretty printing of empty statements lists, which are now printed as `{\n}` instead of `{\n \n}`.
|
||||
This changes the behavior of the protected `PrettyPrinterAbstract::pStmts()` method, so custom pretty printing code
|
||||
making use it of may need to be adjusted.
|
||||
|
||||
* Changed the order of some subnodes to be consistent with their order in the sour code. For example `Stmt\If->cond`
|
||||
will now appear before `Stmt\If->stmts` etc.
|
||||
|
||||
* Added `Scalar\MagicConstant->getName()`, which returns the name of the magic constant (e.g. `__CLASS__`).
|
||||
|
||||
**The following changes are also included in 0.9.5**:
|
||||
|
||||
* [BC] Deprecated `PHPParser_Template` and `PHPParser_TemplateLoader`. This functionality does not belong in the main project
|
||||
and - as far as I know - nobody is using it.
|
||||
|
||||
* Add `NodeTraverser::removeVisitor()` method, which removes a visitor from the node traverser. This also modifies the
|
||||
corresponding `NodeTraverserInterface`.
|
||||
|
||||
* Fix alias resolution in `NameResolver`: Class names are now correctly handled as case-insensitive.
|
||||
|
||||
* The undefined variable error, which is used to the lexer to reset the error state, will no longer interfere with
|
||||
custom error handlers.
|
||||
* `PhpParser\Parser` is now an interface, implemented by `Parser\Php5`, `Parser\Php7` and
|
||||
`Parser\Multiple`. The `Multiple` parser will try multiple parsers, until one succeeds.
|
||||
* Token constants are now defined on `PhpParser\Parser\Tokens` rather than `PhpParser\Parser`.
|
||||
* The `Name->set()`, `Name->append()`, `Name->prepend()` and `Name->setFirst()` methods are
|
||||
deprecated in favor of `Name::concat()` and `Name->slice()`.
|
||||
* The `NodeTraverser` no longer clones nodes by default. The old behavior can be restored by
|
||||
passing `true` to the constructor.
|
||||
* The constructor for `Scalar` nodes no longer has a default value. E.g. `new LNumber()` should now
|
||||
be written as `new LNumber(0)`.
|
||||
|
||||
---
|
||||
|
||||
**This changelog only includes changes from the 1.0 series. For older changes see the [0.9 series changelog][1].**
|
||||
|
||||
[1]: https://github.com/nikic/PHP-Parser/blob/0.9/CHANGELOG.md
|
||||
**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
|
||||
[0.9 series changelog](https://github.com/nikic/PHP-Parser/blob/0.9/CHANGELOG.md).**
|
28
README.md
28
README.md
@ -1,12 +1,14 @@
|
||||
PHP Parser
|
||||
==========
|
||||
|
||||
This is a PHP 5.2 to PHP 5.6 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
[](https://travis-ci.org/nikic/PHP-Parser) [](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
|
||||
|
||||
This is a PHP 5.2 to PHP 7.1 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
manipulation.
|
||||
|
||||
[**Documentation for version 1.x**][doc_1_x] (stable; for running on PHP >= 5.3).
|
||||
[**Documentation for version 2.x**][doc_master] (stable; for running on PHP >= 5.4; for parsing PHP 5.2 to PHP 7.0).
|
||||
|
||||
[Documentation for version 0.9.x][doc_0_9] (unsupported; for running on PHP 5.2).
|
||||
[Documentation for version 1.x][doc_1_x] (unsupported; for running on PHP >= 5.3; for parsing PHP 5.2 to PHP 5.6).
|
||||
|
||||
In a Nutshell
|
||||
-------------
|
||||
@ -70,18 +72,26 @@ programming errors or security issues).
|
||||
Additionally, you can convert a syntax tree back to PHP code. This allows you to do code preprocessing
|
||||
(like automatedly porting code to older PHP versions).
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The preferred installation method is [composer](https://getcomposer.org):
|
||||
|
||||
php composer.phar require nikic/php-parser
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
1. [Introduction](doc/0_Introduction.markdown)
|
||||
2. [Installation](doc/1_Installation.markdown)
|
||||
3. [Usage of basic components](doc/2_Usage_of_basic_components.markdown)
|
||||
4. [Other node tree representations](doc/3_Other_node_tree_representations.markdown)
|
||||
5. [Code generation](doc/4_Code_generation.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)
|
||||
|
||||
Component documentation:
|
||||
|
||||
1. [Lexer](doc/component/Lexer.markdown)
|
||||
1. [Error](doc/component/Error.markdown)
|
||||
2. [Lexer](doc/component/Lexer.markdown)
|
||||
|
||||
[doc_0_9]: https://github.com/nikic/PHP-Parser/tree/0.9/doc
|
||||
[doc_1_x]: https://github.com/nikic/PHP-Parser/tree/1.x/doc
|
||||
[doc_2_x]: https://github.com/nikic/PHP-Parser/tree/2.x/doc
|
||||
[doc_master]: https://github.com/nikic/PHP-Parser/tree/master/doc
|
||||
|
74
UPGRADE-2.0.md
Normal file
74
UPGRADE-2.0.md
Normal file
@ -0,0 +1,74 @@
|
||||
Upgrading from PHP-Parser 1.x to 2.0
|
||||
====================================
|
||||
|
||||
### PHP version requirements
|
||||
|
||||
PHP-Parser now requires PHP 5.4 or newer to run. It is however still possible to *parse* PHP 5.2 and
|
||||
PHP 5.3 source code, while running on a newer version.
|
||||
|
||||
### Creating a parser instance
|
||||
|
||||
Parser instances should now be created through the `ParserFactory`. Old direct instantiation code
|
||||
will not work, because the parser class was renamed.
|
||||
|
||||
Old:
|
||||
|
||||
```php
|
||||
use PhpParser\Parser, PhpParser\Lexer;
|
||||
$parser = new Parser(new Lexer\Emulative);
|
||||
```
|
||||
|
||||
New:
|
||||
|
||||
```php
|
||||
use PhpParser\ParserFactory;
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||
```
|
||||
|
||||
The first argument to `ParserFactory` determines how different PHP versions are handled. The
|
||||
possible values are:
|
||||
|
||||
* `ParserFactory::PREFER_PHP7`: Try to parse code as PHP 7. If this fails, try to parse it as PHP 5.
|
||||
* `ParserFactory::PREFER_PHP5`: Try to parse code as PHP 5. If this fails, try to parse it as PHP 7.
|
||||
* `ParserFactory::ONLY_PHP7`: Parse code as PHP 7.
|
||||
* `ParserFactory::ONLY_PHP5`: Parse code as PHP 5.
|
||||
|
||||
For most practical purposes the difference between `PREFER_PHP7` and `PREFER_PHP5` is mainly whether
|
||||
a scalar type hint like `string` will be stored as `'string'` (PHP 7) or as `new Name('string')`
|
||||
(PHP 5).
|
||||
|
||||
To use a custom lexer, pass it as the second argument to the `create()` method:
|
||||
|
||||
```php
|
||||
use PhpParser\ParserFactory;
|
||||
$lexer = new MyLexer;
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7, $lexer);
|
||||
```
|
||||
|
||||
### Rename of the `PhpParser\Parser` class
|
||||
|
||||
`PhpParser\Parser` is now an interface, which is implemented by `Parser\Php5`, `Parser\Php7` and
|
||||
`Parser\Multiple`. Parser tokens are now defined in `Parser\Tokens`. If you use the `ParserFactory`
|
||||
described above to create your parser instance, these changes should have no further impact on you.
|
||||
|
||||
### Removal of legacy aliases
|
||||
|
||||
All legacy aliases for classes have been removed. This includes the old non-namespaced `PHPParser_`
|
||||
classes, as well as the classes that had to be renamed for PHP 7 support.
|
||||
|
||||
### Deprecations
|
||||
|
||||
The `set()`, `setFirst()`, `append()` and `prepend()` methods of the `Node\Name` class have been
|
||||
deprecated. Instead `Name::concat()` and `Name->slice()` should be used.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* The `NodeTraverser` no longer clones nodes by default. If you want to restore the old behavior,
|
||||
pass `true` to the constructor.
|
||||
* The legacy node format has been removed. If you use custom nodes, they are now expected to
|
||||
implement a `getSubNodeNames()` method.
|
||||
* The default value for `Scalar` node constructors was removed. This means that something like
|
||||
`new LNumber()` should be replaced by `new LNumber(0)`.
|
||||
* String parts of encapsed strings are now represented using `Scalar\EncapsStringPart` nodes, while
|
||||
previously raw strings were used. This affects the `parts` child of `Scalar\Encaps` and
|
||||
`Expr\ShellExec`.
|
48
UPGRADE-3.0.md
Normal file
48
UPGRADE-3.0.md
Normal file
@ -0,0 +1,48 @@
|
||||
Upgrading from PHP-Parser 2.x to 3.0
|
||||
====================================
|
||||
|
||||
This version does not include any major API changes. Only specific details of the node
|
||||
representation have changed in some cases.
|
||||
|
||||
### PHP version requirements
|
||||
|
||||
PHP-Parser now requires PHP 5.5 or newer to run. It is however still possible to *parse* PHP 5.2,
|
||||
5.3 and 5.4 source code, while running on a newer version.
|
||||
|
||||
### Changes to the node structure
|
||||
|
||||
The following changes are likely to require code changes if the respective nodes are used:
|
||||
|
||||
* The `List` subnode `vars` has been renamed to `items` and now contains `ArrayItem`s instead of
|
||||
plain variables.
|
||||
* The `Catch` subnode `type` has been renamed to `types` and is now an array of `Name`s.
|
||||
* The `TryCatch` subnode `finallyStmts` has been replaced with a `finally` subnode that holds an
|
||||
explicit `Finally` node.
|
||||
* The `type` subnode on `Class`, `ClassMethod` and `Property` has been renamed to `flags`. The
|
||||
`type` subnode has retained for backwards compatibility and is populated to the same value as
|
||||
`flags`. However, writes to `type` will not update `flags` and use of `type` is discouraged.
|
||||
|
||||
The following changes are unlikely to require code changes:
|
||||
|
||||
* The `ClassConst` constructor changed to accept an additional `flags` subnode.
|
||||
* The `Trait` constructor now has the same form as the `Class` and `Interface` constructors: It
|
||||
takes an array of subnodes. Unlike classes/interfaces, traits can only have a `stmts` subnode.
|
||||
* The `Array` subnode `items` may now contain `null` elements (due to destructuring).
|
||||
* `void` and `iterable` types are now stored as strings if the PHP 7 parser is used. Previously
|
||||
these would have been represented as `Name` instances.
|
||||
|
||||
### Removed methods
|
||||
|
||||
The following methods have been removed:
|
||||
|
||||
* `Comment::setLine()`, `Comment::setText()`: Create new `Comment` instances instead.
|
||||
* `Name::set()`, `Name::setFirst()`, `Name::setLast()`: Create new `Name` instances instead. For
|
||||
the latter two a combination of `Name::concat()` and `Name::slice()` can be used.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* All methods on `PrettyPrinter\Standard` are now protected. Previoulsy most of them were public.
|
||||
The pretty printer should only be invoked using the `prettyPrint()`, `prettyPrintFile()` and
|
||||
`prettyPrintExpr()` methods.
|
||||
* The node dumper now prints numeric values that act as enums/flags in a string representation.
|
||||
If node dumper results are used in tests, updates may be needed to account for this.
|
@ -1,7 +1,12 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../lib/bootstrap.php';
|
||||
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ini_set('xdebug.max_nesting_level', 3000);
|
||||
|
||||
@ -22,10 +27,14 @@ if (empty($files)) {
|
||||
}
|
||||
|
||||
$lexer = new PhpParser\Lexer\Emulative(array('usedAttributes' => array(
|
||||
'startLine', 'endLine', 'startFilePos', 'endFilePos'
|
||||
'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'
|
||||
)));
|
||||
$parser = new PhpParser\Parser($lexer);
|
||||
$dumper = new PhpParser\NodeDumper;
|
||||
$parser = (new PhpParser\ParserFactory)->create(
|
||||
PhpParser\ParserFactory::PREFER_PHP7,
|
||||
$lexer,
|
||||
array('throwOnError' => !$attributes['with-recovery'])
|
||||
);
|
||||
$dumper = new PhpParser\NodeDumper(['dumpComments' => true]);
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$serializer = new PhpParser\Serializer\XML;
|
||||
|
||||
@ -47,17 +56,15 @@ foreach ($files as $file) {
|
||||
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
} catch (PhpParser\Error $e) {
|
||||
if ($attributes['with-column-info'] && $e->hasColumnInfo()) {
|
||||
$startLine = $e->getStartLine();
|
||||
$endLine = $e->getEndLine();
|
||||
$startColumn = $e->getStartColumn($code);
|
||||
$endColumn = $e->getEndColumn($code);
|
||||
$message .= $e->getRawMessage() . " from $startLine:$startColumn to $endLine:$endColumn";
|
||||
} else {
|
||||
$message = $e->getMessage();
|
||||
foreach ($parser->getErrors() as $error) {
|
||||
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
|
||||
echo $message . "\n";
|
||||
}
|
||||
|
||||
if (null === $stmts) {
|
||||
continue;
|
||||
}
|
||||
} catch (PhpParser\Error $error) {
|
||||
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
|
||||
die($message . "\n");
|
||||
}
|
||||
|
||||
@ -81,11 +88,25 @@ foreach ($files as $file) {
|
||||
}
|
||||
}
|
||||
|
||||
function showHelp($error) {
|
||||
die($error . "\n\n" .
|
||||
<<<OUTPUT
|
||||
Usage: php php-parse.php [operations] file1.php [file2.php ...]
|
||||
or: php php-parse.php [operations] "<?php code"
|
||||
function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) {
|
||||
if ($withColumnInfo && $e->hasColumnInfo()) {
|
||||
$startLine = $e->getStartLine();
|
||||
$endLine = $e->getEndLine();
|
||||
$startColumn = $e->getStartColumn($code);
|
||||
$endColumn = $e->getEndColumn($code);
|
||||
return $e->getRawMessage() . " from $startLine:$startColumn to $endLine:$endColumn";
|
||||
} else {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
function showHelp($error = '') {
|
||||
if ($error) {
|
||||
echo $error . "\n\n";
|
||||
}
|
||||
die(<<<OUTPUT
|
||||
Usage: php-parse [operations] file1.php [file2.php ...]
|
||||
or: php-parse [operations] "<?php code"
|
||||
Turn PHP source code into an abstract syntax tree.
|
||||
|
||||
Operations is a list of the following options (--dump by default):
|
||||
@ -96,9 +117,11 @@ Operations is a list of the following options (--dump by default):
|
||||
--var-dump var_dump() nodes (for exact structure)
|
||||
-N, --resolve-names Resolve names using NodeVisitor\NameResolver
|
||||
-c, --with-column-info Show column-numbers for errors (if available)
|
||||
-r, --with-recovery Use parsing with error recovery
|
||||
-h, --help Display this page
|
||||
|
||||
Example:
|
||||
php php-parse.php -d -p -N -d file.php
|
||||
php-parse -d -p -N -d file.php
|
||||
|
||||
Dumps nodes, pretty prints them, then resolves names and dumps them again.
|
||||
|
||||
@ -111,7 +134,8 @@ function parseArgs($args) {
|
||||
$operations = array();
|
||||
$files = array();
|
||||
$attributes = array(
|
||||
'with-column-info' => false,
|
||||
'with-column-info' => false,
|
||||
'with-recovery' => false,
|
||||
);
|
||||
|
||||
array_shift($args);
|
||||
@ -145,6 +169,14 @@ function parseArgs($args) {
|
||||
case '-c';
|
||||
$attributes['with-column-info'] = true;
|
||||
break;
|
||||
case '--with-recovery':
|
||||
case '-r':
|
||||
$attributes['with-recovery'] = true;
|
||||
break;
|
||||
case '--help':
|
||||
case '-h';
|
||||
showHelp();
|
||||
break;
|
||||
case '--':
|
||||
$parseOptions = false;
|
||||
break;
|
@ -10,15 +10,21 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"php": ">=5.5",
|
||||
"ext-tokenizer": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"files": ["lib/bootstrap.php"]
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpParser\\": "lib/PhpParser"
|
||||
}
|
||||
},
|
||||
"bin": ["bin/php-parse"],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4-dev"
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
This project is a PHP 5.2 to PHP 5.6 parser **written in PHP itself**.
|
||||
This project is a PHP 5.2 to PHP 7.1 parser **written in PHP itself**.
|
||||
|
||||
What is this for?
|
||||
-----------------
|
||||
@ -18,7 +18,7 @@ For example an AST abstracts away the fact that in PHP variables can be written
|
||||
as `$$bar`, `${'foobar'}` or even `${!${''}=barfoo()}`. You don't have to worry about recognizing
|
||||
all the different syntaxes from a stream of tokens.
|
||||
|
||||
Another questions is: Why would I want to have a PHP parser *written in PHP*? Well, PHP might not be
|
||||
Another question is: Why would I want to have a PHP parser *written in PHP*? Well, PHP might not be
|
||||
a language especially suited for fast parsing, but processing the AST is much easier in PHP than it
|
||||
would be in other, faster languages like C. Furthermore the people most probably wanting to do
|
||||
programmatic PHP code analysis are incidentally PHP developers, not C developers.
|
||||
@ -26,13 +26,12 @@ programmatic PHP code analysis are incidentally PHP developers, not C developers
|
||||
What can it parse?
|
||||
------------------
|
||||
|
||||
The parser uses a PHP 5.6 compliant grammar, which is backwards compatible with all PHP version from PHP 5.2
|
||||
upwards (and maybe older).
|
||||
The parser supports parsing PHP 5.2-5.6 and PHP 7.
|
||||
|
||||
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 new tokens from 5.3, 5.4, 5.5 and 5.6 is provided.
|
||||
This allows to parse PHP 5.6 source code running on PHP 5.3, for example. This emulation is very hacky and not
|
||||
perfect, but it should work well on any sane code.
|
||||
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
|
||||
hacky and not perfect, but it should work well on any sane code.
|
||||
|
||||
What output does it produce?
|
||||
----------------------------
|
||||
|
@ -1,31 +0,0 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
There are multiple ways to include the PHP parser into your project:
|
||||
|
||||
Installing via Composer
|
||||
-----------------------
|
||||
|
||||
Run the following command inside your project:
|
||||
|
||||
php composer.phar require nikic/php-parser
|
||||
|
||||
If you haven't installed [Composer][1] yet, you can do so using:
|
||||
|
||||
curl -s http://getcomposer.org/installer | php
|
||||
|
||||
Installing as a Git Submodule
|
||||
-----------------------------
|
||||
|
||||
Run the following command to install the parser into the `vendor/PHP-Parser` folder:
|
||||
|
||||
git submodule add git://github.com/nikic/PHP-Parser.git vendor/PHP-Parser
|
||||
|
||||
Installing from the Zip- or Tarball
|
||||
-----------------------------------
|
||||
|
||||
Download the latest version from [the download page][2], unpack it and move the files somewhere into your project.
|
||||
|
||||
|
||||
[1]: https://getcomposer.org/
|
||||
[2]: https://github.com/nikic/PHP-Parser/tags
|
@ -6,55 +6,60 @@ This document explains how to use the parser, the pretty printer and the node tr
|
||||
Bootstrapping
|
||||
-------------
|
||||
|
||||
The library needs to register a class autoloader. You can either use the `vendor/autoload.php` file generated by
|
||||
Composer or by including the bundled `lib/bootstrap.php` file:
|
||||
To bootstrap the library, include the autoloader generated by composer:
|
||||
|
||||
```php
|
||||
<?php
|
||||
require 'path/to/PHP-Parser/lib/bootstrap.php';
|
||||
// Or, if you're using Composer:
|
||||
require 'path/to/vendor/autoload.php';
|
||||
```
|
||||
|
||||
Additionally you may want to set the `xdebug.max_nesting_level` ini option to a higher value:
|
||||
|
||||
```php
|
||||
<?php
|
||||
ini_set('xdebug.max_nesting_level', 3000);
|
||||
```
|
||||
|
||||
This ensures that there will be no errors when traversing highly nested node trees.
|
||||
This ensures that there will be no errors when traversing highly nested node trees. However, it is
|
||||
preferable to disable XDebug completely, as it can easily make this library more than five times
|
||||
slower.
|
||||
|
||||
Parsing
|
||||
-------
|
||||
|
||||
In order to parse some source code you first have to create a `PhpParser\Parser` object, which
|
||||
needs to be passed a `PhpParser\Lexer` instance:
|
||||
In order to parse code, you first have to create a parser instance:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer);
|
||||
// or
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
|
||||
use PhpParser\ParserFactory;
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||
```
|
||||
|
||||
Use of the emulative lexer is required if you want to parse PHP code from newer versions than the one
|
||||
you're running on. For example it will allow you to parse PHP 5.6 code while running on PHP 5.3.
|
||||
The factory accepts a kind argument, that determines how different PHP versions are treated:
|
||||
|
||||
Kind | Behavior
|
||||
-----|---------
|
||||
`ParserFactory::PREFER_PHP7` | Try to parse code as PHP 7. If this fails, try to parse it as PHP 5.
|
||||
`ParserFactory::PREFER_PHP5` | Try to parse code as PHP 5. If this fails, try to parse it as PHP 7.
|
||||
`ParserFactory::ONLY_PHP7` | Parse code as PHP 7.
|
||||
`ParserFactory::ONLY_PHP5` | Parse code as PHP 5.
|
||||
|
||||
Unless you have 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
|
||||
that require customized lexers are discussed in the [lexer documentation](component/Lexer.markdown).
|
||||
|
||||
Subsequently you can pass PHP code (including the opening `<?php` tag) to the `parse` method in order to
|
||||
create a syntax tree. If a syntax error is encountered, an `PhpParser\Error` exception will be thrown:
|
||||
|
||||
```php
|
||||
<?php
|
||||
$code = '<?php // some code';
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
|
||||
$code = '<?php // some code';
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
// $stmts is an array of statement nodes
|
||||
} catch (PhpParser\Error $e) {
|
||||
} catch (Error $e) {
|
||||
echo 'Parse Error: ', $e->getMessage();
|
||||
}
|
||||
```
|
||||
@ -137,11 +142,14 @@ information the formatting is done using a specified scheme. Currently there is
|
||||
namely `PhpParser\PrettyPrinter\Standard`.
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ParserFactory;
|
||||
use PhpParser\PrettyPrinter;
|
||||
|
||||
$code = "<?php echo 'Hi ', hi\\getTarget();";
|
||||
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer);
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||
$prettyPrinter = new PrettyPrinter\Standard;
|
||||
|
||||
try {
|
||||
// parse
|
||||
@ -158,7 +166,7 @@ try {
|
||||
$code = $prettyPrinter->prettyPrint($stmts);
|
||||
|
||||
echo $code;
|
||||
} catch (PhpParser\Error $e) {
|
||||
} catch (Error $e) {
|
||||
echo 'Parse Error: ', $e->getMessage();
|
||||
}
|
||||
```
|
||||
@ -188,11 +196,13 @@ For this purpose the parser provides a component for traversing and visiting the
|
||||
structure of a program using this `PhpParser\NodeTraverser` looks like this:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\ParserFactory;
|
||||
use PhpParser\PrettyPrinter;
|
||||
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
|
||||
$traverser = new PhpParser\NodeTraverser;
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||
$traverser = new NodeTraverser;
|
||||
$prettyPrinter = new PrettyPrinter\Standard;
|
||||
|
||||
// add your visitor
|
||||
$traverser->addVisitor(new MyNodeVisitor);
|
||||
@ -218,10 +228,10 @@ try {
|
||||
The corresponding node visitor might look like this:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
class MyNodeVisitor extends PhpParser\NodeVisitorAbstract
|
||||
class MyNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
public function leaveNode(Node $node) {
|
||||
if ($node instanceof Node\Scalar\String_) {
|
||||
@ -236,10 +246,12 @@ The above node visitor would change all string literals in the program to `'foo'
|
||||
All visitors must implement the `PhpParser\NodeVisitor` interface, which defines the following four
|
||||
methods:
|
||||
|
||||
public function beforeTraverse(array $nodes);
|
||||
public function enterNode(PhpParser\Node $node);
|
||||
public function leaveNode(PhpParser\Node $node);
|
||||
public function afterTraverse(array $nodes);
|
||||
```php
|
||||
public function beforeTraverse(array $nodes);
|
||||
public function enterNode(\PhpParser\Node $node);
|
||||
public function leaveNode(\PhpParser\Node $node);
|
||||
public function afterTraverse(array $nodes);
|
||||
```
|
||||
|
||||
The `beforeTraverse()` method is called once before the traversal begins and is passed the nodes the
|
||||
traverser was called with. This method can be used for resetting values before traversation or
|
||||
@ -258,7 +270,7 @@ The `enterNode()` method can additionally return the value `NodeTraverser::DONT_
|
||||
which instructs the traverser to skip all children of the current node.
|
||||
|
||||
The `leaveNode()` method can additionally return the value `NodeTraverser::REMOVE_NODE`, in which
|
||||
case the current node will be removed from the parent array. Furthermove it is possible to return
|
||||
case the current node will be removed from the parent array. Furthermore it is possible to return
|
||||
an array of nodes, which will be merged into the parent array at the offset of the current node.
|
||||
I.e. if in `array(A, B, C)` the node `B` should be replaced with `array(X, Y, Z)` the result will
|
||||
be `array(A, X, Y, Z, C)`.
|
||||
@ -299,20 +311,24 @@ assume that no dynamic features are used.
|
||||
We start off with the following base code:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\ParserFactory;
|
||||
use PhpParser\PrettyPrinter;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitor\NameResolver;
|
||||
|
||||
$inDir = '/some/path';
|
||||
$outDir = '/some/other/path';
|
||||
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
|
||||
$traverser = new PhpParser\NodeTraverser;
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||
$traverser = new NodeTraverser;
|
||||
$prettyPrinter = new PrettyPrinter\Standard;
|
||||
|
||||
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver); // we will need resolved names
|
||||
$traverser->addVisitor(new NodeVisitor\NamespaceConverter); // our own node visitor
|
||||
$traverser->addVisitor(new NameResolver); // we will need resolved names
|
||||
$traverser->addVisitor(new NamespaceConverter); // our own node visitor
|
||||
|
||||
// iterate over all .php files in the directory
|
||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($inDir));
|
||||
$files = new RegexIterator($files, '/\.php$/');
|
||||
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($inDir));
|
||||
$files = new \RegexIterator($files, '/\.php$/');
|
||||
|
||||
foreach ($files as $file) {
|
||||
try {
|
||||
@ -343,9 +359,9 @@ Now lets start with the main code, the `NodeVisitor\NamespaceConverter`. One thi
|
||||
is convert `A\\B` style names to `A_B` style ones.
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\Node;
|
||||
class NodeVisitor_NamespaceConverter extends PhpParser\NodeVisitorAbstract
|
||||
|
||||
class NamespaceConverter extends \PhpParser\NodeVisitorAbstract
|
||||
{
|
||||
public function leaveNode(Node $node) {
|
||||
if ($node instanceof Node\Name) {
|
||||
@ -362,14 +378,14 @@ create a name with backslashes either write `$node->toString()` or `(string) $no
|
||||
a new name from the string and return it. Returning a new node replaces the old node.
|
||||
|
||||
Another thing we need to do is change the class/function/const declarations. Currently they contain
|
||||
only the shortname (i.e. the last part of the name), but they need to contain the complete name inclduing
|
||||
only the shortname (i.e. the last part of the name), but they need to contain the complete name including
|
||||
the namespace prefix:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
class NodeVisitor_NamespaceConverter extends PhpParser\NodeVisitorAbstract
|
||||
|
||||
class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract
|
||||
{
|
||||
public function leaveNode(Node $node) {
|
||||
if ($node instanceof Node\Name) {
|
||||
@ -392,10 +408,10 @@ There is not much more to it than converting the namespaced name to string with
|
||||
The last thing we need to do is remove the `namespace` and `use` statements:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
class NodeVisitor_NamespaceConverter extends PhpParser\NodeVisitorAbstract
|
||||
|
||||
class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract
|
||||
{
|
||||
public function leaveNode(Node $node) {
|
||||
if ($node instanceof Node\Name) {
|
||||
|
@ -17,7 +17,6 @@ Furthermore it is possible to dump nodes into a human readable format using the
|
||||
`PhpParser\NodeDumper`. This can be used for debugging.
|
||||
|
||||
```php
|
||||
<?php
|
||||
$code = <<<'CODE'
|
||||
<?php
|
||||
|
||||
@ -28,7 +27,7 @@ function printLine($msg) {
|
||||
printLine('Hello World!!!');
|
||||
CODE;
|
||||
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer);
|
||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7);
|
||||
$nodeDumper = new PhpParser\NodeDumper;
|
||||
|
||||
try {
|
||||
@ -106,7 +105,7 @@ function printLine($msg) {
|
||||
printLine('Hello World!!!');
|
||||
CODE;
|
||||
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer);
|
||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7);
|
||||
$serializer = new PhpParser\Serializer\XML;
|
||||
|
||||
try {
|
||||
|
@ -14,8 +14,11 @@ the following syntactic elements:
|
||||
Here is an example:
|
||||
|
||||
```php
|
||||
<?php
|
||||
$factory = new PhpParser\BuilderFactory;
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\PrettyPrinter;
|
||||
use PhpParser\Node;
|
||||
|
||||
$factory = new BuilderFactory;
|
||||
$node = $factory->namespace('Name\Space')
|
||||
->addStmt($factory->use('Some\Other\Thingy')->as('SomeOtherClass'))
|
||||
->addStmt($factory->class('SomeClass')
|
||||
@ -26,6 +29,7 @@ $node = $factory->namespace('Name\Space')
|
||||
->addStmt($factory->method('someMethod')
|
||||
->makePublic()
|
||||
->makeAbstract() // ->makeFinal()
|
||||
->setReturnType('bool')
|
||||
->addParam($factory->param('someParam')->setTypeHint('SomeClass'))
|
||||
->setDocComment('/**
|
||||
* This method does something.
|
||||
@ -38,7 +42,7 @@ $node = $factory->namespace('Name\Space')
|
||||
->makeProtected() // ->makePublic() [default], ->makePrivate()
|
||||
->addParam($factory->param('someParam')->setDefault('test'))
|
||||
// it is possible to add manually created nodes
|
||||
->addStmt(new PhpParser\Node\Expr\Print_(new PhpParser\Node\Expr\Variable('someParam')))
|
||||
->addStmt(new Node\Expr\Print_(new Node\Expr\Variable('someParam')))
|
||||
)
|
||||
|
||||
// properties will be correctly reordered above the methods
|
||||
@ -50,7 +54,7 @@ $node = $factory->namespace('Name\Space')
|
||||
;
|
||||
|
||||
$stmts = array($node);
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard();
|
||||
$prettyPrinter = new PrettyPrinter\Standard();
|
||||
echo $prettyPrinter->prettyPrintFile($stmts);
|
||||
```
|
||||
|
||||
@ -71,7 +75,7 @@ abstract class SomeClass extends SomeOtherClass implements A\Few, \Interfaces
|
||||
*
|
||||
* @param SomeClass And takes a parameter
|
||||
*/
|
||||
public abstract function someMethod(SomeClass $someParam);
|
||||
public abstract function someMethod(SomeClass $someParam) : bool;
|
||||
protected function anotherMethod($someParam = 'test')
|
||||
{
|
||||
print $someParam;
|
||||
|
@ -17,7 +17,7 @@ position attributes in the lexer need to be enabled:
|
||||
$lexer = new PhpParser\Lexer(array(
|
||||
'usedAttributes' => array('comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'),
|
||||
));
|
||||
$parser = new PhpParser\Parser($lexer);
|
||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
|
||||
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
@ -58,7 +58,7 @@ or `null` if recovery fails.
|
||||
A usage example:
|
||||
|
||||
```php
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer, array(
|
||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, null, array(
|
||||
'throwOnError' => false,
|
||||
));
|
||||
|
||||
|
@ -72,7 +72,7 @@ $lexer = new PhpParser\Lexer(array(
|
||||
'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'
|
||||
)
|
||||
));
|
||||
$parser = new PhpParser\Parser($lexer);
|
||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
|
||||
|
||||
$visitor = new MyNodeVisitor();
|
||||
$traverser = new PhpParser\NodeTraverser();
|
||||
@ -127,14 +127,17 @@ information about the formatting of integers (like decimal vs. hexadecimal) or s
|
||||
escape sequences). This can be remedied by storing the original value in an attribute:
|
||||
|
||||
```php
|
||||
class KeepOriginalValueLexer extends PHPParser\Lexer // or PHPParser\Lexer\Emulative
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Parser\Tokens;
|
||||
|
||||
class KeepOriginalValueLexer extends Lexer // or Lexer\Emulative
|
||||
{
|
||||
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
|
||||
$tokenId = parent::getNextToken($value, $startAttributes, $endAttributes);
|
||||
|
||||
if ($tokenId == PHPParser\Parser::T_CONSTANT_ENCAPSED_STRING // non-interpolated string
|
||||
|| $tokenId == PHPParser\Parser::T_LNUMBER // integer
|
||||
|| $tokenId == PHPParser\Parser::T_DNUMBER // floating point number
|
||||
if ($tokenId == Tokens::T_CONSTANT_ENCAPSED_STRING // non-interpolated string
|
||||
|| $tokenId == Tokens::T_LNUMBER // integer
|
||||
|| $tokenId == Tokens::T_DNUMBER // floating point number
|
||||
) {
|
||||
// could also use $startAttributes, doesn't really matter here
|
||||
$endAttributes['originalValue'] = $value;
|
||||
|
@ -1,28 +1,28 @@
|
||||
What do all those files mean?
|
||||
=============================
|
||||
|
||||
* `zend_language_parser.phpy`: PHP grammer written in a pseudo language
|
||||
* `analyze.php`: Analyzes the `.phpy`-grammer and outputs some info about it
|
||||
* `rebuildParser.php`: Preprocesses the `.phpy`-grammar and builds the parser using `kmyacc`
|
||||
* `kmyacc.php.parser`: A `kmyacc` parser prototype file for PHP
|
||||
* `php5.y`: PHP 5 grammar written in a pseudo language
|
||||
* `php7.y`: PHP 7 grammar written in a pseudo language
|
||||
* `tokens.y`: Tokens definition shared between PHP 5 and PHP 7 grammars
|
||||
* `parser.template`: A `kmyacc` parser prototype file for PHP
|
||||
* `tokens.template`: A `kmyacc` prototype file for the `Tokens` class
|
||||
* `rebuildParsers.php`: Preprocesses the grammar and builds the parser using `kmyacc`
|
||||
|
||||
.phpy pseudo language
|
||||
=====================
|
||||
|
||||
The `.phpy` file is a normal grammer in `kmyacc` (`yacc`) style, with some transformations
|
||||
The `.y` file is a normal grammer in `kmyacc` (`yacc`) style, with some transformations
|
||||
applied to it:
|
||||
|
||||
* Nodes are created using the syntax `Name[..., ...]`. This is transformed into
|
||||
`new Name(..., ..., attributes())`
|
||||
* Some function-like constructs are resolved (see `rebuildParser.php` for a list)
|
||||
* Associative arrays are written as `[key: value, ...]`, which is transformed to
|
||||
`array('key' => value, ...)`
|
||||
* Some function-like constructs are resolved (see `rebuildParsers.php` for a list)
|
||||
|
||||
Building the parser
|
||||
===================
|
||||
|
||||
In order to rebuild the parser, you need [moriyoshi's fork of kmyacc](https://github.com/moriyoshi/kmyacc-forked).
|
||||
After you compiled/installed it, run the `rebuildParser.php` script.
|
||||
After you compiled/installed it, run the `rebuildParsers.php` script.
|
||||
|
||||
By default only the `Parser.php` is built. If you want to additionally emit debug symbols and create `y.output`, run the
|
||||
script with `--debug`. If you want to retain the preprocessed grammar pass `--keep-tmp-grammar`.
|
||||
|
@ -1,96 +0,0 @@
|
||||
<?php
|
||||
|
||||
const GRAMMAR_FILE = './zend_language_parser.phpy';
|
||||
|
||||
const LIB = '(?(DEFINE)
|
||||
(?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
|
||||
(?<doubleQuotedString>"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
|
||||
(?<string>(?&singleQuotedString)|(?&doubleQuotedString))
|
||||
(?<comment>/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
|
||||
(?<code>\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
|
||||
)';
|
||||
|
||||
const RULE_BLOCK = '(?<name>[a-z_]++):(?<rules>[^\'"/{};]*+(?:(?:(?&string)|(?&comment)|(?&code)|/|})[^\'"/{};]*+)*+);';
|
||||
|
||||
$usedTerminals = array_flip(array(
|
||||
'T_VARIABLE', 'T_STRING', 'T_INLINE_HTML', 'T_ENCAPSED_AND_WHITESPACE',
|
||||
'T_LNUMBER', 'T_DNUMBER', 'T_CONSTANT_ENCAPSED_STRING', 'T_STRING_VARNAME', 'T_NUM_STRING'
|
||||
));
|
||||
$unusedNonterminals = array_flip(array(
|
||||
'case_separator', 'optional_comma'
|
||||
));
|
||||
|
||||
function regex($regex) {
|
||||
return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
|
||||
}
|
||||
|
||||
function magicSplit($regex, $string) {
|
||||
$pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
|
||||
|
||||
foreach ($pieces as &$piece) {
|
||||
$piece = trim($piece);
|
||||
}
|
||||
|
||||
return array_filter($pieces);
|
||||
}
|
||||
|
||||
echo '<pre>';
|
||||
|
||||
////////////////////
|
||||
////////////////////
|
||||
////////////////////
|
||||
|
||||
list($defs, $ruleBlocks) = magicSplit('%%', file_get_contents(GRAMMAR_FILE));
|
||||
|
||||
if ('' !== trim(preg_replace(regex(RULE_BLOCK), '', $ruleBlocks))) {
|
||||
die('Not all rule blocks were properly recognized!');
|
||||
}
|
||||
|
||||
preg_match_all(regex(RULE_BLOCK), $ruleBlocks, $ruleBlocksMatches, PREG_SET_ORDER);
|
||||
foreach ($ruleBlocksMatches as $match) {
|
||||
$ruleBlockName = $match['name'];
|
||||
$rules = magicSplit('\|', $match['rules']);
|
||||
|
||||
foreach ($rules as &$rule) {
|
||||
$parts = magicSplit('\s+', $rule);
|
||||
$usedParts = array();
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if ('{' === $part[0]) {
|
||||
preg_match_all('~\$([0-9]+)~', $part, $backReferencesMatches, PREG_SET_ORDER);
|
||||
foreach ($backReferencesMatches as $match) {
|
||||
$usedParts[$match[1]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$i = 1;
|
||||
foreach ($parts as &$part) {
|
||||
if ('/' === $part[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($usedParts[$i])) {
|
||||
if ('\'' === $part[0] || '{' === $part[0]
|
||||
|| (ctype_upper($part[0]) && !isset($usedTerminals[$part]))
|
||||
|| (ctype_lower($part[0]) && isset($unusedNonterminals[$part]))
|
||||
) {
|
||||
$part = '<span style="background-color: red; color: white;">' . $part . '</span>';
|
||||
} else {
|
||||
$part = '<strong><em>' . $part . '</em></strong>';
|
||||
}
|
||||
} elseif ((ctype_upper($part[0]) && isset($usedTerminals[$part]))
|
||||
|| (ctype_lower($part[0]) && !isset($unusedNonterminals[$part]))
|
||||
|
||||
) {
|
||||
$part = '<span style="background-color: blue; color: white;">' . $part . '</span>';
|
||||
}
|
||||
|
||||
++$i;
|
||||
}
|
||||
|
||||
$rule = implode(' ', $parts);
|
||||
}
|
||||
|
||||
echo $ruleBlockName, ':', "\n", ' ', implode("\n" . ' | ', $rules), "\n", ';', "\n\n";
|
||||
}
|
@ -5,16 +5,23 @@ $meta #
|
||||
#semval(%n) $this->stackPos-(%l-%n)
|
||||
#semval(%n,%t) $this->stackPos-(%l-%n)
|
||||
|
||||
namespace PhpParser;
|
||||
namespace PhpParser\Parser;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
#include;
|
||||
|
||||
/* This is an automatically GENERATED file, which should not be manually edited.
|
||||
* Instead edit one of the following:
|
||||
* * the grammar file grammar/zend_language_parser.phpy
|
||||
* * the skeleton file grammar/kymacc.php.parser
|
||||
* * the preprocessing script grammar/rebuildParser.php
|
||||
* * the grammar files grammar/php5.y or grammar/php7.y
|
||||
* * the skeleton file grammar/parser.template
|
||||
* * the preprocessing script grammar/rebuildParsers.php
|
||||
*/
|
||||
class Parser extends ParserAbstract
|
||||
class #(-p) extends \PhpParser\ParserAbstract
|
||||
{
|
||||
protected $tokenToSymbolMapSize = #(YYMAXLEX);
|
||||
protected $actionTableSize = #(YYLAST);
|
||||
@ -28,10 +35,6 @@ class Parser extends ParserAbstract
|
||||
protected $YY2TBLSTATE = #(YY2TBLSTATE);
|
||||
protected $YYNLSTATES = #(YYNLSTATES);
|
||||
|
||||
#tokenval
|
||||
const %s = %n;
|
||||
#endtokenval
|
||||
|
||||
protected $symbolToName = array(
|
||||
#listvar terminals
|
||||
);
|
@ -1,122 +1,7 @@
|
||||
%pure_parser
|
||||
%expect 2
|
||||
%expect 6
|
||||
|
||||
%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
|
||||
%left ','
|
||||
%left T_LOGICAL_OR
|
||||
%left T_LOGICAL_XOR
|
||||
%left T_LOGICAL_AND
|
||||
%right T_PRINT
|
||||
%right T_YIELD
|
||||
%right T_YIELD_FROM
|
||||
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL
|
||||
%left '?' ':'
|
||||
%right T_COALESCE
|
||||
%left T_BOOLEAN_OR
|
||||
%left T_BOOLEAN_AND
|
||||
%left '|'
|
||||
%left '^'
|
||||
%left '&'
|
||||
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
|
||||
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
|
||||
%left T_SL T_SR
|
||||
%left '+' '-' '.'
|
||||
%left '*' '/' '%'
|
||||
%right '!'
|
||||
%nonassoc T_INSTANCEOF
|
||||
%right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
|
||||
%right T_POW
|
||||
%right '['
|
||||
%nonassoc T_NEW T_CLONE
|
||||
%token T_EXIT
|
||||
%token T_IF
|
||||
%left T_ELSEIF
|
||||
%left T_ELSE
|
||||
%left T_ENDIF
|
||||
%token T_LNUMBER
|
||||
%token T_DNUMBER
|
||||
%token T_STRING
|
||||
%token T_STRING_VARNAME
|
||||
%token T_VARIABLE
|
||||
%token T_NUM_STRING
|
||||
%token T_INLINE_HTML
|
||||
%token T_CHARACTER
|
||||
%token T_BAD_CHARACTER
|
||||
%token T_ENCAPSED_AND_WHITESPACE
|
||||
%token T_CONSTANT_ENCAPSED_STRING
|
||||
%token T_ECHO
|
||||
%token T_DO
|
||||
%token T_WHILE
|
||||
%token T_ENDWHILE
|
||||
%token T_FOR
|
||||
%token T_ENDFOR
|
||||
%token T_FOREACH
|
||||
%token T_ENDFOREACH
|
||||
%token T_DECLARE
|
||||
%token T_ENDDECLARE
|
||||
%token T_AS
|
||||
%token T_SWITCH
|
||||
%token T_ENDSWITCH
|
||||
%token T_CASE
|
||||
%token T_DEFAULT
|
||||
%token T_BREAK
|
||||
%token T_CONTINUE
|
||||
%token T_GOTO
|
||||
%token T_FUNCTION
|
||||
%token T_CONST
|
||||
%token T_RETURN
|
||||
%token T_TRY
|
||||
%token T_CATCH
|
||||
%token T_FINALLY
|
||||
%token T_THROW
|
||||
%token T_USE
|
||||
%token T_INSTEADOF
|
||||
%token T_GLOBAL
|
||||
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
|
||||
%token T_VAR
|
||||
%token T_UNSET
|
||||
%token T_ISSET
|
||||
%token T_EMPTY
|
||||
%token T_HALT_COMPILER
|
||||
%token T_CLASS
|
||||
%token T_TRAIT
|
||||
%token T_INTERFACE
|
||||
%token T_EXTENDS
|
||||
%token T_IMPLEMENTS
|
||||
%token T_OBJECT_OPERATOR
|
||||
%token T_DOUBLE_ARROW
|
||||
%token T_LIST
|
||||
%token T_ARRAY
|
||||
%token T_CALLABLE
|
||||
%token T_CLASS_C
|
||||
%token T_TRAIT_C
|
||||
%token T_METHOD_C
|
||||
%token T_FUNC_C
|
||||
%token T_LINE
|
||||
%token T_FILE
|
||||
%token T_COMMENT
|
||||
%token T_DOC_COMMENT
|
||||
%token T_OPEN_TAG
|
||||
%token T_OPEN_TAG_WITH_ECHO
|
||||
%token T_CLOSE_TAG
|
||||
%token T_WHITESPACE
|
||||
%token T_START_HEREDOC
|
||||
%token T_END_HEREDOC
|
||||
%token T_DOLLAR_OPEN_CURLY_BRACES
|
||||
%token T_CURLY_OPEN
|
||||
%token T_PAAMAYIM_NEKUDOTAYIM
|
||||
%token T_NAMESPACE
|
||||
%token T_NS_C
|
||||
%token T_DIR
|
||||
%token T_NS_SEPARATOR
|
||||
%token T_ELLIPSIS
|
||||
|
||||
%{
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
%}
|
||||
%tokens
|
||||
|
||||
%%
|
||||
|
||||
@ -124,11 +9,37 @@ start:
|
||||
top_statement_list { $$ = $this->handleNamespaces($1); }
|
||||
;
|
||||
|
||||
top_statement_list:
|
||||
top_statement_list top_statement { pushNormalizing($1, $2); }
|
||||
top_statement_list_ex:
|
||||
top_statement_list_ex top_statement { pushNormalizing($1, $2); }
|
||||
| /* empty */ { init(); }
|
||||
;
|
||||
|
||||
top_statement_list:
|
||||
top_statement_list_ex
|
||||
{ makeNop($nop, $this->lookaheadStartAttributes);
|
||||
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
|
||||
;
|
||||
|
||||
reserved_non_modifiers:
|
||||
T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
|
||||
| T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
|
||||
| T_ENDWHILE | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH
|
||||
| T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
|
||||
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
|
||||
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
|
||||
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER
|
||||
;
|
||||
|
||||
semi_reserved:
|
||||
reserved_non_modifiers
|
||||
| T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC
|
||||
;
|
||||
|
||||
identifier:
|
||||
T_STRING { $$ = $1; }
|
||||
| semi_reserved { $$ = $1; }
|
||||
;
|
||||
|
||||
namespace_name_parts:
|
||||
T_STRING { init($1); }
|
||||
| namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); }
|
||||
@ -148,21 +59,57 @@ top_statement:
|
||||
| T_NAMESPACE namespace_name '{' top_statement_list '}' { $$ = Stmt\Namespace_[$2, $4]; }
|
||||
| T_NAMESPACE '{' top_statement_list '}' { $$ = Stmt\Namespace_[null, $3]; }
|
||||
| T_USE use_declarations ';' { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
||||
| T_USE T_FUNCTION use_declarations ';' { $$ = Stmt\Use_[$3, Stmt\Use_::TYPE_FUNCTION]; }
|
||||
| T_USE T_CONST use_declarations ';' { $$ = Stmt\Use_[$3, Stmt\Use_::TYPE_CONSTANT]; }
|
||||
| T_USE use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; }
|
||||
| group_use_declaration ';' { $$ = $1; }
|
||||
| T_CONST constant_declaration_list ';' { $$ = Stmt\Const_[$2]; }
|
||||
;
|
||||
|
||||
use_type:
|
||||
T_FUNCTION { $$ = Stmt\Use_::TYPE_FUNCTION; }
|
||||
| T_CONST { $$ = Stmt\Use_::TYPE_CONSTANT; }
|
||||
;
|
||||
|
||||
/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */
|
||||
group_use_declaration:
|
||||
T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
|
||||
{ $$ = Stmt\GroupUse[Name[$3], $6, $2]; }
|
||||
| T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
|
||||
{ $$ = Stmt\GroupUse[Name[$4], $7, $2]; }
|
||||
| T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
|
||||
{ $$ = Stmt\GroupUse[Name[$2], $5, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||
| T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
|
||||
{ $$ = Stmt\GroupUse[Name[$3], $6, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||
;
|
||||
|
||||
unprefixed_use_declarations:
|
||||
unprefixed_use_declarations ',' unprefixed_use_declaration
|
||||
{ push($1, $3); }
|
||||
| unprefixed_use_declaration { init($1); }
|
||||
;
|
||||
|
||||
use_declarations:
|
||||
use_declarations ',' use_declaration { push($1, $3); }
|
||||
| use_declaration { init($1); }
|
||||
;
|
||||
|
||||
inline_use_declarations:
|
||||
inline_use_declarations ',' inline_use_declaration { push($1, $3); }
|
||||
| inline_use_declaration { init($1); }
|
||||
;
|
||||
|
||||
unprefixed_use_declaration:
|
||||
namespace_name { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||
| namespace_name T_AS T_STRING { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||
;
|
||||
|
||||
use_declaration:
|
||||
namespace_name { $$ = Stmt\UseUse[$1, null]; }
|
||||
| namespace_name T_AS T_STRING { $$ = Stmt\UseUse[$1, $3]; }
|
||||
| T_NS_SEPARATOR namespace_name { $$ = Stmt\UseUse[$2, null]; }
|
||||
| T_NS_SEPARATOR namespace_name T_AS T_STRING { $$ = Stmt\UseUse[$2, $4]; }
|
||||
unprefixed_use_declaration { $$ = $1; }
|
||||
| T_NS_SEPARATOR unprefixed_use_declaration { $$ = $2; }
|
||||
;
|
||||
|
||||
inline_use_declaration:
|
||||
unprefixed_use_declaration { $$ = $1; $$->type = Stmt\Use_::TYPE_NORMAL; }
|
||||
| use_type unprefixed_use_declaration { $$ = $2; $$->type = $1; }
|
||||
;
|
||||
|
||||
constant_declaration_list:
|
||||
@ -174,11 +121,26 @@ constant_declaration:
|
||||
T_STRING '=' static_scalar { $$ = Node\Const_[$1, $3]; }
|
||||
;
|
||||
|
||||
inner_statement_list:
|
||||
inner_statement_list inner_statement { pushNormalizing($1, $2); }
|
||||
class_const_list:
|
||||
class_const_list ',' class_const { push($1, $3); }
|
||||
| class_const { init($1); }
|
||||
;
|
||||
|
||||
class_const:
|
||||
identifier '=' static_scalar { $$ = Node\Const_[$1, $3]; }
|
||||
;
|
||||
|
||||
inner_statement_list_ex:
|
||||
inner_statement_list_ex inner_statement { pushNormalizing($1, $2); }
|
||||
| /* empty */ { init(); }
|
||||
;
|
||||
|
||||
inner_statement_list:
|
||||
inner_statement_list_ex
|
||||
{ makeNop($nop, $this->lookaheadStartAttributes);
|
||||
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
|
||||
;
|
||||
|
||||
inner_statement:
|
||||
statement { $$ = $1; }
|
||||
| function_declaration_statement { $$ = $1; }
|
||||
@ -187,16 +149,16 @@ inner_statement:
|
||||
{ throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); }
|
||||
;
|
||||
|
||||
statement:
|
||||
'{' inner_statement_list '}' { $$ = $2; }
|
||||
non_empty_statement:
|
||||
'{' inner_statement_list '}' { $$ = $2; prependLeadingComments($$); }
|
||||
| T_IF parentheses_expr statement elseif_list else_single
|
||||
{ $$ = Stmt\If_[$2, [stmts: toArray($3), elseifs: $4, else: $5]]; }
|
||||
{ $$ = Stmt\If_[$2, ['stmts' => toArray($3), 'elseifs' => $4, 'else' => $5]]; }
|
||||
| T_IF parentheses_expr ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
|
||||
{ $$ = Stmt\If_[$2, [stmts: $4, elseifs: $5, else: $6]]; }
|
||||
{ $$ = Stmt\If_[$2, ['stmts' => $4, 'elseifs' => $5, 'else' => $6]]; }
|
||||
| T_WHILE parentheses_expr while_statement { $$ = Stmt\While_[$2, $3]; }
|
||||
| T_DO statement T_WHILE parentheses_expr ';' { $$ = Stmt\Do_ [$4, toArray($2)]; }
|
||||
| T_FOR '(' for_expr ';' for_expr ';' for_expr ')' for_statement
|
||||
{ $$ = Stmt\For_[[init: $3, cond: $5, loop: $7, stmts: $9]]; }
|
||||
{ $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; }
|
||||
| T_SWITCH parentheses_expr switch_case_list { $$ = Stmt\Switch_[$2, $3]; }
|
||||
| T_BREAK ';' { $$ = Stmt\Break_[null]; }
|
||||
| T_BREAK expr ';' { $$ = Stmt\Break_[$2]; }
|
||||
@ -212,19 +174,26 @@ statement:
|
||||
| expr ';' { $$ = $1; }
|
||||
| T_UNSET '(' variables_list ')' ';' { $$ = Stmt\Unset_[$3]; }
|
||||
| T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
|
||||
{ $$ = Stmt\Foreach_[$3, $5[0], [keyVar: null, byRef: $5[1], stmts: $7]]; }
|
||||
{ $$ = Stmt\Foreach_[$3, $5[0], ['keyVar' => null, 'byRef' => $5[1], 'stmts' => $7]]; }
|
||||
| T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement
|
||||
{ $$ = Stmt\Foreach_[$3, $7[0], [keyVar: $5, byRef: $7[1], stmts: $9]]; }
|
||||
{ $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; }
|
||||
| T_DECLARE '(' declare_list ')' declare_statement { $$ = Stmt\Declare_[$3, $5]; }
|
||||
| ';' { $$ = array(); /* means: no statement */ }
|
||||
| T_TRY '{' inner_statement_list '}' catches optional_finally
|
||||
{ $$ = Stmt\TryCatch[$3, $5, $6]; }
|
||||
| T_THROW expr ';' { $$ = Stmt\Throw_[$2]; }
|
||||
| T_GOTO T_STRING ';' { $$ = Stmt\Goto_[$2]; }
|
||||
| T_STRING ':' { $$ = Stmt\Label[$1]; }
|
||||
| expr error { $$ = $1; }
|
||||
| error { $$ = array(); /* means: no statement */ }
|
||||
;
|
||||
|
||||
statement:
|
||||
non_empty_statement { $$ = $1; }
|
||||
| ';'
|
||||
{ makeNop($$, $this->startAttributeStack[#1]);
|
||||
if ($$ === null) $$ = array(); /* means: no statement */ }
|
||||
;
|
||||
|
||||
catches:
|
||||
/* empty */ { init(); }
|
||||
| catches catch { push($1, $2); }
|
||||
@ -232,12 +201,12 @@ catches:
|
||||
|
||||
catch:
|
||||
T_CATCH '(' name T_VARIABLE ')' '{' inner_statement_list '}'
|
||||
{ $$ = Stmt\Catch_[$3, parseVar($4), $7]; }
|
||||
{ $$ = Stmt\Catch_[array($3), parseVar($4), $7]; }
|
||||
;
|
||||
|
||||
optional_finally:
|
||||
/* empty */ { $$ = null; }
|
||||
| T_FINALLY '{' inner_statement_list '}' { $$ = $3; }
|
||||
| T_FINALLY '{' inner_statement_list '}' { $$ = Stmt\Finally_[$3]; }
|
||||
;
|
||||
|
||||
variables_list:
|
||||
@ -257,16 +226,16 @@ optional_ellipsis:
|
||||
|
||||
function_declaration_statement:
|
||||
T_FUNCTION optional_ref T_STRING '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
||||
{ $$ = Stmt\Function_[$3, [byRef: $2, params: $5, returnType: $7, stmts: $9]]; }
|
||||
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; }
|
||||
;
|
||||
|
||||
class_declaration_statement:
|
||||
class_entry_type T_STRING extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Class_[$2, [type: $1, extends: $3, implements: $4, stmts: $6]]; }
|
||||
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; }
|
||||
| T_INTERFACE T_STRING interface_extends_list '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Interface_[$2, [extends: $3, stmts: $5]]; }
|
||||
{ $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; }
|
||||
| T_TRAIT T_STRING '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Trait_[$2, $4]; }
|
||||
{ $$ = Stmt\Trait_[$2, ['stmts' => $4]]; }
|
||||
;
|
||||
|
||||
class_entry_type:
|
||||
@ -306,7 +275,8 @@ foreach_statement:
|
||||
;
|
||||
|
||||
declare_statement:
|
||||
statement { $$ = toArray($1); }
|
||||
non_empty_statement { $$ = toArray($1); }
|
||||
| ';' { $$ = null; }
|
||||
| ':' inner_statement_list T_ENDDECLARE ';' { $$ = $2; }
|
||||
;
|
||||
|
||||
@ -458,9 +428,9 @@ class_statement_list:
|
||||
|
||||
class_statement:
|
||||
variable_modifiers property_declaration_list ';' { $$ = Stmt\Property[$1, $2]; }
|
||||
| T_CONST constant_declaration_list ';' { $$ = Stmt\ClassConst[$2]; }
|
||||
| method_modifiers T_FUNCTION optional_ref T_STRING '(' parameter_list ')' optional_return_type method_body
|
||||
{ $$ = Stmt\ClassMethod[$4, [type: $1, byRef: $3, params: $6, returnType: $8, stmts: $9]]; }
|
||||
| T_CONST class_const_list ';' { $$ = Stmt\ClassConst[$2, 0]; }
|
||||
| method_modifiers T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type method_body
|
||||
{ $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; }
|
||||
| T_USE name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; }
|
||||
;
|
||||
|
||||
@ -477,20 +447,22 @@ trait_adaptation_list:
|
||||
trait_adaptation:
|
||||
trait_method_reference_fully_qualified T_INSTEADOF name_list ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; }
|
||||
| trait_method_reference T_AS member_modifier T_STRING ';'
|
||||
| trait_method_reference T_AS member_modifier identifier ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; }
|
||||
| trait_method_reference T_AS member_modifier ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
|
||||
| trait_method_reference T_AS T_STRING ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||
| trait_method_reference T_AS reserved_non_modifiers ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||
;
|
||||
|
||||
trait_method_reference_fully_qualified:
|
||||
name T_PAAMAYIM_NEKUDOTAYIM T_STRING { $$ = array($1, $3); }
|
||||
name T_PAAMAYIM_NEKUDOTAYIM identifier { $$ = array($1, $3); }
|
||||
;
|
||||
trait_method_reference:
|
||||
trait_method_reference_fully_qualified { $$ = $1; }
|
||||
| T_STRING { $$ = array(null, $1); }
|
||||
| identifier { $$ = array(null, $1); }
|
||||
;
|
||||
|
||||
method_body:
|
||||
@ -617,7 +589,10 @@ expr:
|
||||
| T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; }
|
||||
| T_BOOL_CAST expr { $$ = Expr\Cast\Bool_ [$2]; }
|
||||
| T_UNSET_CAST expr { $$ = Expr\Cast\Unset_ [$2]; }
|
||||
| T_EXIT exit_expr { $$ = Expr\Exit_ [$2]; }
|
||||
| T_EXIT exit_expr
|
||||
{ $attrs = attributes();
|
||||
$attrs['kind'] = strtolower($1) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
|
||||
$$ = new Expr\Exit_($2, $attrs); }
|
||||
| '@' expr { $$ = Expr\ErrorSuppress[$2]; }
|
||||
| scalar { $$ = $1; }
|
||||
| array_expr { $$ = $1; }
|
||||
@ -628,10 +603,10 @@ expr:
|
||||
| T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; }
|
||||
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
|
||||
'{' inner_statement_list '}'
|
||||
{ $$ = Expr\Closure[[static: false, byRef: $2, params: $4, uses: $6, returnType: $7, stmts: $9]]; }
|
||||
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $9]]; }
|
||||
| T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
|
||||
'{' inner_statement_list '}'
|
||||
{ $$ = Expr\Closure[[static: true, byRef: $3, params: $5, uses: $7, returnType: $8, stmts: $10]]; }
|
||||
{ $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $10]]; }
|
||||
;
|
||||
|
||||
parentheses_expr:
|
||||
@ -645,14 +620,19 @@ yield_expr:
|
||||
;
|
||||
|
||||
array_expr:
|
||||
T_ARRAY '(' array_pair_list ')' { $$ = Expr\Array_[$3]; }
|
||||
| '[' array_pair_list ']' { $$ = Expr\Array_[$2]; }
|
||||
T_ARRAY '(' array_pair_list ')'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
|
||||
$$ = new Expr\Array_($3, $attrs); }
|
||||
| '[' array_pair_list ']'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_SHORT;
|
||||
$$ = new Expr\Array_($2, $attrs); }
|
||||
;
|
||||
|
||||
scalar_dereference:
|
||||
array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']'
|
||||
{ $$ = Expr\ArrayDimFetch[Scalar\String_[Scalar\String_::parse($1)], $3]; }
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = Expr\ArrayDimFetch[new Scalar\String_(Scalar\String_::parse($1), $attrs), $3]; }
|
||||
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
/* alternative array syntax missing intentionally */
|
||||
@ -660,7 +640,7 @@ scalar_dereference:
|
||||
|
||||
anonymous_class:
|
||||
T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = array(Stmt\Class_[null, [type: 0, extends: $3, implements: $4, stmts: $6]], $2); }
|
||||
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); }
|
||||
|
||||
new_expr:
|
||||
T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; }
|
||||
@ -684,7 +664,7 @@ lexical_var:
|
||||
|
||||
function_call:
|
||||
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM T_STRING argument_list
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier argument_list
|
||||
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list
|
||||
{ $$ = Expr\StaticCall[$1, $4, $6]; }
|
||||
@ -752,8 +732,9 @@ exit_expr:
|
||||
|
||||
backticks_expr:
|
||||
/* empty */ { $$ = array(); }
|
||||
| T_ENCAPSED_AND_WHITESPACE { $$ = array(Scalar\String_::parseEscapeSequences($1, '`')); }
|
||||
| encaps_list { parseEncapsed($1, '`'); $$ = $1; }
|
||||
| T_ENCAPSED_AND_WHITESPACE
|
||||
{ $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`', false)]); }
|
||||
| encaps_list { parseEncapsed($1, '`', false); $$ = $1; }
|
||||
;
|
||||
|
||||
ctor_arguments:
|
||||
@ -762,9 +743,11 @@ ctor_arguments:
|
||||
;
|
||||
|
||||
common_scalar:
|
||||
T_LNUMBER { $$ = Scalar\LNumber[Scalar\LNumber::parse($1)]; }
|
||||
T_LNUMBER { $$ = Scalar\LNumber::fromString($1, attributes(), true); }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_[Scalar\String_::parse($1)]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parse($1, false), $attrs); }
|
||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||
@ -774,14 +757,16 @@ common_scalar:
|
||||
| T_FUNC_C { $$ = Scalar\MagicConst\Function_[]; }
|
||||
| T_NS_C { $$ = Scalar\MagicConst\Namespace_[]; }
|
||||
| T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
|
||||
{ $$ = Scalar\String_[Scalar\String_::parseDocString($1, $2)]; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parseDocString($1, $2, false), $attrs); }
|
||||
| T_START_HEREDOC T_END_HEREDOC
|
||||
{ $$ = Scalar\String_['']; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
$$ = new Scalar\String_('', $attrs); }
|
||||
;
|
||||
|
||||
static_scalar:
|
||||
common_scalar { $$ = $1; }
|
||||
| class_name T_PAAMAYIM_NEKUDOTAYIM class_const_name { $$ = Expr\ClassConstFetch[$1, $3]; }
|
||||
| class_name T_PAAMAYIM_NEKUDOTAYIM identifier { $$ = Expr\ClassConstFetch[$1, $3]; }
|
||||
| name { $$ = Expr\ConstFetch[$1]; }
|
||||
| T_ARRAY '(' static_array_pair_list ')' { $$ = Expr\Array_[$3]; }
|
||||
| '[' static_array_pair_list ']' { $$ = Expr\Array_[$2]; }
|
||||
@ -826,7 +811,7 @@ static_operation:
|
||||
|
||||
constant:
|
||||
name { $$ = Expr\ConstFetch[$1]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM class_const_name
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier
|
||||
{ $$ = Expr\ClassConstFetch[$1, $3]; }
|
||||
;
|
||||
|
||||
@ -834,14 +819,11 @@ scalar:
|
||||
common_scalar { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| '"' encaps_list '"'
|
||||
{ parseEncapsed($2, '"'); $$ = Scalar\Encapsed[$2]; }
|
||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
| T_START_HEREDOC encaps_list T_END_HEREDOC
|
||||
{ parseEncapsedDoc($2); $$ = Scalar\Encapsed[$2]; }
|
||||
;
|
||||
|
||||
class_const_name:
|
||||
T_STRING { $$ = $1; }
|
||||
| T_CLASS { $$ = 'class'; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
parseEncapsedDoc($2, true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
;
|
||||
|
||||
static_array_pair_list:
|
||||
@ -933,6 +915,7 @@ object_property:
|
||||
T_STRING { $$ = $1; }
|
||||
| '{' expr '}' { $$ = $2; }
|
||||
| variable_without_objects { $$ = $1; }
|
||||
| error { $$ = Expr\Error[]; $this->errorState = 2; }
|
||||
;
|
||||
|
||||
list_expr:
|
||||
@ -945,7 +928,7 @@ list_expr_elements:
|
||||
;
|
||||
|
||||
list_expr_element:
|
||||
variable { $$ = $1; }
|
||||
variable { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||
| list_expr { $$ = $1; }
|
||||
| /* empty */ { $$ = null; }
|
||||
;
|
||||
@ -969,9 +952,13 @@ array_pair:
|
||||
|
||||
encaps_list:
|
||||
encaps_list encaps_var { push($1, $2); }
|
||||
| encaps_list T_ENCAPSED_AND_WHITESPACE { push($1, $2); }
|
||||
| encaps_list encaps_string_part { push($1, $2); }
|
||||
| encaps_var { init($1); }
|
||||
| T_ENCAPSED_AND_WHITESPACE encaps_var { init($1, $2); }
|
||||
| encaps_string_part encaps_var { init($1, $2); }
|
||||
;
|
||||
|
||||
encaps_string_part:
|
||||
T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart[$1]; }
|
||||
;
|
||||
|
||||
encaps_var:
|
861
grammar/php7.y
Normal file
861
grammar/php7.y
Normal file
@ -0,0 +1,861 @@
|
||||
%pure_parser
|
||||
%expect 2
|
||||
|
||||
%tokens
|
||||
|
||||
%%
|
||||
|
||||
start:
|
||||
top_statement_list { $$ = $this->handleNamespaces($1); }
|
||||
;
|
||||
|
||||
top_statement_list_ex:
|
||||
top_statement_list_ex top_statement { pushNormalizing($1, $2); }
|
||||
| /* empty */ { init(); }
|
||||
;
|
||||
|
||||
top_statement_list:
|
||||
top_statement_list_ex
|
||||
{ makeNop($nop, $this->lookaheadStartAttributes);
|
||||
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
|
||||
;
|
||||
|
||||
reserved_non_modifiers:
|
||||
T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
|
||||
| T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
|
||||
| T_ENDWHILE | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH
|
||||
| T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
|
||||
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
|
||||
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
|
||||
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER
|
||||
;
|
||||
|
||||
semi_reserved:
|
||||
reserved_non_modifiers
|
||||
| T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC
|
||||
;
|
||||
|
||||
identifier:
|
||||
T_STRING { $$ = $1; }
|
||||
| semi_reserved { $$ = $1; }
|
||||
;
|
||||
|
||||
namespace_name_parts:
|
||||
T_STRING { init($1); }
|
||||
| namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); }
|
||||
;
|
||||
|
||||
namespace_name:
|
||||
namespace_name_parts { $$ = Name[$1]; }
|
||||
;
|
||||
|
||||
top_statement:
|
||||
statement { $$ = $1; }
|
||||
| function_declaration_statement { $$ = $1; }
|
||||
| class_declaration_statement { $$ = $1; }
|
||||
| T_HALT_COMPILER
|
||||
{ $$ = Stmt\HaltCompiler[$this->lexer->handleHaltCompiler()]; }
|
||||
| T_NAMESPACE namespace_name ';' { $$ = Stmt\Namespace_[$2, null]; }
|
||||
| T_NAMESPACE namespace_name '{' top_statement_list '}' { $$ = Stmt\Namespace_[$2, $4]; }
|
||||
| T_NAMESPACE '{' top_statement_list '}' { $$ = Stmt\Namespace_[null, $3]; }
|
||||
| T_USE use_declarations ';' { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
||||
| T_USE use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; }
|
||||
| group_use_declaration ';' { $$ = $1; }
|
||||
| T_CONST constant_declaration_list ';' { $$ = Stmt\Const_[$2]; }
|
||||
;
|
||||
|
||||
use_type:
|
||||
T_FUNCTION { $$ = Stmt\Use_::TYPE_FUNCTION; }
|
||||
| T_CONST { $$ = Stmt\Use_::TYPE_CONSTANT; }
|
||||
;
|
||||
|
||||
/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */
|
||||
group_use_declaration:
|
||||
T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
|
||||
{ $$ = Stmt\GroupUse[Name[$3], $6, $2]; }
|
||||
| T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
|
||||
{ $$ = Stmt\GroupUse[Name[$4], $7, $2]; }
|
||||
| T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
|
||||
{ $$ = Stmt\GroupUse[Name[$2], $5, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||
| T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
|
||||
{ $$ = Stmt\GroupUse[Name[$3], $6, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||
;
|
||||
|
||||
unprefixed_use_declarations:
|
||||
unprefixed_use_declarations ',' unprefixed_use_declaration
|
||||
{ push($1, $3); }
|
||||
| unprefixed_use_declaration { init($1); }
|
||||
;
|
||||
|
||||
use_declarations:
|
||||
use_declarations ',' use_declaration { push($1, $3); }
|
||||
| use_declaration { init($1); }
|
||||
;
|
||||
|
||||
inline_use_declarations:
|
||||
inline_use_declarations ',' inline_use_declaration { push($1, $3); }
|
||||
| inline_use_declaration { init($1); }
|
||||
;
|
||||
|
||||
unprefixed_use_declaration:
|
||||
namespace_name { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||
| namespace_name T_AS T_STRING { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; }
|
||||
;
|
||||
|
||||
use_declaration:
|
||||
unprefixed_use_declaration { $$ = $1; }
|
||||
| T_NS_SEPARATOR unprefixed_use_declaration { $$ = $2; }
|
||||
;
|
||||
|
||||
inline_use_declaration:
|
||||
unprefixed_use_declaration { $$ = $1; $$->type = Stmt\Use_::TYPE_NORMAL; }
|
||||
| use_type unprefixed_use_declaration { $$ = $2; $$->type = $1; }
|
||||
;
|
||||
|
||||
constant_declaration_list:
|
||||
constant_declaration_list ',' constant_declaration { push($1, $3); }
|
||||
| constant_declaration { init($1); }
|
||||
;
|
||||
|
||||
constant_declaration:
|
||||
T_STRING '=' expr { $$ = Node\Const_[$1, $3]; }
|
||||
;
|
||||
|
||||
class_const_list:
|
||||
class_const_list ',' class_const { push($1, $3); }
|
||||
| class_const { init($1); }
|
||||
;
|
||||
|
||||
class_const:
|
||||
identifier '=' expr { $$ = Node\Const_[$1, $3]; }
|
||||
;
|
||||
|
||||
inner_statement_list_ex:
|
||||
inner_statement_list_ex inner_statement { pushNormalizing($1, $2); }
|
||||
| /* empty */ { init(); }
|
||||
;
|
||||
|
||||
inner_statement_list:
|
||||
inner_statement_list_ex
|
||||
{ makeNop($nop, $this->lookaheadStartAttributes);
|
||||
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
|
||||
;
|
||||
|
||||
inner_statement:
|
||||
statement { $$ = $1; }
|
||||
| function_declaration_statement { $$ = $1; }
|
||||
| class_declaration_statement { $$ = $1; }
|
||||
| T_HALT_COMPILER
|
||||
{ throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); }
|
||||
;
|
||||
|
||||
non_empty_statement:
|
||||
'{' inner_statement_list '}' { $$ = $2; prependLeadingComments($$); }
|
||||
| T_IF '(' expr ')' statement elseif_list else_single
|
||||
{ $$ = Stmt\If_[$3, ['stmts' => toArray($5), 'elseifs' => $6, 'else' => $7]]; }
|
||||
| T_IF '(' expr ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
|
||||
{ $$ = Stmt\If_[$3, ['stmts' => $6, 'elseifs' => $7, 'else' => $8]]; }
|
||||
| T_WHILE '(' expr ')' while_statement { $$ = Stmt\While_[$3, $5]; }
|
||||
| T_DO statement T_WHILE '(' expr ')' ';' { $$ = Stmt\Do_ [$5, toArray($2)]; }
|
||||
| T_FOR '(' for_expr ';' for_expr ';' for_expr ')' for_statement
|
||||
{ $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; }
|
||||
| T_SWITCH '(' expr ')' switch_case_list { $$ = Stmt\Switch_[$3, $5]; }
|
||||
| T_BREAK optional_expr ';' { $$ = Stmt\Break_[$2]; }
|
||||
| T_CONTINUE optional_expr ';' { $$ = Stmt\Continue_[$2]; }
|
||||
| T_RETURN optional_expr ';' { $$ = Stmt\Return_[$2]; }
|
||||
| T_GLOBAL global_var_list ';' { $$ = Stmt\Global_[$2]; }
|
||||
| T_STATIC static_var_list ';' { $$ = Stmt\Static_[$2]; }
|
||||
| T_ECHO expr_list ';' { $$ = Stmt\Echo_[$2]; }
|
||||
| T_INLINE_HTML { $$ = Stmt\InlineHTML[$1]; }
|
||||
| expr ';' { $$ = $1; }
|
||||
| T_UNSET '(' variables_list ')' ';' { $$ = Stmt\Unset_[$3]; }
|
||||
| T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
|
||||
{ $$ = Stmt\Foreach_[$3, $5[0], ['keyVar' => null, 'byRef' => $5[1], 'stmts' => $7]]; }
|
||||
| T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement
|
||||
{ $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; }
|
||||
| T_DECLARE '(' declare_list ')' declare_statement { $$ = Stmt\Declare_[$3, $5]; }
|
||||
| T_TRY '{' inner_statement_list '}' catches optional_finally
|
||||
{ $$ = Stmt\TryCatch[$3, $5, $6]; }
|
||||
| T_THROW expr ';' { $$ = Stmt\Throw_[$2]; }
|
||||
| T_GOTO T_STRING ';' { $$ = Stmt\Goto_[$2]; }
|
||||
| T_STRING ':' { $$ = Stmt\Label[$1]; }
|
||||
| expr error { $$ = $1; }
|
||||
| error { $$ = array(); /* means: no statement */ }
|
||||
;
|
||||
|
||||
statement:
|
||||
non_empty_statement { $$ = $1; }
|
||||
| ';'
|
||||
{ makeNop($$, $this->startAttributeStack[#1]);
|
||||
if ($$ === null) $$ = array(); /* means: no statement */ }
|
||||
;
|
||||
|
||||
catches:
|
||||
/* empty */ { init(); }
|
||||
| catches catch { push($1, $2); }
|
||||
;
|
||||
|
||||
name_union:
|
||||
name { init($1); }
|
||||
| name_union '|' name { push($1, $3); }
|
||||
;
|
||||
|
||||
catch:
|
||||
T_CATCH '(' name_union T_VARIABLE ')' '{' inner_statement_list '}'
|
||||
{ $$ = Stmt\Catch_[$3, parseVar($4), $7]; }
|
||||
;
|
||||
|
||||
optional_finally:
|
||||
/* empty */ { $$ = null; }
|
||||
| T_FINALLY '{' inner_statement_list '}' { $$ = Stmt\Finally_[$3]; }
|
||||
;
|
||||
|
||||
variables_list:
|
||||
variable { init($1); }
|
||||
| variables_list ',' variable { push($1, $3); }
|
||||
;
|
||||
|
||||
optional_ref:
|
||||
/* empty */ { $$ = false; }
|
||||
| '&' { $$ = true; }
|
||||
;
|
||||
|
||||
optional_ellipsis:
|
||||
/* empty */ { $$ = false; }
|
||||
| T_ELLIPSIS { $$ = true; }
|
||||
;
|
||||
|
||||
function_declaration_statement:
|
||||
T_FUNCTION optional_ref T_STRING '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
||||
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; }
|
||||
;
|
||||
|
||||
class_declaration_statement:
|
||||
class_entry_type T_STRING extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; }
|
||||
| T_INTERFACE T_STRING interface_extends_list '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; }
|
||||
| T_TRAIT T_STRING '{' class_statement_list '}'
|
||||
{ $$ = Stmt\Trait_[$2, ['stmts' => $4]]; }
|
||||
;
|
||||
|
||||
class_entry_type:
|
||||
T_CLASS { $$ = 0; }
|
||||
| T_ABSTRACT T_CLASS { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
||||
| T_FINAL T_CLASS { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
||||
;
|
||||
|
||||
extends_from:
|
||||
/* empty */ { $$ = null; }
|
||||
| T_EXTENDS name { $$ = $2; }
|
||||
;
|
||||
|
||||
interface_extends_list:
|
||||
/* empty */ { $$ = array(); }
|
||||
| T_EXTENDS name_list { $$ = $2; }
|
||||
;
|
||||
|
||||
implements_list:
|
||||
/* empty */ { $$ = array(); }
|
||||
| T_IMPLEMENTS name_list { $$ = $2; }
|
||||
;
|
||||
|
||||
name_list:
|
||||
name { init($1); }
|
||||
| name_list ',' name { push($1, $3); }
|
||||
;
|
||||
|
||||
for_statement:
|
||||
statement { $$ = toArray($1); }
|
||||
| ':' inner_statement_list T_ENDFOR ';' { $$ = $2; }
|
||||
;
|
||||
|
||||
foreach_statement:
|
||||
statement { $$ = toArray($1); }
|
||||
| ':' inner_statement_list T_ENDFOREACH ';' { $$ = $2; }
|
||||
;
|
||||
|
||||
declare_statement:
|
||||
non_empty_statement { $$ = toArray($1); }
|
||||
| ';' { $$ = null; }
|
||||
| ':' inner_statement_list T_ENDDECLARE ';' { $$ = $2; }
|
||||
;
|
||||
|
||||
declare_list:
|
||||
declare_list_element { init($1); }
|
||||
| declare_list ',' declare_list_element { push($1, $3); }
|
||||
;
|
||||
|
||||
declare_list_element:
|
||||
T_STRING '=' expr { $$ = Stmt\DeclareDeclare[$1, $3]; }
|
||||
;
|
||||
|
||||
switch_case_list:
|
||||
'{' case_list '}' { $$ = $2; }
|
||||
| '{' ';' case_list '}' { $$ = $3; }
|
||||
| ':' case_list T_ENDSWITCH ';' { $$ = $2; }
|
||||
| ':' ';' case_list T_ENDSWITCH ';' { $$ = $3; }
|
||||
;
|
||||
|
||||
case_list:
|
||||
/* empty */ { init(); }
|
||||
| case_list case { push($1, $2); }
|
||||
;
|
||||
|
||||
case:
|
||||
T_CASE expr case_separator inner_statement_list { $$ = Stmt\Case_[$2, $4]; }
|
||||
| T_DEFAULT case_separator inner_statement_list { $$ = Stmt\Case_[null, $3]; }
|
||||
;
|
||||
|
||||
case_separator:
|
||||
':'
|
||||
| ';'
|
||||
;
|
||||
|
||||
while_statement:
|
||||
statement { $$ = toArray($1); }
|
||||
| ':' inner_statement_list T_ENDWHILE ';' { $$ = $2; }
|
||||
;
|
||||
|
||||
elseif_list:
|
||||
/* empty */ { init(); }
|
||||
| elseif_list elseif { push($1, $2); }
|
||||
;
|
||||
|
||||
elseif:
|
||||
T_ELSEIF '(' expr ')' statement { $$ = Stmt\ElseIf_[$3, toArray($5)]; }
|
||||
;
|
||||
|
||||
new_elseif_list:
|
||||
/* empty */ { init(); }
|
||||
| new_elseif_list new_elseif { push($1, $2); }
|
||||
;
|
||||
|
||||
new_elseif:
|
||||
T_ELSEIF '(' expr ')' ':' inner_statement_list { $$ = Stmt\ElseIf_[$3, $6]; }
|
||||
;
|
||||
|
||||
else_single:
|
||||
/* empty */ { $$ = null; }
|
||||
| T_ELSE statement { $$ = Stmt\Else_[toArray($2)]; }
|
||||
;
|
||||
|
||||
new_else_single:
|
||||
/* empty */ { $$ = null; }
|
||||
| T_ELSE ':' inner_statement_list { $$ = Stmt\Else_[$3]; }
|
||||
;
|
||||
|
||||
foreach_variable:
|
||||
variable { $$ = array($1, false); }
|
||||
| '&' variable { $$ = array($2, true); }
|
||||
| list_expr { $$ = array($1, false); }
|
||||
| array_short_syntax { $$ = array($1, false); }
|
||||
;
|
||||
|
||||
parameter_list:
|
||||
non_empty_parameter_list { $$ = $1; }
|
||||
| /* empty */ { $$ = array(); }
|
||||
;
|
||||
|
||||
non_empty_parameter_list:
|
||||
parameter { init($1); }
|
||||
| non_empty_parameter_list ',' parameter { push($1, $3); }
|
||||
;
|
||||
|
||||
parameter:
|
||||
optional_param_type optional_ref optional_ellipsis T_VARIABLE
|
||||
{ $$ = Node\Param[parseVar($4), null, $1, $2, $3]; }
|
||||
| optional_param_type optional_ref optional_ellipsis T_VARIABLE '=' expr
|
||||
{ $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; }
|
||||
;
|
||||
|
||||
type_expr:
|
||||
type { $$ = $1; }
|
||||
| '?' type { $$ = Node\NullableType[$2]; }
|
||||
;
|
||||
|
||||
type:
|
||||
name { $$ = $this->handleBuiltinTypes($1); }
|
||||
| T_ARRAY { $$ = 'array'; }
|
||||
| T_CALLABLE { $$ = 'callable'; }
|
||||
;
|
||||
|
||||
optional_param_type:
|
||||
/* empty */ { $$ = null; }
|
||||
| type_expr { $$ = $1; }
|
||||
;
|
||||
|
||||
optional_return_type:
|
||||
/* empty */ { $$ = null; }
|
||||
| ':' type_expr { $$ = $2; }
|
||||
;
|
||||
|
||||
argument_list:
|
||||
'(' ')' { $$ = array(); }
|
||||
| '(' non_empty_argument_list ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
non_empty_argument_list:
|
||||
argument { init($1); }
|
||||
| non_empty_argument_list ',' argument { push($1, $3); }
|
||||
;
|
||||
|
||||
argument:
|
||||
expr { $$ = Node\Arg[$1, false, false]; }
|
||||
| '&' variable { $$ = Node\Arg[$2, true, false]; }
|
||||
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
||||
;
|
||||
|
||||
global_var_list:
|
||||
global_var_list ',' global_var { push($1, $3); }
|
||||
| global_var { init($1); }
|
||||
;
|
||||
|
||||
global_var:
|
||||
simple_variable { $$ = Expr\Variable[$1]; }
|
||||
;
|
||||
|
||||
static_var_list:
|
||||
static_var_list ',' static_var { push($1, $3); }
|
||||
| static_var { init($1); }
|
||||
;
|
||||
|
||||
static_var:
|
||||
T_VARIABLE { $$ = Stmt\StaticVar[parseVar($1), null]; }
|
||||
| T_VARIABLE '=' expr { $$ = Stmt\StaticVar[parseVar($1), $3]; }
|
||||
;
|
||||
|
||||
class_statement_list:
|
||||
class_statement_list class_statement { push($1, $2); }
|
||||
| /* empty */ { init(); }
|
||||
;
|
||||
|
||||
class_statement:
|
||||
variable_modifiers property_declaration_list ';' { $$ = Stmt\Property[$1, $2]; }
|
||||
| method_modifiers T_CONST class_const_list ';' { $$ = Stmt\ClassConst[$3, $1]; }
|
||||
| method_modifiers T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type method_body
|
||||
{ $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; }
|
||||
| T_USE name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; }
|
||||
;
|
||||
|
||||
trait_adaptations:
|
||||
';' { $$ = array(); }
|
||||
| '{' trait_adaptation_list '}' { $$ = $2; }
|
||||
;
|
||||
|
||||
trait_adaptation_list:
|
||||
/* empty */ { init(); }
|
||||
| trait_adaptation_list trait_adaptation { push($1, $2); }
|
||||
;
|
||||
|
||||
trait_adaptation:
|
||||
trait_method_reference_fully_qualified T_INSTEADOF name_list ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; }
|
||||
| trait_method_reference T_AS member_modifier identifier ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; }
|
||||
| trait_method_reference T_AS member_modifier ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
|
||||
| trait_method_reference T_AS T_STRING ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||
| trait_method_reference T_AS reserved_non_modifiers ';'
|
||||
{ $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
|
||||
;
|
||||
|
||||
trait_method_reference_fully_qualified:
|
||||
name T_PAAMAYIM_NEKUDOTAYIM identifier { $$ = array($1, $3); }
|
||||
;
|
||||
trait_method_reference:
|
||||
trait_method_reference_fully_qualified { $$ = $1; }
|
||||
| identifier { $$ = array(null, $1); }
|
||||
;
|
||||
|
||||
method_body:
|
||||
';' /* abstract method */ { $$ = null; }
|
||||
| '{' inner_statement_list '}' { $$ = $2; }
|
||||
;
|
||||
|
||||
variable_modifiers:
|
||||
non_empty_member_modifiers { $$ = $1; }
|
||||
| T_VAR { $$ = 0; }
|
||||
;
|
||||
|
||||
method_modifiers:
|
||||
/* empty */ { $$ = 0; }
|
||||
| non_empty_member_modifiers { $$ = $1; }
|
||||
;
|
||||
|
||||
non_empty_member_modifiers:
|
||||
member_modifier { $$ = $1; }
|
||||
| non_empty_member_modifiers member_modifier { Stmt\Class_::verifyModifier($1, $2); $$ = $1 | $2; }
|
||||
;
|
||||
|
||||
member_modifier:
|
||||
T_PUBLIC { $$ = Stmt\Class_::MODIFIER_PUBLIC; }
|
||||
| T_PROTECTED { $$ = Stmt\Class_::MODIFIER_PROTECTED; }
|
||||
| T_PRIVATE { $$ = Stmt\Class_::MODIFIER_PRIVATE; }
|
||||
| T_STATIC { $$ = Stmt\Class_::MODIFIER_STATIC; }
|
||||
| T_ABSTRACT { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
||||
| T_FINAL { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
||||
;
|
||||
|
||||
property_declaration_list:
|
||||
property_declaration { init($1); }
|
||||
| property_declaration_list ',' property_declaration { push($1, $3); }
|
||||
;
|
||||
|
||||
property_declaration:
|
||||
T_VARIABLE { $$ = Stmt\PropertyProperty[parseVar($1), null]; }
|
||||
| T_VARIABLE '=' expr { $$ = Stmt\PropertyProperty[parseVar($1), $3]; }
|
||||
;
|
||||
|
||||
expr_list:
|
||||
expr_list ',' expr { push($1, $3); }
|
||||
| expr { init($1); }
|
||||
;
|
||||
|
||||
for_expr:
|
||||
/* empty */ { $$ = array(); }
|
||||
| expr_list { $$ = $1; }
|
||||
;
|
||||
|
||||
expr:
|
||||
variable { $$ = $1; }
|
||||
| list_expr '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||
| array_short_syntax '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||
| variable '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||
| variable '=' '&' variable { $$ = Expr\AssignRef[$1, $4]; }
|
||||
| new_expr { $$ = $1; }
|
||||
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
||||
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
|
||||
| variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; }
|
||||
| variable T_MUL_EQUAL expr { $$ = Expr\AssignOp\Mul [$1, $3]; }
|
||||
| variable T_DIV_EQUAL expr { $$ = Expr\AssignOp\Div [$1, $3]; }
|
||||
| variable T_CONCAT_EQUAL expr { $$ = Expr\AssignOp\Concat [$1, $3]; }
|
||||
| variable T_MOD_EQUAL expr { $$ = Expr\AssignOp\Mod [$1, $3]; }
|
||||
| variable T_AND_EQUAL expr { $$ = Expr\AssignOp\BitwiseAnd[$1, $3]; }
|
||||
| variable T_OR_EQUAL expr { $$ = Expr\AssignOp\BitwiseOr [$1, $3]; }
|
||||
| variable T_XOR_EQUAL expr { $$ = Expr\AssignOp\BitwiseXor[$1, $3]; }
|
||||
| variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
|
||||
| variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
|
||||
| variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; }
|
||||
| variable T_INC { $$ = Expr\PostInc[$1]; }
|
||||
| T_INC variable { $$ = Expr\PreInc [$2]; }
|
||||
| variable T_DEC { $$ = Expr\PostDec[$1]; }
|
||||
| T_DEC variable { $$ = Expr\PreDec [$2]; }
|
||||
| expr T_BOOLEAN_OR expr { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; }
|
||||
| expr T_BOOLEAN_AND expr { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; }
|
||||
| expr T_LOGICAL_OR expr { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; }
|
||||
| expr T_LOGICAL_AND expr { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
|
||||
| expr T_LOGICAL_XOR expr { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
|
||||
| expr '|' expr { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
|
||||
| expr '&' expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
|
||||
| expr '^' expr { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
|
||||
| expr '.' expr { $$ = Expr\BinaryOp\Concat [$1, $3]; }
|
||||
| expr '+' expr { $$ = Expr\BinaryOp\Plus [$1, $3]; }
|
||||
| expr '-' expr { $$ = Expr\BinaryOp\Minus [$1, $3]; }
|
||||
| expr '*' expr { $$ = Expr\BinaryOp\Mul [$1, $3]; }
|
||||
| expr '/' expr { $$ = Expr\BinaryOp\Div [$1, $3]; }
|
||||
| expr '%' expr { $$ = Expr\BinaryOp\Mod [$1, $3]; }
|
||||
| expr T_SL expr { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; }
|
||||
| expr T_SR expr { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; }
|
||||
| expr T_POW expr { $$ = Expr\BinaryOp\Pow [$1, $3]; }
|
||||
| '+' expr %prec T_INC { $$ = Expr\UnaryPlus [$2]; }
|
||||
| '-' expr %prec T_INC { $$ = Expr\UnaryMinus[$2]; }
|
||||
| '!' expr { $$ = Expr\BooleanNot[$2]; }
|
||||
| '~' expr { $$ = Expr\BitwiseNot[$2]; }
|
||||
| expr T_IS_IDENTICAL expr { $$ = Expr\BinaryOp\Identical [$1, $3]; }
|
||||
| expr T_IS_NOT_IDENTICAL expr { $$ = Expr\BinaryOp\NotIdentical [$1, $3]; }
|
||||
| expr T_IS_EQUAL expr { $$ = Expr\BinaryOp\Equal [$1, $3]; }
|
||||
| expr T_IS_NOT_EQUAL expr { $$ = Expr\BinaryOp\NotEqual [$1, $3]; }
|
||||
| expr T_SPACESHIP expr { $$ = Expr\BinaryOp\Spaceship [$1, $3]; }
|
||||
| expr '<' expr { $$ = Expr\BinaryOp\Smaller [$1, $3]; }
|
||||
| expr T_IS_SMALLER_OR_EQUAL expr { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
|
||||
| expr '>' expr { $$ = Expr\BinaryOp\Greater [$1, $3]; }
|
||||
| expr T_IS_GREATER_OR_EQUAL expr { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
|
||||
| expr T_INSTANCEOF class_name_reference { $$ = Expr\Instanceof_[$1, $3]; }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; }
|
||||
| expr '?' ':' expr { $$ = Expr\Ternary[$1, null, $4]; }
|
||||
| expr T_COALESCE expr { $$ = Expr\BinaryOp\Coalesce[$1, $3]; }
|
||||
| T_ISSET '(' variables_list ')' { $$ = Expr\Isset_[$3]; }
|
||||
| T_EMPTY '(' expr ')' { $$ = Expr\Empty_[$3]; }
|
||||
| T_INCLUDE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; }
|
||||
| T_INCLUDE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE_ONCE]; }
|
||||
| T_EVAL '(' expr ')' { $$ = Expr\Eval_[$3]; }
|
||||
| T_REQUIRE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; }
|
||||
| T_REQUIRE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; }
|
||||
| T_INT_CAST expr { $$ = Expr\Cast\Int_ [$2]; }
|
||||
| T_DOUBLE_CAST expr { $$ = Expr\Cast\Double [$2]; }
|
||||
| T_STRING_CAST expr { $$ = Expr\Cast\String_ [$2]; }
|
||||
| T_ARRAY_CAST expr { $$ = Expr\Cast\Array_ [$2]; }
|
||||
| T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; }
|
||||
| T_BOOL_CAST expr { $$ = Expr\Cast\Bool_ [$2]; }
|
||||
| T_UNSET_CAST expr { $$ = Expr\Cast\Unset_ [$2]; }
|
||||
| T_EXIT exit_expr
|
||||
{ $attrs = attributes();
|
||||
$attrs['kind'] = strtolower($1) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
|
||||
$$ = new Expr\Exit_($2, $attrs); }
|
||||
| '@' expr { $$ = Expr\ErrorSuppress[$2]; }
|
||||
| scalar { $$ = $1; }
|
||||
| '`' backticks_expr '`' { $$ = Expr\ShellExec[$2]; }
|
||||
| T_PRINT expr { $$ = Expr\Print_[$2]; }
|
||||
| T_YIELD { $$ = Expr\Yield_[null, null]; }
|
||||
| T_YIELD expr { $$ = Expr\Yield_[$2, null]; }
|
||||
| T_YIELD expr T_DOUBLE_ARROW expr { $$ = Expr\Yield_[$4, $2]; }
|
||||
| T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; }
|
||||
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
|
||||
'{' inner_statement_list '}'
|
||||
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $9]]; }
|
||||
| T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
|
||||
'{' inner_statement_list '}'
|
||||
{ $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $10]]; }
|
||||
;
|
||||
|
||||
anonymous_class:
|
||||
T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); }
|
||||
|
||||
new_expr:
|
||||
T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; }
|
||||
| T_NEW anonymous_class
|
||||
{ list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; }
|
||||
;
|
||||
|
||||
lexical_vars:
|
||||
/* empty */ { $$ = array(); }
|
||||
| T_USE '(' lexical_var_list ')' { $$ = $3; }
|
||||
;
|
||||
|
||||
lexical_var_list:
|
||||
lexical_var { init($1); }
|
||||
| lexical_var_list ',' lexical_var { push($1, $3); }
|
||||
;
|
||||
|
||||
lexical_var:
|
||||
optional_ref T_VARIABLE { $$ = Expr\ClosureUse[parseVar($2), $1]; }
|
||||
;
|
||||
|
||||
function_call:
|
||||
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||
| callable_expr argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
|
||||
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
||||
;
|
||||
|
||||
class_name:
|
||||
T_STATIC { $$ = Name[$1]; }
|
||||
| name { $$ = $1; }
|
||||
;
|
||||
|
||||
name:
|
||||
namespace_name_parts { $$ = Name[$1]; }
|
||||
| T_NS_SEPARATOR namespace_name_parts { $$ = Name\FullyQualified[$2]; }
|
||||
| T_NAMESPACE T_NS_SEPARATOR namespace_name_parts { $$ = Name\Relative[$3]; }
|
||||
;
|
||||
|
||||
class_name_reference:
|
||||
class_name { $$ = $1; }
|
||||
| new_variable { $$ = $1; }
|
||||
;
|
||||
|
||||
class_name_or_var:
|
||||
class_name { $$ = $1; }
|
||||
| dereferencable { $$ = $1; }
|
||||
;
|
||||
|
||||
exit_expr:
|
||||
/* empty */ { $$ = null; }
|
||||
| '(' optional_expr ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
backticks_expr:
|
||||
/* empty */ { $$ = array(); }
|
||||
| T_ENCAPSED_AND_WHITESPACE
|
||||
{ $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`')]); }
|
||||
| encaps_list { parseEncapsed($1, '`', true); $$ = $1; }
|
||||
;
|
||||
|
||||
ctor_arguments:
|
||||
/* empty */ { $$ = array(); }
|
||||
| argument_list { $$ = $1; }
|
||||
;
|
||||
|
||||
constant:
|
||||
name { $$ = Expr\ConstFetch[$1]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier
|
||||
{ $$ = Expr\ClassConstFetch[$1, $3]; }
|
||||
;
|
||||
|
||||
array_short_syntax:
|
||||
'[' array_pair_list ']'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_SHORT;
|
||||
$$ = new Expr\Array_($2, $attrs); }
|
||||
;
|
||||
|
||||
dereferencable_scalar:
|
||||
T_ARRAY '(' array_pair_list ')'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
|
||||
$$ = new Expr\Array_($3, $attrs); }
|
||||
| array_short_syntax { $$ = $1; }
|
||||
| T_CONSTANT_ENCAPSED_STRING
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); }
|
||||
;
|
||||
|
||||
scalar:
|
||||
T_LNUMBER { $$ = Scalar\LNumber::fromString($1, attributes()); }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||
| T_CLASS_C { $$ = Scalar\MagicConst\Class_[]; }
|
||||
| T_TRAIT_C { $$ = Scalar\MagicConst\Trait_[]; }
|
||||
| T_METHOD_C { $$ = Scalar\MagicConst\Method[]; }
|
||||
| T_FUNC_C { $$ = Scalar\MagicConst\Function_[]; }
|
||||
| T_NS_C { $$ = Scalar\MagicConst\Namespace_[]; }
|
||||
| dereferencable_scalar { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parseDocString($1, $2), $attrs); }
|
||||
| T_START_HEREDOC T_END_HEREDOC
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
$$ = new Scalar\String_('', $attrs); }
|
||||
| '"' encaps_list '"'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
| T_START_HEREDOC encaps_list T_END_HEREDOC
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
parseEncapsedDoc($2, true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
;
|
||||
|
||||
optional_expr:
|
||||
/* empty */ { $$ = null; }
|
||||
| expr { $$ = $1; }
|
||||
;
|
||||
|
||||
dereferencable:
|
||||
variable { $$ = $1; }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| dereferencable_scalar { $$ = $1; }
|
||||
;
|
||||
|
||||
callable_expr:
|
||||
callable_variable { $$ = $1; }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| dereferencable_scalar { $$ = $1; }
|
||||
;
|
||||
|
||||
callable_variable:
|
||||
simple_variable { $$ = Expr\Variable[$1]; }
|
||||
| dereferencable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| constant '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| dereferencable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| function_call { $$ = $1; }
|
||||
| dereferencable T_OBJECT_OPERATOR property_name argument_list
|
||||
{ $$ = Expr\MethodCall[$1, $3, $4]; }
|
||||
;
|
||||
|
||||
variable:
|
||||
callable_variable { $$ = $1; }
|
||||
| static_member { $$ = $1; }
|
||||
| dereferencable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; }
|
||||
;
|
||||
|
||||
simple_variable:
|
||||
T_VARIABLE { $$ = parseVar($1); }
|
||||
| '$' '{' expr '}' { $$ = $3; }
|
||||
| '$' simple_variable { $$ = Expr\Variable[$2]; }
|
||||
;
|
||||
|
||||
static_member:
|
||||
class_name_or_var T_PAAMAYIM_NEKUDOTAYIM simple_variable
|
||||
{ $$ = Expr\StaticPropertyFetch[$1, $3]; }
|
||||
;
|
||||
|
||||
new_variable:
|
||||
simple_variable { $$ = Expr\Variable[$1]; }
|
||||
| new_variable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| new_variable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| new_variable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; }
|
||||
| class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable { $$ = Expr\StaticPropertyFetch[$1, $3]; }
|
||||
| new_variable T_PAAMAYIM_NEKUDOTAYIM simple_variable { $$ = Expr\StaticPropertyFetch[$1, $3]; }
|
||||
;
|
||||
|
||||
member_name:
|
||||
identifier { $$ = $1; }
|
||||
| '{' expr '}' { $$ = $2; }
|
||||
| simple_variable { $$ = Expr\Variable[$1]; }
|
||||
;
|
||||
|
||||
property_name:
|
||||
T_STRING { $$ = $1; }
|
||||
| '{' expr '}' { $$ = $2; }
|
||||
| simple_variable { $$ = Expr\Variable[$1]; }
|
||||
| error { $$ = Expr\Error[]; $this->errorState = 2; }
|
||||
;
|
||||
|
||||
list_expr:
|
||||
T_LIST '(' list_expr_elements ')' { $$ = Expr\List_[$3]; }
|
||||
;
|
||||
|
||||
list_expr_elements:
|
||||
list_expr_elements ',' list_expr_element { push($1, $3); }
|
||||
| list_expr_element { init($1); }
|
||||
;
|
||||
|
||||
list_expr_element:
|
||||
variable { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||
| list_expr { $$ = $1; }
|
||||
| expr T_DOUBLE_ARROW variable { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||
| expr T_DOUBLE_ARROW list_expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||
| /* empty */ { $$ = null; }
|
||||
;
|
||||
|
||||
array_pair_list:
|
||||
inner_array_pair_list
|
||||
{ $$ = $1; $end = count($$)-1; if ($$[$end] === null) unset($$[$end]); }
|
||||
;
|
||||
|
||||
inner_array_pair_list:
|
||||
inner_array_pair_list ',' array_pair { push($1, $3); }
|
||||
| array_pair { init($1); }
|
||||
;
|
||||
|
||||
array_pair:
|
||||
expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||
| expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
|
||||
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
|
||||
| /* empty */ { $$ = null; }
|
||||
;
|
||||
|
||||
encaps_list:
|
||||
encaps_list encaps_var { push($1, $2); }
|
||||
| encaps_list encaps_string_part { push($1, $2); }
|
||||
| encaps_var { init($1); }
|
||||
| encaps_string_part encaps_var { init($1, $2); }
|
||||
;
|
||||
|
||||
encaps_string_part:
|
||||
T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart[$1]; }
|
||||
;
|
||||
|
||||
encaps_var:
|
||||
T_VARIABLE { $$ = Expr\Variable[parseVar($1)]; }
|
||||
| T_VARIABLE '[' encaps_var_offset ']' { $$ = Expr\ArrayDimFetch[Expr\Variable[parseVar($1)], $3]; }
|
||||
| T_VARIABLE T_OBJECT_OPERATOR T_STRING { $$ = Expr\PropertyFetch[Expr\Variable[parseVar($1)], $3]; }
|
||||
| T_DOLLAR_OPEN_CURLY_BRACES expr '}' { $$ = Expr\Variable[$2]; }
|
||||
| T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}' { $$ = Expr\Variable[$2]; }
|
||||
| T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}'
|
||||
{ $$ = Expr\ArrayDimFetch[Expr\Variable[$2], $4]; }
|
||||
| T_CURLY_OPEN variable '}' { $$ = $2; }
|
||||
;
|
||||
|
||||
encaps_var_offset:
|
||||
T_STRING { $$ = Scalar\String_[$1]; }
|
||||
| T_NUM_STRING { $$ = Scalar\String_[$1]; }
|
||||
| T_VARIABLE { $$ = Expr\Variable[parseVar($1)]; }
|
||||
;
|
||||
|
||||
%%
|
@ -1,10 +1,17 @@
|
||||
<?php
|
||||
|
||||
$grammarFile = __DIR__ . '/zend_language_parser.phpy';
|
||||
$skeletonFile = __DIR__ . '/kmyacc.php.parser';
|
||||
$tmpGrammarFile = __DIR__ . '/tmp_parser.phpy';
|
||||
$tmpResultFile = __DIR__ . '/tmp_parser.php';
|
||||
$parserResultFile = __DIR__ . '/../lib/PhpParser/Parser.php';
|
||||
$grammarFileToName = [
|
||||
__DIR__ . '/php5.y' => 'Php5',
|
||||
__DIR__ . '/php7.y' => 'Php7',
|
||||
];
|
||||
|
||||
$tokensFile = __DIR__ . '/tokens.y';
|
||||
$tokensTemplate = __DIR__ . '/tokens.template';
|
||||
$skeletonFile = __DIR__ . '/parser.template';
|
||||
$tmpGrammarFile = __DIR__ . '/tmp_parser.phpy';
|
||||
$tmpResultFile = __DIR__ . '/tmp_parser.php';
|
||||
$resultDir = __DIR__ . '/../lib/PhpParser/Parser';
|
||||
$tokensResultsFile = $resultDir . '/Tokens.php';
|
||||
|
||||
// check for kmyacc.exe binary in this directory, otherwise fall back to global name
|
||||
$kmyacc = __DIR__ . '/kmyacc.exe';
|
||||
@ -35,32 +42,41 @@ const ARGS = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
|
||||
/// Main script ///
|
||||
///////////////////
|
||||
|
||||
echo 'Building temporary preproprocessed grammar file.', "\n";
|
||||
$tokens = file_get_contents($tokensFile);
|
||||
|
||||
$grammarCode = file_get_contents($grammarFile);
|
||||
foreach ($grammarFileToName as $grammarFile => $name) {
|
||||
echo "Building temporary $name grammar file.\n";
|
||||
|
||||
$grammarCode = resolveNodes($grammarCode);
|
||||
$grammarCode = resolveMacros($grammarCode);
|
||||
$grammarCode = resolveArrays($grammarCode);
|
||||
$grammarCode = resolveStackAccess($grammarCode);
|
||||
$grammarCode = file_get_contents($grammarFile);
|
||||
$grammarCode = str_replace('%tokens', $tokens, $grammarCode);
|
||||
|
||||
file_put_contents($tmpGrammarFile, $grammarCode);
|
||||
$grammarCode = resolveNodes($grammarCode);
|
||||
$grammarCode = resolveMacros($grammarCode);
|
||||
$grammarCode = resolveStackAccess($grammarCode);
|
||||
|
||||
$additionalArgs = $optionDebug ? '-t -v' : '';
|
||||
file_put_contents($tmpGrammarFile, $grammarCode);
|
||||
|
||||
echo "Building parser.\n";
|
||||
$output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile $tmpGrammarFile 2>&1"));
|
||||
echo "Output: \"$output\"\n";
|
||||
$additionalArgs = $optionDebug ? '-t -v' : '';
|
||||
|
||||
$resultCode = file_get_contents($tmpResultFile);
|
||||
$resultCode = removeTrailingWhitespace($resultCode);
|
||||
echo "Building $name parser.\n";
|
||||
$output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile -p $name $tmpGrammarFile 2>&1"));
|
||||
echo "Output: \"$output\"\n";
|
||||
|
||||
ensureDirExists(dirname($parserResultFile));
|
||||
file_put_contents($parserResultFile, $resultCode);
|
||||
unlink($tmpResultFile);
|
||||
$resultCode = file_get_contents($tmpResultFile);
|
||||
$resultCode = removeTrailingWhitespace($resultCode);
|
||||
|
||||
if (!$optionKeepTmpGrammar) {
|
||||
unlink($tmpGrammarFile);
|
||||
ensureDirExists($resultDir);
|
||||
file_put_contents("$resultDir/$name.php", $resultCode);
|
||||
unlink($tmpResultFile);
|
||||
|
||||
echo "Building token definition.\n";
|
||||
$output = trim(shell_exec("$kmyacc -l -m $tokensTemplate $tmpGrammarFile 2>&1"));
|
||||
assert($output === '');
|
||||
rename($tmpResultFile, $tokensResultsFile);
|
||||
|
||||
if (!$optionKeepTmpGrammar) {
|
||||
unlink($tmpGrammarFile);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
@ -69,7 +85,7 @@ if (!$optionKeepTmpGrammar) {
|
||||
|
||||
function resolveNodes($code) {
|
||||
return preg_replace_callback(
|
||||
'~(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
|
||||
'~\b(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
|
||||
function($matches) {
|
||||
// recurse
|
||||
$matches['params'] = resolveNodes($matches['params']);
|
||||
@ -121,7 +137,8 @@ function resolveMacros($code) {
|
||||
if ('pushNormalizing' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); } else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
|
||||
return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
|
||||
. ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
|
||||
}
|
||||
|
||||
if ('toArray' == $name) {
|
||||
@ -137,15 +154,54 @@ function resolveMacros($code) {
|
||||
}
|
||||
|
||||
if ('parseEncapsed' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
assertArgs(3, $args, $name);
|
||||
|
||||
return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = Node\Scalar\String_::parseEscapeSequences($s, ' . $args[1] . '); } }';
|
||||
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
|
||||
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
|
||||
}
|
||||
|
||||
if ('parseEncapsedDoc' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
|
||||
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, ' . $args[1] . '); } }'
|
||||
. ' $s->value = preg_replace(\'~(\r\n|\n|\r)\z~\', \'\', $s->value);'
|
||||
. ' if (\'\' === $s->value) array_pop(' . $args[0] . ');';
|
||||
}
|
||||
|
||||
if ('makeNop' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return '$startAttributes = ' . $args[1] . ';'
|
||||
. ' if (isset($startAttributes[\'comments\']))'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop([\'comments\' => $startAttributes[\'comments\']]); }'
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('strKind' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = Node\Scalar\String_::parseEscapeSequences($s, null); } } $s = preg_replace(\'~(\r\n|\n|\r)\z~\', \'\', $s); if (\'\' === $s) array_pop(' . $args[0] . ');';
|
||||
return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && '
|
||||
. '(' . $args[0] . '[0] === \'b\' || ' . $args[0] . '[0] === \'B\')) '
|
||||
. '? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED)';
|
||||
}
|
||||
|
||||
if ('setDocStringAttrs' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return $args[0] . '[\'kind\'] = strpos(' . $args[1] . ', "\'") === false '
|
||||
. '? Scalar\String_::KIND_HEREDOC : Scalar\String_::KIND_NOWDOC; '
|
||||
. 'preg_match(\'/\A[bB]?<<<[ \t]*[\\\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\\\'"]?(?:\r\n|\n|\r)\z/\', ' . $args[1] . ', $matches); '
|
||||
. $args[0] . '[\'docLabel\'] = $matches[1];';
|
||||
}
|
||||
|
||||
if ('prependLeadingComments' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; '
|
||||
. 'if (!empty($attrs[\'comments\']) && isset($stmts[0])) {'
|
||||
. '$stmts[0]->setAttribute(\'comments\', '
|
||||
. 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }';
|
||||
}
|
||||
|
||||
return $matches[0];
|
||||
@ -160,37 +216,6 @@ function assertArgs($num, $args, $name) {
|
||||
}
|
||||
}
|
||||
|
||||
function resolveArrays($code) {
|
||||
return preg_replace_callback(
|
||||
'~' . PARAMS . '~',
|
||||
function ($matches) {
|
||||
$elements = magicSplit(
|
||||
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
|
||||
$matches['params']
|
||||
);
|
||||
|
||||
// don't convert [] to array, it might have different meaning
|
||||
if (empty($elements)) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$elementCodes = array();
|
||||
foreach ($elements as $element) {
|
||||
// convert only arrays where all elements have keys
|
||||
if (false === strpos($element, ':')) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
list($key, $value) = explode(':', $element, 2);
|
||||
$elementCodes[] = "'" . $key . "' =>" . $value;
|
||||
}
|
||||
|
||||
return 'array(' . implode(', ', $elementCodes) . ')';
|
||||
},
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
function resolveStackAccess($code) {
|
||||
$code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
|
||||
$code = preg_replace('/#(\d+)/', '$$1', $code);
|
||||
@ -224,5 +249,9 @@ function magicSplit($regex, $string) {
|
||||
$piece = trim($piece);
|
||||
}
|
||||
|
||||
return array_filter($pieces);
|
||||
if ($pieces === ['']) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $pieces;
|
||||
}
|
17
grammar/tokens.template
Normal file
17
grammar/tokens.template
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
$meta #
|
||||
#semval($) $this->semValue
|
||||
#semval($,%t) $this->semValue
|
||||
#semval(%n) $this->stackPos-(%l-%n)
|
||||
#semval(%n,%t) $this->stackPos-(%l-%n)
|
||||
|
||||
namespace PhpParser\Parser;
|
||||
#include;
|
||||
|
||||
/* GENERATED file based on grammar/tokens.y */
|
||||
final class Tokens
|
||||
{
|
||||
#tokenval
|
||||
const %s = %n;
|
||||
#endtokenval
|
||||
}
|
113
grammar/tokens.y
Normal file
113
grammar/tokens.y
Normal file
@ -0,0 +1,113 @@
|
||||
/* We currently rely on the token ID mapping to be the same between PHP 5 and PHP 7 - so the same lexer can be used for
|
||||
* both. This is enforced by sharing this token file. */
|
||||
|
||||
%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
|
||||
%left ','
|
||||
%left T_LOGICAL_OR
|
||||
%left T_LOGICAL_XOR
|
||||
%left T_LOGICAL_AND
|
||||
%right T_PRINT
|
||||
%right T_YIELD
|
||||
%right T_DOUBLE_ARROW
|
||||
%right T_YIELD_FROM
|
||||
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL
|
||||
%left '?' ':'
|
||||
%right T_COALESCE
|
||||
%left T_BOOLEAN_OR
|
||||
%left T_BOOLEAN_AND
|
||||
%left '|'
|
||||
%left '^'
|
||||
%left '&'
|
||||
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
|
||||
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
|
||||
%left T_SL T_SR
|
||||
%left '+' '-' '.'
|
||||
%left '*' '/' '%'
|
||||
%right '!'
|
||||
%nonassoc T_INSTANCEOF
|
||||
%right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
|
||||
%right T_POW
|
||||
%right '['
|
||||
%nonassoc T_NEW T_CLONE
|
||||
%token T_EXIT
|
||||
%token T_IF
|
||||
%left T_ELSEIF
|
||||
%left T_ELSE
|
||||
%left T_ENDIF
|
||||
%token T_LNUMBER
|
||||
%token T_DNUMBER
|
||||
%token T_STRING
|
||||
%token T_STRING_VARNAME
|
||||
%token T_VARIABLE
|
||||
%token T_NUM_STRING
|
||||
%token T_INLINE_HTML
|
||||
%token T_CHARACTER
|
||||
%token T_BAD_CHARACTER
|
||||
%token T_ENCAPSED_AND_WHITESPACE
|
||||
%token T_CONSTANT_ENCAPSED_STRING
|
||||
%token T_ECHO
|
||||
%token T_DO
|
||||
%token T_WHILE
|
||||
%token T_ENDWHILE
|
||||
%token T_FOR
|
||||
%token T_ENDFOR
|
||||
%token T_FOREACH
|
||||
%token T_ENDFOREACH
|
||||
%token T_DECLARE
|
||||
%token T_ENDDECLARE
|
||||
%token T_AS
|
||||
%token T_SWITCH
|
||||
%token T_ENDSWITCH
|
||||
%token T_CASE
|
||||
%token T_DEFAULT
|
||||
%token T_BREAK
|
||||
%token T_CONTINUE
|
||||
%token T_GOTO
|
||||
%token T_FUNCTION
|
||||
%token T_CONST
|
||||
%token T_RETURN
|
||||
%token T_TRY
|
||||
%token T_CATCH
|
||||
%token T_FINALLY
|
||||
%token T_THROW
|
||||
%token T_USE
|
||||
%token T_INSTEADOF
|
||||
%token T_GLOBAL
|
||||
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
|
||||
%token T_VAR
|
||||
%token T_UNSET
|
||||
%token T_ISSET
|
||||
%token T_EMPTY
|
||||
%token T_HALT_COMPILER
|
||||
%token T_CLASS
|
||||
%token T_TRAIT
|
||||
%token T_INTERFACE
|
||||
%token T_EXTENDS
|
||||
%token T_IMPLEMENTS
|
||||
%token T_OBJECT_OPERATOR
|
||||
%token T_DOUBLE_ARROW
|
||||
%token T_LIST
|
||||
%token T_ARRAY
|
||||
%token T_CALLABLE
|
||||
%token T_CLASS_C
|
||||
%token T_TRAIT_C
|
||||
%token T_METHOD_C
|
||||
%token T_FUNC_C
|
||||
%token T_LINE
|
||||
%token T_FILE
|
||||
%token T_COMMENT
|
||||
%token T_DOC_COMMENT
|
||||
%token T_OPEN_TAG
|
||||
%token T_OPEN_TAG_WITH_ECHO
|
||||
%token T_CLOSE_TAG
|
||||
%token T_WHITESPACE
|
||||
%token T_START_HEREDOC
|
||||
%token T_END_HEREDOC
|
||||
%token T_DOLLAR_OPEN_CURLY_BRACES
|
||||
%token T_CURLY_OPEN
|
||||
%token T_PAAMAYIM_NEKUDOTAYIM
|
||||
%token T_NAMESPACE
|
||||
%token T_NS_C
|
||||
%token T_DIR
|
||||
%token T_NS_SEPARATOR
|
||||
%token T_ELLIPSIS
|
@ -10,9 +10,6 @@ class Autoloader
|
||||
/** @var bool Whether the autoloader has been registered. */
|
||||
private static $registered = false;
|
||||
|
||||
/** @var bool Whether we're running on PHP 7. */
|
||||
private static $runningOnPhp7;
|
||||
|
||||
/**
|
||||
* Registers PhpParser\Autoloader as an SPL autoloader.
|
||||
*
|
||||
@ -25,7 +22,6 @@ class Autoloader
|
||||
|
||||
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
|
||||
self::$registered = true;
|
||||
self::$runningOnPhp7 = version_compare(PHP_VERSION, '7.0-dev', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,241 +31,10 @@ class Autoloader
|
||||
*/
|
||||
static public function autoload($class) {
|
||||
if (0 === strpos($class, 'PhpParser\\')) {
|
||||
if (isset(self::$php7AliasesOldToNew[$class])) {
|
||||
if (self::$runningOnPhp7) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the new class, alias will be registered afterwards
|
||||
$class = self::$php7AliasesOldToNew[$class];
|
||||
}
|
||||
|
||||
$fileName = dirname(__DIR__) . '/' . strtr($class, '\\', '/') . '.php';
|
||||
$fileName = __DIR__ . strtr(substr($class, 9), '\\', '/') . '.php';
|
||||
if (file_exists($fileName)) {
|
||||
require $fileName;
|
||||
}
|
||||
|
||||
if (isset(self::$php7AliasesNewToOld[$class])) {
|
||||
// New class name was used, register alias for old one, otherwise
|
||||
// it won't be usable in "instanceof" and other non-autoloading places.
|
||||
if (!self::$runningOnPhp7) {
|
||||
class_alias($class, self::$php7AliasesNewToOld[$class]);
|
||||
}
|
||||
}
|
||||
} else if (0 === strpos($class, 'PHPParser_')) {
|
||||
if (isset(self::$nonNamespacedAliases[$class])) {
|
||||
// Register all aliases at once to avoid dependency issues
|
||||
self::registerNonNamespacedAliases();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function registerNonNamespacedAliases() {
|
||||
foreach (self::$nonNamespacedAliases as $old => $new) {
|
||||
class_alias($new, $old);
|
||||
}
|
||||
}
|
||||
|
||||
private static $php7AliasesOldToNew = array(
|
||||
'PhpParser\Node\Expr\Cast\Bool' => 'PhpParser\Node\Expr\Cast\Bool_',
|
||||
'PhpParser\Node\Expr\Cast\Int' => 'PhpParser\Node\Expr\Cast\Int_',
|
||||
'PhpParser\Node\Expr\Cast\Object' => 'PhpParser\Node\Expr\Cast\Object_',
|
||||
'PhpParser\Node\Expr\Cast\String' => 'PhpParser\Node\Expr\Cast\String_',
|
||||
'PhpParser\Node\Scalar\String' => 'PhpParser\Node\Scalar\String_',
|
||||
);
|
||||
|
||||
private static $php7AliasesNewToOld = array(
|
||||
'PhpParser\Node\Expr\Cast\Bool_' => 'PhpParser\Node\Expr\Cast\Bool',
|
||||
'PhpParser\Node\Expr\Cast\Int_' => 'PhpParser\Node\Expr\Cast\Int',
|
||||
'PhpParser\Node\Expr\Cast\Object_' => 'PhpParser\Node\Expr\Cast\Object',
|
||||
'PhpParser\Node\Expr\Cast\String_' => 'PhpParser\Node\Expr\Cast\String',
|
||||
'PhpParser\Node\Scalar\String_' => 'PhpParser\Node\Scalar\String',
|
||||
);
|
||||
|
||||
private static $nonNamespacedAliases = array(
|
||||
'PHPParser_Builder' => 'PhpParser\Builder',
|
||||
'PHPParser_BuilderAbstract' => 'PhpParser\BuilderAbstract',
|
||||
'PHPParser_BuilderFactory' => 'PhpParser\BuilderFactory',
|
||||
'PHPParser_Comment' => 'PhpParser\Comment',
|
||||
'PHPParser_Comment_Doc' => 'PhpParser\Comment\Doc',
|
||||
'PHPParser_Error' => 'PhpParser\Error',
|
||||
'PHPParser_Lexer' => 'PhpParser\Lexer',
|
||||
'PHPParser_Lexer_Emulative' => 'PhpParser\Lexer\Emulative',
|
||||
'PHPParser_Node' => 'PhpParser\Node',
|
||||
'PHPParser_NodeAbstract' => 'PhpParser\NodeAbstract',
|
||||
'PHPParser_NodeDumper' => 'PhpParser\NodeDumper',
|
||||
'PHPParser_NodeTraverser' => 'PhpParser\NodeTraverser',
|
||||
'PHPParser_NodeTraverserInterface' => 'PhpParser\NodeTraverserInterface',
|
||||
'PHPParser_NodeVisitor' => 'PhpParser\NodeVisitor',
|
||||
'PHPParser_NodeVisitor_NameResolver' => 'PhpParser\NodeVisitor\NameResolver',
|
||||
'PHPParser_NodeVisitorAbstract' => 'PhpParser\NodeVisitorAbstract',
|
||||
'PHPParser_Parser' => 'PhpParser\Parser',
|
||||
'PHPParser_PrettyPrinterAbstract' => 'PhpParser\PrettyPrinterAbstract',
|
||||
'PHPParser_PrettyPrinter_Default' => 'PhpParser\PrettyPrinter\Standard',
|
||||
'PHPParser_PrettyPrinter_Zend' => 'PhpParser\PrettyPrinter\Standard',
|
||||
'PHPParser_Serializer' => 'PhpParser\Serializer',
|
||||
'PHPParser_Serializer_XML' => 'PhpParser\Serializer\XML',
|
||||
'PHPParser_Unserializer' => 'PhpParser\Unserializer',
|
||||
'PHPParser_Unserializer_XML' => 'PhpParser\Unserializer\XML',
|
||||
|
||||
'PHPParser_Builder_Class' => 'PhpParser\Builder\Class_',
|
||||
'PHPParser_Builder_Function' => 'PhpParser\Builder\Function_',
|
||||
'PHPParser_Builder_Interface' => 'PhpParser\Builder\Interface_',
|
||||
'PHPParser_Builder_Method' => 'PhpParser\Builder\Method',
|
||||
'PHPParser_Builder_Param' => 'PhpParser\Builder\Param',
|
||||
'PHPParser_Builder_Property' => 'PhpParser\Builder\Property',
|
||||
|
||||
'PHPParser_Node_Arg' => 'PhpParser\Node\Arg',
|
||||
'PHPParser_Node_Const' => 'PhpParser\Node\Const_',
|
||||
'PHPParser_Node_Expr' => 'PhpParser\Node\Expr',
|
||||
'PHPParser_Node_Name' => 'PhpParser\Node\Name',
|
||||
'PHPParser_Node_Name_FullyQualified' => 'PhpParser\Node\Name\FullyQualified',
|
||||
'PHPParser_Node_Name_Relative' => 'PhpParser\Node\Name\Relative',
|
||||
'PHPParser_Node_Param' => 'PhpParser\Node\Param',
|
||||
'PHPParser_Node_Scalar' => 'PhpParser\Node\Scalar',
|
||||
'PHPParser_Node_Stmt' => 'PhpParser\Node\Stmt',
|
||||
|
||||
'PHPParser_Node_Stmt_Break' => 'PhpParser\Node\Stmt\Break_',
|
||||
'PHPParser_Node_Stmt_Case' => 'PhpParser\Node\Stmt\Case_',
|
||||
'PHPParser_Node_Stmt_Catch' => 'PhpParser\Node\Stmt\Catch_',
|
||||
'PHPParser_Node_Stmt_Class' => 'PhpParser\Node\Stmt\Class_',
|
||||
'PHPParser_Node_Stmt_ClassConst' => 'PhpParser\Node\Stmt\ClassConst',
|
||||
'PHPParser_Node_Stmt_ClassMethod' => 'PhpParser\Node\Stmt\ClassMethod',
|
||||
'PHPParser_Node_Stmt_Const' => 'PhpParser\Node\Stmt\Const_',
|
||||
'PHPParser_Node_Stmt_Continue' => 'PhpParser\Node\Stmt\Continue_',
|
||||
'PHPParser_Node_Stmt_Declare' => 'PhpParser\Node\Stmt\Declare_',
|
||||
'PHPParser_Node_Stmt_DeclareDeclare' => 'PhpParser\Node\Stmt\DeclareDeclare',
|
||||
'PHPParser_Node_Stmt_Do' => 'PhpParser\Node\Stmt\Do_',
|
||||
'PHPParser_Node_Stmt_Echo' => 'PhpParser\Node\Stmt\Echo_',
|
||||
'PHPParser_Node_Stmt_Else' => 'PhpParser\Node\Stmt\Else_',
|
||||
'PHPParser_Node_Stmt_ElseIf' => 'PhpParser\Node\Stmt\ElseIf_',
|
||||
'PHPParser_Node_Stmt_For' => 'PhpParser\Node\Stmt\For_',
|
||||
'PHPParser_Node_Stmt_Foreach' => 'PhpParser\Node\Stmt\Foreach_',
|
||||
'PHPParser_Node_Stmt_Function' => 'PhpParser\Node\Stmt\Function_',
|
||||
'PHPParser_Node_Stmt_Global' => 'PhpParser\Node\Stmt\Global_',
|
||||
'PHPParser_Node_Stmt_Goto' => 'PhpParser\Node\Stmt\Goto_',
|
||||
'PHPParser_Node_Stmt_HaltCompiler' => 'PhpParser\Node\Stmt\HaltCompiler',
|
||||
'PHPParser_Node_Stmt_If' => 'PhpParser\Node\Stmt\If_',
|
||||
'PHPParser_Node_Stmt_InlineHTML' => 'PhpParser\Node\Stmt\InlineHTML',
|
||||
'PHPParser_Node_Stmt_Interface' => 'PhpParser\Node\Stmt\Interface_',
|
||||
'PHPParser_Node_Stmt_Label' => 'PhpParser\Node\Stmt\Label',
|
||||
'PHPParser_Node_Stmt_Namespace' => 'PhpParser\Node\Stmt\Namespace_',
|
||||
'PHPParser_Node_Stmt_Property' => 'PhpParser\Node\Stmt\Property',
|
||||
'PHPParser_Node_Stmt_PropertyProperty' => 'PhpParser\Node\Stmt\PropertyProperty',
|
||||
'PHPParser_Node_Stmt_Return' => 'PhpParser\Node\Stmt\Return_',
|
||||
'PHPParser_Node_Stmt_Static' => 'PhpParser\Node\Stmt\Static_',
|
||||
'PHPParser_Node_Stmt_StaticVar' => 'PhpParser\Node\Stmt\StaticVar',
|
||||
'PHPParser_Node_Stmt_Switch' => 'PhpParser\Node\Stmt\Switch_',
|
||||
'PHPParser_Node_Stmt_Throw' => 'PhpParser\Node\Stmt\Throw_',
|
||||
'PHPParser_Node_Stmt_Trait' => 'PhpParser\Node\Stmt\Trait_',
|
||||
'PHPParser_Node_Stmt_TraitUse' => 'PhpParser\Node\Stmt\TraitUse',
|
||||
'PHPParser_Node_Stmt_TraitUseAdaptation' => 'PhpParser\Node\Stmt\TraitUseAdaptation',
|
||||
'PHPParser_Node_Stmt_TraitUseAdaptation_Alias' => 'PhpParser\Node\Stmt\TraitUseAdaptation\Alias',
|
||||
'PHPParser_Node_Stmt_TraitUseAdaptation_Precedence' => 'PhpParser\Node\Stmt\TraitUseAdaptation\Precedence',
|
||||
'PHPParser_Node_Stmt_TryCatch' => 'PhpParser\Node\Stmt\TryCatch',
|
||||
'PHPParser_Node_Stmt_Unset' => 'PhpParser\Node\Stmt\Unset_',
|
||||
'PHPParser_Node_Stmt_UseUse' => 'PhpParser\Node\Stmt\UseUse',
|
||||
'PHPParser_Node_Stmt_Use' => 'PhpParser\Node\Stmt\Use_',
|
||||
'PHPParser_Node_Stmt_While' => 'PhpParser\Node\Stmt\While_',
|
||||
|
||||
'PHPParser_Node_Expr_AssignBitwiseAnd' => 'PhpParser\Node\Expr\AssignOp\BitwiseAnd',
|
||||
'PHPParser_Node_Expr_AssignBitwiseOr' => 'PhpParser\Node\Expr\AssignOp\BitwiseOr',
|
||||
'PHPParser_Node_Expr_AssignBitwiseXor' => 'PhpParser\Node\Expr\AssignOp\BitwiseXor',
|
||||
'PHPParser_Node_Expr_AssignConcat' => 'PhpParser\Node\Expr\AssignOp\Concat',
|
||||
'PHPParser_Node_Expr_AssignDiv' => 'PhpParser\Node\Expr\AssignOp\Div',
|
||||
'PHPParser_Node_Expr_AssignMinus' => 'PhpParser\Node\Expr\AssignOp\Minus',
|
||||
'PHPParser_Node_Expr_AssignMod' => 'PhpParser\Node\Expr\AssignOp\Mod',
|
||||
'PHPParser_Node_Expr_AssignMul' => 'PhpParser\Node\Expr\AssignOp\Mul',
|
||||
'PHPParser_Node_Expr_AssignPlus' => 'PhpParser\Node\Expr\AssignOp\Plus',
|
||||
'PHPParser_Node_Expr_AssignShiftLeft' => 'PhpParser\Node\Expr\AssignOp\ShiftLeft',
|
||||
'PHPParser_Node_Expr_AssignShiftRight' => 'PhpParser\Node\Expr\AssignOp\ShiftRight',
|
||||
|
||||
'PHPParser_Node_Expr_Cast' => 'PhpParser\Node\Expr\Cast',
|
||||
'PHPParser_Node_Expr_Cast_Array' => 'PhpParser\Node\Expr\Cast\Array_',
|
||||
'PHPParser_Node_Expr_Cast_Bool' => 'PhpParser\Node\Expr\Cast\Bool_',
|
||||
'PHPParser_Node_Expr_Cast_Double' => 'PhpParser\Node\Expr\Cast\Double',
|
||||
'PHPParser_Node_Expr_Cast_Int' => 'PhpParser\Node\Expr\Cast\Int_',
|
||||
'PHPParser_Node_Expr_Cast_Object' => 'PhpParser\Node\Expr\Cast\Object_',
|
||||
'PHPParser_Node_Expr_Cast_String' => 'PhpParser\Node\Expr\Cast\String_',
|
||||
'PHPParser_Node_Expr_Cast_Unset' => 'PhpParser\Node\Expr\Cast\Unset_',
|
||||
|
||||
'PHPParser_Node_Expr_BitwiseAnd' => 'PhpParser\Node\Expr\BinaryOp\BitwiseAnd',
|
||||
'PHPParser_Node_Expr_BitwiseOr' => 'PhpParser\Node\Expr\BinaryOp\BitwiseOr',
|
||||
'PHPParser_Node_Expr_BitwiseXor' => 'PhpParser\Node\Expr\BinaryOp\BitwiseXor',
|
||||
'PHPParser_Node_Expr_BooleanAnd' => 'PhpParser\Node\Expr\BinaryOp\BooleanAnd',
|
||||
'PHPParser_Node_Expr_BooleanOr' => 'PhpParser\Node\Expr\BinaryOp\BooleanOr',
|
||||
'PHPParser_Node_Expr_Concat' => 'PhpParser\Node\Expr\BinaryOp\Concat',
|
||||
'PHPParser_Node_Expr_Div' => 'PhpParser\Node\Expr\BinaryOp\Div',
|
||||
'PHPParser_Node_Expr_Equal' => 'PhpParser\Node\Expr\BinaryOp\Equal',
|
||||
'PHPParser_Node_Expr_Greater' => 'PhpParser\Node\Expr\BinaryOp\Greater',
|
||||
'PHPParser_Node_Expr_GreaterOrEqual' => 'PhpParser\Node\Expr\BinaryOp\GreaterOrEqual',
|
||||
'PHPParser_Node_Expr_Identical' => 'PhpParser\Node\Expr\BinaryOp\Identical',
|
||||
'PHPParser_Node_Expr_LogicalAnd' => 'PhpParser\Node\Expr\BinaryOp\LogicalAnd',
|
||||
'PHPParser_Node_Expr_LogicalOr' => 'PhpParser\Node\Expr\BinaryOp\LogicalOr',
|
||||
'PHPParser_Node_Expr_LogicalXor' => 'PhpParser\Node\Expr\BinaryOp\LogicalXor',
|
||||
'PHPParser_Node_Expr_Minus' => 'PhpParser\Node\Expr\BinaryOp\Minus',
|
||||
'PHPParser_Node_Expr_Mod' => 'PhpParser\Node\Expr\BinaryOp\Mod',
|
||||
'PHPParser_Node_Expr_Mul' => 'PhpParser\Node\Expr\BinaryOp\Mul',
|
||||
'PHPParser_Node_Expr_NotEqual' => 'PhpParser\Node\Expr\BinaryOp\NotEqual',
|
||||
'PHPParser_Node_Expr_NotIdentical' => 'PhpParser\Node\Expr\BinaryOp\NotIdentical',
|
||||
'PHPParser_Node_Expr_Plus' => 'PhpParser\Node\Expr\BinaryOp\Plus',
|
||||
'PHPParser_Node_Expr_ShiftLeft' => 'PhpParser\Node\Expr\BinaryOp\ShiftLeft',
|
||||
'PHPParser_Node_Expr_ShiftRight' => 'PhpParser\Node\Expr\BinaryOp\ShiftRight',
|
||||
'PHPParser_Node_Expr_Smaller' => 'PhpParser\Node\Expr\BinaryOp\Smaller',
|
||||
'PHPParser_Node_Expr_SmallerOrEqual' => 'PhpParser\Node\Expr\BinaryOp\SmallerOrEqual',
|
||||
|
||||
'PHPParser_Node_Expr_Array' => 'PhpParser\Node\Expr\Array_',
|
||||
'PHPParser_Node_Expr_ArrayDimFetch' => 'PhpParser\Node\Expr\ArrayDimFetch',
|
||||
'PHPParser_Node_Expr_ArrayItem' => 'PhpParser\Node\Expr\ArrayItem',
|
||||
'PHPParser_Node_Expr_Assign' => 'PhpParser\Node\Expr\Assign',
|
||||
'PHPParser_Node_Expr_AssignRef' => 'PhpParser\Node\Expr\AssignRef',
|
||||
'PHPParser_Node_Expr_BitwiseNot' => 'PhpParser\Node\Expr\BitwiseNot',
|
||||
'PHPParser_Node_Expr_BooleanNot' => 'PhpParser\Node\Expr\BooleanNot',
|
||||
'PHPParser_Node_Expr_ClassConstFetch' => 'PhpParser\Node\Expr\ClassConstFetch',
|
||||
'PHPParser_Node_Expr_Clone' => 'PhpParser\Node\Expr\Clone_',
|
||||
'PHPParser_Node_Expr_Closure' => 'PhpParser\Node\Expr\Closure',
|
||||
'PHPParser_Node_Expr_ClosureUse' => 'PhpParser\Node\Expr\ClosureUse',
|
||||
'PHPParser_Node_Expr_ConstFetch' => 'PhpParser\Node\Expr\ConstFetch',
|
||||
'PHPParser_Node_Expr_Empty' => 'PhpParser\Node\Expr\Empty_',
|
||||
'PHPParser_Node_Expr_ErrorSuppress' => 'PhpParser\Node\Expr\ErrorSuppress',
|
||||
'PHPParser_Node_Expr_Eval' => 'PhpParser\Node\Expr\Eval_',
|
||||
'PHPParser_Node_Expr_Exit' => 'PhpParser\Node\Expr\Exit_',
|
||||
'PHPParser_Node_Expr_FuncCall' => 'PhpParser\Node\Expr\FuncCall',
|
||||
'PHPParser_Node_Expr_Include' => 'PhpParser\Node\Expr\Include_',
|
||||
'PHPParser_Node_Expr_Instanceof' => 'PhpParser\Node\Expr\Instanceof_',
|
||||
'PHPParser_Node_Expr_Isset' => 'PhpParser\Node\Expr\Isset_',
|
||||
'PHPParser_Node_Expr_List' => 'PhpParser\Node\Expr\List_',
|
||||
'PHPParser_Node_Expr_MethodCall' => 'PhpParser\Node\Expr\MethodCall',
|
||||
'PHPParser_Node_Expr_New' => 'PhpParser\Node\Expr\New_',
|
||||
'PHPParser_Node_Expr_PostDec' => 'PhpParser\Node\Expr\PostDec',
|
||||
'PHPParser_Node_Expr_PostInc' => 'PhpParser\Node\Expr\PostInc',
|
||||
'PHPParser_Node_Expr_PreDec' => 'PhpParser\Node\Expr\PreDec',
|
||||
'PHPParser_Node_Expr_PreInc' => 'PhpParser\Node\Expr\PreInc',
|
||||
'PHPParser_Node_Expr_Print' => 'PhpParser\Node\Expr\Print_',
|
||||
'PHPParser_Node_Expr_PropertyFetch' => 'PhpParser\Node\Expr\PropertyFetch',
|
||||
'PHPParser_Node_Expr_ShellExec' => 'PhpParser\Node\Expr\ShellExec',
|
||||
'PHPParser_Node_Expr_StaticCall' => 'PhpParser\Node\Expr\StaticCall',
|
||||
'PHPParser_Node_Expr_StaticPropertyFetch' => 'PhpParser\Node\Expr\StaticPropertyFetch',
|
||||
'PHPParser_Node_Expr_Ternary' => 'PhpParser\Node\Expr\Ternary',
|
||||
'PHPParser_Node_Expr_UnaryMinus' => 'PhpParser\Node\Expr\UnaryMinus',
|
||||
'PHPParser_Node_Expr_UnaryPlus' => 'PhpParser\Node\Expr\UnaryPlus',
|
||||
'PHPParser_Node_Expr_Variable' => 'PhpParser\Node\Expr\Variable',
|
||||
'PHPParser_Node_Expr_Yield' => 'PhpParser\Node\Expr\Yield_',
|
||||
|
||||
'PHPParser_Node_Scalar_ClassConst' => 'PhpParser\Node\Scalar\MagicConst\Class_',
|
||||
'PHPParser_Node_Scalar_DirConst' => 'PhpParser\Node\Scalar\MagicConst\Dir',
|
||||
'PHPParser_Node_Scalar_FileConst' => 'PhpParser\Node\Scalar\MagicConst\File',
|
||||
'PHPParser_Node_Scalar_FuncConst' => 'PhpParser\Node\Scalar\MagicConst\Function_',
|
||||
'PHPParser_Node_Scalar_LineConst' => 'PhpParser\Node\Scalar\MagicConst\Line',
|
||||
'PHPParser_Node_Scalar_MethodConst' => 'PhpParser\Node\Scalar\MagicConst\Method',
|
||||
'PHPParser_Node_Scalar_NSConst' => 'PhpParser\Node\Scalar\MagicConst\Namespace_',
|
||||
'PHPParser_Node_Scalar_TraitConst' => 'PhpParser\Node\Scalar\MagicConst\Trait_',
|
||||
|
||||
'PHPParser_Node_Scalar_DNumber' => 'PhpParser\Node\Scalar\DNumber',
|
||||
'PHPParser_Node_Scalar_Encapsed' => 'PhpParser\Node\Scalar\Encapsed',
|
||||
'PHPParser_Node_Scalar_LNumber' => 'PhpParser\Node\Scalar\LNumber',
|
||||
'PHPParser_Node_Scalar_String' => 'PhpParser\Node\Scalar\String_',
|
||||
);
|
||||
}
|
||||
|
||||
class_alias('PhpParser\Autoloader', 'PHPParser_Autoloader');
|
||||
|
@ -12,7 +12,7 @@ class Class_ extends Declaration
|
||||
|
||||
protected $extends = null;
|
||||
protected $implements = array();
|
||||
protected $type = 0;
|
||||
protected $flags = 0;
|
||||
|
||||
protected $uses = array();
|
||||
protected $constants = array();
|
||||
@ -44,8 +44,7 @@ class Class_ extends Declaration
|
||||
/**
|
||||
* Implements one or more interfaces.
|
||||
*
|
||||
* @param Name|string $interface Name of interface to implement
|
||||
* @param Name|string $... More interfaces to implement
|
||||
* @param Name|string ...$interfaces Names of interfaces to implement
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
@ -113,7 +112,7 @@ class Class_ extends Declaration
|
||||
*/
|
||||
public function getNode() {
|
||||
return new Stmt\Class_($this->name, array(
|
||||
'type' => $this->type,
|
||||
'flags' => $this->flags,
|
||||
'extends' => $this->extends,
|
||||
'implements' => $this->implements,
|
||||
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
|
||||
|
@ -10,6 +10,7 @@ abstract class FunctionLike extends Declaration
|
||||
{
|
||||
protected $returnByRef = false;
|
||||
protected $params = array();
|
||||
protected $returnType = null;
|
||||
|
||||
/**
|
||||
* Make the function return by reference.
|
||||
@ -55,4 +56,19 @@ abstract class FunctionLike extends Declaration
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the return type for PHP 7.
|
||||
*
|
||||
* @param string|Node\Name $type One of array, callable, string, int, float, bool, iterable,
|
||||
* or a class/interface name.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setReturnType($type)
|
||||
{
|
||||
$this->returnType = $this->normalizeType($type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -40,9 +40,10 @@ class Function_ extends FunctionLike
|
||||
*/
|
||||
public function getNode() {
|
||||
return new Stmt\Function_($this->name, array(
|
||||
'byRef' => $this->returnByRef,
|
||||
'params' => $this->params,
|
||||
'stmts' => $this->stmts,
|
||||
'byRef' => $this->returnByRef,
|
||||
'params' => $this->params,
|
||||
'returnType' => $this->returnType,
|
||||
'stmts' => $this->stmts,
|
||||
), $this->attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,7 @@ class Interface_ extends Declaration
|
||||
/**
|
||||
* Extends one or more interfaces.
|
||||
*
|
||||
* @param Name|string $interface Name of interface to extend
|
||||
* @param Name|string $... More interfaces to extend
|
||||
* @param Name|string ...$interfaces Names of interfaces to extend
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@ use PhpParser\Node\Stmt;
|
||||
class Method extends FunctionLike
|
||||
{
|
||||
protected $name;
|
||||
protected $type = 0;
|
||||
protected $flags = 0;
|
||||
protected $stmts = array();
|
||||
|
||||
/**
|
||||
@ -116,10 +116,11 @@ class Method extends FunctionLike
|
||||
*/
|
||||
public function getNode() {
|
||||
return new Stmt\ClassMethod($this->name, array(
|
||||
'type' => $this->type,
|
||||
'byRef' => $this->returnByRef,
|
||||
'params' => $this->params,
|
||||
'stmts' => $this->stmts,
|
||||
'flags' => $this->flags,
|
||||
'byRef' => $this->returnByRef,
|
||||
'params' => $this->params,
|
||||
'returnType' => $this->returnType,
|
||||
'stmts' => $this->stmts,
|
||||
), $this->attributes);
|
||||
}
|
||||
}
|
||||
|
@ -43,10 +43,9 @@ class Param extends PhpParser\BuilderAbstract
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setTypeHint($type) {
|
||||
if ($type === 'array' || $type === 'callable') {
|
||||
$this->type = $type;
|
||||
} else {
|
||||
$this->type = $this->normalizeName($type);
|
||||
$this->type = $this->normalizeType($type);
|
||||
if ($this->type === 'void') {
|
||||
throw new \LogicException('Parameter type cannot be void');
|
||||
}
|
||||
|
||||
return $this;
|
||||
@ -73,4 +72,4 @@ class Param extends PhpParser\BuilderAbstract
|
||||
$this->name, $this->default, $this->type, $this->byRef
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ class Property extends PhpParser\BuilderAbstract
|
||||
{
|
||||
protected $name;
|
||||
|
||||
protected $type = 0;
|
||||
protected $flags = 0;
|
||||
protected $default = null;
|
||||
protected $attributes = array();
|
||||
|
||||
@ -101,7 +101,7 @@ class Property extends PhpParser\BuilderAbstract
|
||||
*/
|
||||
public function getNode() {
|
||||
return new Stmt\Property(
|
||||
$this->type !== 0 ? $this->type : Stmt\Class_::MODIFIER_PUBLIC,
|
||||
$this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC,
|
||||
array(
|
||||
new Stmt\PropertyProperty($this->name, $this->default)
|
||||
),
|
||||
|
@ -49,7 +49,9 @@ class Trait_ extends Declaration
|
||||
*/
|
||||
public function getNode() {
|
||||
return new Stmt\Trait_(
|
||||
$this->name, array_merge($this->properties, $this->methods), $this->attributes
|
||||
$this->name, array(
|
||||
'stmts' => array_merge($this->properties, $this->methods)
|
||||
), $this->attributes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace PhpParser;
|
||||
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Comment;
|
||||
@ -53,6 +54,49 @@ abstract class BuilderAbstract implements Builder {
|
||||
throw new \LogicException('Name must be a string or an instance of PhpParser\Node\Name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a type: Converts plain-text type names into proper AST representation.
|
||||
*
|
||||
* In particular, builtin types are left as strings, custom types become Names and nullables
|
||||
* are wrapped in NullableType nodes.
|
||||
*
|
||||
* @param Name|string $type The type to normalize
|
||||
*
|
||||
* @return Name|string The normalized type
|
||||
*/
|
||||
protected function normalizeType($type) {
|
||||
if (!is_string($type)) {
|
||||
if (!$type instanceof Name && !$type instanceof NullableType) {
|
||||
throw new \LogicException(
|
||||
'Type must be a string, or an instance of Name or NullableType');
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
$nullable = false;
|
||||
if (strlen($type) > 0 && $type[0] === '?') {
|
||||
$nullable = true;
|
||||
$type = substr($type, 1);
|
||||
}
|
||||
|
||||
$builtinTypes = array(
|
||||
'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void'
|
||||
);
|
||||
|
||||
$lowerType = strtolower($type);
|
||||
if (in_array($lowerType, $builtinTypes)) {
|
||||
$type = $lowerType;
|
||||
} else {
|
||||
$type = $this->normalizeName($type);
|
||||
}
|
||||
|
||||
if ($nullable && $type === 'void') {
|
||||
throw new \LogicException('void type cannot be nullable');
|
||||
}
|
||||
|
||||
return $nullable ? new Node\NullableType($type) : $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a value: Converts nulls, booleans, integers,
|
||||
* floats, strings and arrays into their respective nodes
|
||||
@ -125,7 +169,7 @@ abstract class BuilderAbstract implements Builder {
|
||||
* @param int $modifier Modifier to set
|
||||
*/
|
||||
protected function setModifier($modifier) {
|
||||
Stmt\Class_::verifyModifier($this->type, $modifier);
|
||||
$this->type |= $modifier;
|
||||
Stmt\Class_::verifyModifier($this->flags, $modifier);
|
||||
$this->flags |= $modifier;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class BuilderFactory
|
||||
{
|
||||
/**
|
||||
* Creates a namespace builder.
|
||||
*
|
||||
*
|
||||
* @param null|string|Node\Name $name Name of the namespace
|
||||
*
|
||||
* @return Builder\Namespace_ The created namespace builder
|
||||
|
@ -2,20 +2,23 @@
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
class Comment
|
||||
class Comment implements \JsonSerializable
|
||||
{
|
||||
protected $text;
|
||||
protected $line;
|
||||
protected $filePos;
|
||||
|
||||
/**
|
||||
* Constructs a comment node.
|
||||
*
|
||||
* @param string $text Comment text (including comment delimiters like /*)
|
||||
* @param int $line Line number the comment started on
|
||||
* @param string $text Comment text (including comment delimiters like /*)
|
||||
* @param int $startLine Line number the comment started on
|
||||
* @param int $startFilePos File offset the comment started on
|
||||
*/
|
||||
public function __construct($text, $line = -1) {
|
||||
public function __construct($text, $startLine = -1, $startFilePos = -1) {
|
||||
$this->text = $text;
|
||||
$this->line = $line;
|
||||
$this->line = $startLine;
|
||||
$this->filePos = $startFilePos;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -27,15 +30,6 @@ class Comment
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment text.
|
||||
*
|
||||
* @param string $text The comment text (including comment delimiters like /*)
|
||||
*/
|
||||
public function setText($text) {
|
||||
$this->text = $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number the comment started on.
|
||||
*
|
||||
@ -46,12 +40,12 @@ class Comment
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line number the comment started on.
|
||||
* Gets the file offset the comment started on.
|
||||
*
|
||||
* @param int $line Line number
|
||||
* @return int File offset
|
||||
*/
|
||||
public function setLine($line) {
|
||||
$this->line = $line;
|
||||
public function getFilePos() {
|
||||
return $this->filePos;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,7 +69,8 @@ class Comment
|
||||
*/
|
||||
public function getReformattedText() {
|
||||
$text = trim($this->text);
|
||||
if (false === strpos($text, "\n")) {
|
||||
$newlinePos = strpos($text, "\n");
|
||||
if (false === $newlinePos) {
|
||||
// Single line comments don't need further processing
|
||||
return $text;
|
||||
} elseif (preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\R\s+\*.*)+$)', $text)) {
|
||||
@ -105,15 +100,41 @@ class Comment
|
||||
//
|
||||
// /* Some text.
|
||||
// Some more text.
|
||||
// Indented text.
|
||||
// Even more text. */
|
||||
//
|
||||
// is handled by taking the length of the "/* " segment and leaving only that
|
||||
// many space characters before the lines. Thus in the above example only three
|
||||
// space characters are left at the start of every line.
|
||||
return preg_replace('(^\s*(?= {' . strlen($matches[0]) . '}(?!\s)))m', '', $text);
|
||||
// is handled by removing the difference between the shortest whitespace prefix on all
|
||||
// lines and the length of the "/* " opening sequence.
|
||||
$prefixLen = $this->getShortestWhitespacePrefixLen(substr($text, $newlinePos + 1));
|
||||
$removeLen = $prefixLen - strlen($matches[0]);
|
||||
return preg_replace('(^\s{' . $removeLen . '})m', '', $text);
|
||||
}
|
||||
|
||||
// No idea how to format this comment, so simply return as is
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function getShortestWhitespacePrefixLen($str) {
|
||||
$lines = explode("\n", $str);
|
||||
$shortestPrefixLen = INF;
|
||||
foreach ($lines as $line) {
|
||||
preg_match('(^\s*)', $line, $matches);
|
||||
$prefixLen = strlen($matches[0]);
|
||||
if ($prefixLen < $shortestPrefixLen) {
|
||||
$shortestPrefixLen = $prefixLen;
|
||||
}
|
||||
}
|
||||
return $shortestPrefixLen;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
// Technically not a node, but we make it look like one anyway
|
||||
$type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment';
|
||||
return [
|
||||
'nodeType' => $type,
|
||||
'text' => $this->text,
|
||||
'line' => $this->line,
|
||||
'filePos' => $this->filePos,
|
||||
];
|
||||
}
|
||||
}
|
@ -2,6 +2,9 @@
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Parser\Tokens;
|
||||
|
||||
class Lexer
|
||||
{
|
||||
protected $code;
|
||||
@ -9,6 +12,7 @@ class Lexer
|
||||
protected $pos;
|
||||
protected $line;
|
||||
protected $filePos;
|
||||
protected $prevCloseTagHasNewline;
|
||||
|
||||
protected $tokenMap;
|
||||
protected $dropTokens;
|
||||
@ -19,10 +23,10 @@ class Lexer
|
||||
* Creates a Lexer.
|
||||
*
|
||||
* @param array $options Options array. Currently only the 'usedAttributes' option is supported,
|
||||
* which is an array of attributes to add to the AST nodes. Possible attributes
|
||||
* are: 'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos',
|
||||
* 'startFilePos', 'endFilePos'. The option defaults to the first three.
|
||||
* For more info see getNextToken() docs.
|
||||
* which is an array of attributes to add to the AST nodes. Possible
|
||||
* attributes are: 'comments', 'startLine', 'endLine', 'startTokenPos',
|
||||
* 'endTokenPos', 'startFilePos', 'endFilePos'. The option defaults to the
|
||||
* first three. For more info see getNextToken() docs.
|
||||
*/
|
||||
public function __construct(array $options = array()) {
|
||||
// map from internal tokens to PhpParser tokens
|
||||
@ -30,7 +34,9 @@ class Lexer
|
||||
|
||||
// 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.)
|
||||
$this->dropTokens = array_fill_keys(array(T_WHITESPACE, T_OPEN_TAG), 1);
|
||||
$this->dropTokens = array_fill_keys(
|
||||
array(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
|
||||
// value (here "true")
|
||||
@ -62,17 +68,28 @@ class Lexer
|
||||
$this->pos = -1;
|
||||
$this->line = 1;
|
||||
$this->filePos = 0;
|
||||
|
||||
// If inline HTML occurs without preceding code, treat it as if it had a leading newline.
|
||||
// This ensures proper composability, because having a newline is the "safe" assumption.
|
||||
$this->prevCloseTagHasNewline = true;
|
||||
}
|
||||
|
||||
protected function resetErrors() {
|
||||
// set error_get_last() to defined state by forcing an undefined variable error
|
||||
set_error_handler(function() { return false; }, 0);
|
||||
@$undefinedVariable;
|
||||
restore_error_handler();
|
||||
if (function_exists('error_clear_last')) {
|
||||
error_clear_last();
|
||||
} else {
|
||||
// set error_get_last() to defined state by forcing an undefined variable error
|
||||
set_error_handler(function() { return false; }, 0);
|
||||
@$undefinedVariable;
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleErrors() {
|
||||
$error = error_get_last();
|
||||
if (null === $error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match(
|
||||
'~^Unterminated comment starting line ([0-9]+)$~',
|
||||
@ -111,7 +128,7 @@ class Lexer
|
||||
* * 'startTokenPos' => Offset into the token array of the first token in the node.
|
||||
* * 'endTokenPos' => Offset into the token array of the last token in the node.
|
||||
* * 'startFilePos' => Offset into the code string of the first character that is part of the node.
|
||||
* * 'endFilePos' => Offset into the code string of the last character that is part of the node
|
||||
* * 'endFilePos' => Offset into the code string of the last character that is part of the node.
|
||||
*
|
||||
* @param mixed $value Variable to store token content in
|
||||
* @param mixed $startAttributes Variable to store start attributes in
|
||||
@ -131,6 +148,9 @@ class Lexer
|
||||
$token = "\0";
|
||||
}
|
||||
|
||||
if (isset($this->usedAttributes['startLine'])) {
|
||||
$startAttributes['startLine'] = $this->line;
|
||||
}
|
||||
if (isset($this->usedAttributes['startTokenPos'])) {
|
||||
$startAttributes['startTokenPos'] = $this->pos;
|
||||
}
|
||||
@ -138,63 +158,53 @@ class Lexer
|
||||
$startAttributes['startFilePos'] = $this->filePos;
|
||||
}
|
||||
|
||||
if (is_string($token)) {
|
||||
// bug in token_get_all
|
||||
if ('b"' === $token) {
|
||||
$value = 'b"';
|
||||
if (\is_string($token)) {
|
||||
$value = $token;
|
||||
if (isset($token[1])) {
|
||||
// bug in token_get_all
|
||||
$this->filePos += 2;
|
||||
$id = ord('"');
|
||||
} else {
|
||||
$value = $token;
|
||||
$this->filePos += 1;
|
||||
$id = ord($token);
|
||||
}
|
||||
|
||||
if (isset($this->usedAttributes['startLine'])) {
|
||||
$startAttributes['startLine'] = $this->line;
|
||||
}
|
||||
if (isset($this->usedAttributes['endLine'])) {
|
||||
$endAttributes['endLine'] = $this->line;
|
||||
}
|
||||
if (isset($this->usedAttributes['endTokenPos'])) {
|
||||
$endAttributes['endTokenPos'] = $this->pos;
|
||||
}
|
||||
if (isset($this->usedAttributes['endFilePos'])) {
|
||||
$endAttributes['endFilePos'] = $this->filePos - 1;
|
||||
} elseif (!isset($this->dropTokens[$token[0]])) {
|
||||
$value = $token[1];
|
||||
$id = $this->tokenMap[$token[0]];
|
||||
if (T_CLOSE_TAG === $token[0]) {
|
||||
$this->prevCloseTagHasNewline = false !== strpos($token[1], "\n");
|
||||
} else if (T_INLINE_HTML === $token[0]) {
|
||||
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
|
||||
}
|
||||
|
||||
return $id;
|
||||
$this->line += substr_count($value, "\n");
|
||||
$this->filePos += \strlen($value);
|
||||
} else {
|
||||
$this->line += substr_count($token[1], "\n");
|
||||
$this->filePos += strlen($token[1]);
|
||||
|
||||
if (T_COMMENT === $token[0]) {
|
||||
if (T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0]) {
|
||||
if (isset($this->usedAttributes['comments'])) {
|
||||
$startAttributes['comments'][] = new Comment($token[1], $token[2]);
|
||||
$comment = T_DOC_COMMENT === $token[0]
|
||||
? new Comment\Doc($token[1], $this->line, $this->filePos)
|
||||
: new Comment($token[1], $this->line, $this->filePos);
|
||||
$startAttributes['comments'][] = $comment;
|
||||
}
|
||||
} elseif (T_DOC_COMMENT === $token[0]) {
|
||||
if (isset($this->usedAttributes['comments'])) {
|
||||
$startAttributes['comments'][] = new Comment\Doc($token[1], $token[2]);
|
||||
}
|
||||
} elseif (!isset($this->dropTokens[$token[0]])) {
|
||||
$value = $token[1];
|
||||
|
||||
if (isset($this->usedAttributes['startLine'])) {
|
||||
$startAttributes['startLine'] = $token[2];
|
||||
}
|
||||
if (isset($this->usedAttributes['endLine'])) {
|
||||
$endAttributes['endLine'] = $this->line;
|
||||
}
|
||||
if (isset($this->usedAttributes['endTokenPos'])) {
|
||||
$endAttributes['endTokenPos'] = $this->pos;
|
||||
}
|
||||
if (isset($this->usedAttributes['endFilePos'])) {
|
||||
$endAttributes['endFilePos'] = $this->filePos - 1;
|
||||
}
|
||||
|
||||
return $this->tokenMap[$token[0]];
|
||||
}
|
||||
|
||||
$this->line += substr_count($token[1], "\n");
|
||||
$this->filePos += \strlen($token[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($this->usedAttributes['endLine'])) {
|
||||
$endAttributes['endLine'] = $this->line;
|
||||
}
|
||||
if (isset($this->usedAttributes['endTokenPos'])) {
|
||||
$endAttributes['endTokenPos'] = $this->pos;
|
||||
}
|
||||
if (isset($this->usedAttributes['endFilePos'])) {
|
||||
$endAttributes['endFilePos'] = $this->filePos - 1;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Reached end of lexer loop');
|
||||
@ -254,18 +264,18 @@ class Lexer
|
||||
for ($i = 256; $i < 1000; ++$i) {
|
||||
if (T_DOUBLE_COLON === $i) {
|
||||
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
|
||||
$tokenMap[$i] = Parser::T_PAAMAYIM_NEKUDOTAYIM;
|
||||
$tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
|
||||
} elseif(T_OPEN_TAG_WITH_ECHO === $i) {
|
||||
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
|
||||
$tokenMap[$i] = Parser::T_ECHO;
|
||||
$tokenMap[$i] = Tokens::T_ECHO;
|
||||
} elseif(T_CLOSE_TAG === $i) {
|
||||
// T_CLOSE_TAG is equivalent to ';'
|
||||
$tokenMap[$i] = ord(';');
|
||||
} elseif ('UNKNOWN' !== $name = token_name($i)) {
|
||||
if ('T_HASHBANG' === $name) {
|
||||
// HHVM uses a special token for #! hashbang lines
|
||||
$tokenMap[$i] = Parser::T_INLINE_HTML;
|
||||
} else if (defined($name = 'PhpParser\Parser::' . $name)) {
|
||||
$tokenMap[$i] = Tokens::T_INLINE_HTML;
|
||||
} else if (defined($name = 'PhpParser\Parser\Tokens::' . $name)) {
|
||||
// Other tokens can be mapped directly
|
||||
$tokenMap[$i] = constant($name);
|
||||
}
|
||||
@ -274,11 +284,11 @@ class Lexer
|
||||
|
||||
// HHVM uses a special token for numbers that overflow to double
|
||||
if (defined('T_ONUMBER')) {
|
||||
$tokenMap[T_ONUMBER] = Parser::T_DNUMBER;
|
||||
$tokenMap[T_ONUMBER] = Tokens::T_DNUMBER;
|
||||
}
|
||||
// HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant
|
||||
if (defined('T_COMPILER_HALT_OFFSET')) {
|
||||
$tokenMap[T_COMPILER_HALT_OFFSET] = Parser::T_STRING;
|
||||
$tokenMap[T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
|
||||
}
|
||||
|
||||
return $tokenMap;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace PhpParser\Lexer;
|
||||
|
||||
use PhpParser\Parser;
|
||||
use PhpParser\Parser\Tokens;
|
||||
|
||||
/**
|
||||
* ATTENTION: This code is WRITE-ONLY. Do not try to read it.
|
||||
@ -21,23 +21,12 @@ class Emulative extends \PhpParser\Lexer
|
||||
|
||||
const PHP_7_0 = '7.0.0dev';
|
||||
const PHP_5_6 = '5.6.0rc1';
|
||||
const PHP_5_5 = '5.5.0beta1';
|
||||
const PHP_5_4 = '5.4.0beta1';
|
||||
|
||||
public function __construct(array $options = array()) {
|
||||
parent::__construct($options);
|
||||
|
||||
$newKeywordsPerVersion = array(
|
||||
self::PHP_5_5 => array(
|
||||
'finally' => Parser::T_FINALLY,
|
||||
'yield' => Parser::T_YIELD,
|
||||
),
|
||||
self::PHP_5_4 => array(
|
||||
'callable' => Parser::T_CALLABLE,
|
||||
'insteadof' => Parser::T_INSTEADOF,
|
||||
'trait' => Parser::T_TRAIT,
|
||||
'__trait__' => Parser::T_TRAIT_C,
|
||||
),
|
||||
// No new keywords since PHP 5.5
|
||||
);
|
||||
|
||||
$this->newKeywords = array();
|
||||
@ -52,16 +41,16 @@ class Emulative extends \PhpParser\Lexer
|
||||
if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
|
||||
return;
|
||||
}
|
||||
$this->tokenMap[self::T_COALESCE] = Parser::T_COALESCE;
|
||||
$this->tokenMap[self::T_SPACESHIP] = Parser::T_SPACESHIP;
|
||||
$this->tokenMap[self::T_YIELD_FROM] = Parser::T_YIELD_FROM;
|
||||
$this->tokenMap[self::T_COALESCE] = Tokens::T_COALESCE;
|
||||
$this->tokenMap[self::T_SPACESHIP] = Tokens::T_SPACESHIP;
|
||||
$this->tokenMap[self::T_YIELD_FROM] = Tokens::T_YIELD_FROM;
|
||||
|
||||
if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
|
||||
return;
|
||||
}
|
||||
$this->tokenMap[self::T_ELLIPSIS] = Parser::T_ELLIPSIS;
|
||||
$this->tokenMap[self::T_POW] = Parser::T_POW;
|
||||
$this->tokenMap[self::T_POW_EQUAL] = Parser::T_POW_EQUAL;
|
||||
$this->tokenMap[self::T_ELLIPSIS] = Tokens::T_ELLIPSIS;
|
||||
$this->tokenMap[self::T_POW] = Tokens::T_POW;
|
||||
$this->tokenMap[self::T_POW_EQUAL] = Tokens::T_POW_EQUAL;
|
||||
}
|
||||
|
||||
public function startLexing($code) {
|
||||
@ -106,12 +95,7 @@ class Emulative extends \PhpParser\Lexer
|
||||
$code = preg_replace('((?<!/)\*\*=)', '~__EMU__POWEQUAL__~', $code);
|
||||
$code = preg_replace('((?<!/)\*\*(?!/))', '~__EMU__POW__~', $code);
|
||||
|
||||
if (version_compare(PHP_VERSION, self::PHP_5_4, '>=')) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
// binary notation (0b010101101001...)
|
||||
return preg_replace('(\b0b[01]+\b)', '~__EMU__BINARY__$0__~', $code);
|
||||
return $code;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -130,14 +114,7 @@ class Emulative extends \PhpParser\Lexer
|
||||
&& T_STRING === $this->tokens[$i + 1][0]
|
||||
&& preg_match('(^__EMU__([A-Z]++)__(?:([A-Za-z0-9]++)__)?$)', $this->tokens[$i + 1][1], $matches)
|
||||
) {
|
||||
if ('BINARY' === $matches[1]) {
|
||||
// the binary number can either be an integer or a double, so return a LNUMBER
|
||||
// or DNUMBER respectively
|
||||
$isInt = is_int(bindec($matches[2]));
|
||||
$replace = array(
|
||||
array($isInt ? T_LNUMBER : T_DNUMBER, $matches[2], $this->tokens[$i + 1][2])
|
||||
);
|
||||
} else if ('ELLIPSIS' === $matches[1]) {
|
||||
if ('ELLIPSIS' === $matches[1]) {
|
||||
$replace = array(
|
||||
array(self::T_ELLIPSIS, '...', $this->tokens[$i + 1][2])
|
||||
);
|
||||
@ -158,7 +135,7 @@ class Emulative extends \PhpParser\Lexer
|
||||
array(self::T_SPACESHIP, '<=>', $this->tokens[$i + 1][2]),
|
||||
);
|
||||
} else if ('YIELDFROM' === $matches[1]) {
|
||||
$content = $this->hex2bin($matches[2]);
|
||||
$content = hex2bin($matches[2]);
|
||||
$replace = array(
|
||||
array(self::T_YIELD_FROM, $content, $this->tokens[$i + 1][2] - substr_count($content, "\n"))
|
||||
);
|
||||
@ -187,9 +164,7 @@ class Emulative extends \PhpParser\Lexer
|
||||
* multichar tokens (like strings) to their original value.
|
||||
*/
|
||||
public function restoreContentCallback(array $matches) {
|
||||
if ('BINARY' === $matches[1]) {
|
||||
return $matches[2];
|
||||
} else if ('ELLIPSIS' === $matches[1]) {
|
||||
if ('ELLIPSIS' === $matches[1]) {
|
||||
return '...';
|
||||
} else if ('POW' === $matches[1]) {
|
||||
return '**';
|
||||
@ -200,30 +175,25 @@ class Emulative extends \PhpParser\Lexer
|
||||
} else if ('SPACESHIP' === $matches[1]) {
|
||||
return '<=>';
|
||||
} else if ('YIELDFROM' === $matches[1]) {
|
||||
return $this->hex2bin($matches[2]);
|
||||
return hex2bin($matches[2]);
|
||||
} else {
|
||||
return $matches[0];
|
||||
}
|
||||
}
|
||||
|
||||
private function hex2bin($str) {
|
||||
// TODO Drop when removing support for PHP 5.3
|
||||
return pack('H*', $str);
|
||||
}
|
||||
|
||||
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
|
||||
$token = parent::getNextToken($value, $startAttributes, $endAttributes);
|
||||
|
||||
// replace new keywords by their respective tokens. This is not done
|
||||
// if we currently are in an object access (e.g. in $obj->namespace
|
||||
// "namespace" stays a T_STRING tokens and isn't converted to T_NAMESPACE)
|
||||
if (Parser::T_STRING === $token && !$this->inObjectAccess) {
|
||||
if (Tokens::T_STRING === $token && !$this->inObjectAccess) {
|
||||
if (isset($this->newKeywords[strtolower($value)])) {
|
||||
return $this->newKeywords[strtolower($value)];
|
||||
}
|
||||
} else {
|
||||
// keep track of whether we currently are in an object access (after ->)
|
||||
$this->inObjectAccess = Parser::T_OBJECT_OPERATOR === $token;
|
||||
$this->inObjectAccess = Tokens::T_OBJECT_OPERATOR === $token;
|
||||
}
|
||||
|
||||
return $token;
|
||||
|
@ -22,7 +22,7 @@ class Arg extends NodeAbstract
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $value, $byRef = false, $unpack = false, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->value = $value;
|
||||
$this->byRef = $byRef;
|
||||
$this->unpack = $unpack;
|
||||
|
@ -19,7 +19,7 @@ class Const_ extends NodeAbstract
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, Expr $value, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class ArrayDimFetch extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, Expr $dim = null, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
$this->dim = $dim;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class ArrayItem extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $value, Expr $key = null, $byRef = false, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
$this->byRef = $byRef;
|
||||
|
@ -6,6 +6,10 @@ use PhpParser\Node\Expr;
|
||||
|
||||
class Array_ extends Expr
|
||||
{
|
||||
// For use in "kind" attribute
|
||||
const KIND_LONG = 1; // array() syntax
|
||||
const KIND_SHORT = 2; // [] syntax
|
||||
|
||||
/** @var ArrayItem[] Items */
|
||||
public $items;
|
||||
|
||||
@ -16,7 +20,7 @@ class Array_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $items = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class Assign extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
@ -4,10 +4,6 @@ namespace PhpParser\Node\Expr;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
|
||||
/**
|
||||
* @property Expr $var Variable
|
||||
* @property Expr $expr Expression
|
||||
*/
|
||||
abstract class AssignOp extends Expr
|
||||
{
|
||||
/** @var Expr Variable */
|
||||
@ -23,7 +19,7 @@ abstract class AssignOp extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
@ -4,10 +4,6 @@ namespace PhpParser\Node\Expr;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
|
||||
/**
|
||||
* @property Expr $var Variable reference is assigned to
|
||||
* @property Expr $expr Variable which is referenced
|
||||
*/
|
||||
class AssignRef extends Expr
|
||||
{
|
||||
/** @var Expr Variable reference is assigned to */
|
||||
@ -23,7 +19,7 @@ class AssignRef extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ abstract class BinaryOp extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $left, Expr $right, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->left = $left;
|
||||
$this->right = $right;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class BitwiseNot extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class BooleanNot extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ abstract class Cast extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ class ClassConstFetch extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($class, $name, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->class = $class;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class Clone_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ class Closure extends Expr implements FunctionLike
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $subNodes = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->static = isset($subNodes['static']) ? $subNodes['static'] : false;
|
||||
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
|
||||
$this->params = isset($subNodes['params']) ? $subNodes['params'] : array();
|
||||
|
@ -19,7 +19,7 @@ class ClosureUse extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($var, $byRef = false, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
$this->byRef = $byRef;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class ConstFetch extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Name $name, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Empty_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
27
lib/PhpParser/Node/Expr/Error.php
Normal file
27
lib/PhpParser/Node/Expr/Error.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\Node\Expr;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
|
||||
/**
|
||||
* Error node used during parsing with error recovery.
|
||||
*
|
||||
* An error node may be placed at a position where an expression is required, but an error occurred.
|
||||
* Error nodes will not be present if the parser is run in throwOnError mode (the default).
|
||||
*/
|
||||
class Error extends Expr
|
||||
{
|
||||
/**
|
||||
* Constructs an error node.
|
||||
*
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array();
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ class ErrorSuppress extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Eval_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,10 @@ use PhpParser\Node\Expr;
|
||||
|
||||
class Exit_ extends Expr
|
||||
{
|
||||
/* For use in "kind" attribute */
|
||||
const KIND_EXIT = 1;
|
||||
const KIND_DIE = 2;
|
||||
|
||||
/** @var null|Expr Expression */
|
||||
public $expr;
|
||||
|
||||
@ -16,7 +20,7 @@ class Exit_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr = null, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ class FuncCall extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $args = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->name = $name;
|
||||
$this->args = $args;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class Include_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, $type, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class Instanceof_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, $class, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
$this->class = $class;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class Isset_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $vars, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->vars = $vars;
|
||||
}
|
||||
|
||||
|
@ -6,21 +6,21 @@ use PhpParser\Node\Expr;
|
||||
|
||||
class List_ extends Expr
|
||||
{
|
||||
/** @var Expr[] List of variables to assign to */
|
||||
public $vars;
|
||||
/** @var ArrayItem[] List of items to assign to */
|
||||
public $items;
|
||||
|
||||
/**
|
||||
* Constructs a list() destructuring node.
|
||||
*
|
||||
* @param Expr[] $vars List of variables to assign to
|
||||
* @param array $attributes Additional attributes
|
||||
* @param ArrayItem[] $items List of items to assign to
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $vars, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
$this->vars = $vars;
|
||||
public function __construct(array $items, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('vars');
|
||||
return array('items');
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class MethodCall extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, $name, array $args = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
$this->name = $name;
|
||||
$this->args = $args;
|
||||
|
@ -20,7 +20,7 @@ class New_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($class, array $args = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->class = $class;
|
||||
$this->args = $args;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class PostDec extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class PostInc extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class PreDec extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class PreInc extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Print_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class PropertyFetch extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $var, $name, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->var = $var;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class ShellExec extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $parts, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->parts = $parts;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ class StaticCall extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($class, $name, array $args = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->class = $class;
|
||||
$this->name = $name;
|
||||
$this->args = $args;
|
||||
|
@ -20,7 +20,7 @@ class StaticPropertyFetch extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($class, $name, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->class = $class;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class Ternary extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $cond, $if, Expr $else, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->cond = $cond;
|
||||
$this->if = $if;
|
||||
$this->else = $else;
|
||||
|
@ -16,7 +16,7 @@ class UnaryMinus extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class UnaryPlus extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Variable extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class YieldFrom extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class Yield_ extends Expr
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $value = null, Expr $key = null, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class Name extends NodeAbstract
|
||||
$parts = explode('\\', $parts);
|
||||
}
|
||||
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->parts = $parts;
|
||||
}
|
||||
|
||||
@ -103,49 +103,80 @@ class Name extends NodeAbstract
|
||||
return implode('\\', $this->parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the whole name.
|
||||
*
|
||||
* @param string|array|self $name The name to set the whole name to
|
||||
*/
|
||||
public function set($name) {
|
||||
$this->parts = $this->prepareName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends a name to this name.
|
||||
*
|
||||
* @deprecated Use Name::concat($name1, $name2) instead
|
||||
*
|
||||
* @param string|array|self $name Name to prepend
|
||||
*/
|
||||
public function prepend($name) {
|
||||
$this->parts = array_merge($this->prepareName($name), $this->parts);
|
||||
$this->parts = array_merge(self::prepareName($name), $this->parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a name to this name.
|
||||
*
|
||||
* @deprecated Use Name::concat($name1, $name2) instead
|
||||
*
|
||||
* @param string|array|self $name Name to append
|
||||
*/
|
||||
public function append($name) {
|
||||
$this->parts = array_merge($this->parts, $this->prepareName($name));
|
||||
$this->parts = array_merge($this->parts, self::prepareName($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the first part of the name.
|
||||
* Gets a slice of a name (similar to array_slice).
|
||||
*
|
||||
* @param string|array|self $name The name to set the first part to
|
||||
* This method returns a new instance of the same type as the original and with the same
|
||||
* attributes.
|
||||
*
|
||||
* If the slice is empty, a Name with an empty parts array is returned. While this is
|
||||
* meaningless in itself, it works correctly in conjunction with concat().
|
||||
*
|
||||
* Offset and length have the same meaning as in array_slice().
|
||||
*
|
||||
* @param int $offset Offset to start the slice at (may be negative)
|
||||
* @param int|null $length Length of the slice (may be negative)
|
||||
*
|
||||
* @return static Sliced name
|
||||
*/
|
||||
public function setFirst($name) {
|
||||
array_splice($this->parts, 0, 1, $this->prepareName($name));
|
||||
public function slice($offset, $length = null) {
|
||||
$numParts = count($this->parts);
|
||||
|
||||
$realOffset = $offset < 0 ? $offset + $numParts : $offset;
|
||||
if ($realOffset < 0 || $realOffset > $numParts) {
|
||||
throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset));
|
||||
}
|
||||
|
||||
if (null === $length) {
|
||||
$realLength = $numParts - $realOffset;
|
||||
} else {
|
||||
$realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
|
||||
if ($realLength < 0 || $realLength > $numParts) {
|
||||
throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
|
||||
}
|
||||
}
|
||||
|
||||
return new static(array_slice($this->parts, $realOffset, $realLength), $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last part of the name.
|
||||
* Concatenate two names, yielding a new Name instance.
|
||||
*
|
||||
* @param string|array|self $name The name to set the last part to
|
||||
* The type of the generated instance depends on which class this method is called on, for
|
||||
* example Name\FullyQualified::concat() will yield a Name\FullyQualified instance.
|
||||
*
|
||||
* @param string|array|self $name1 The first name
|
||||
* @param string|array|self $name2 The second name
|
||||
* @param array $attributes Attributes to assign to concatenated name
|
||||
*
|
||||
* @return static Concatenated name
|
||||
*/
|
||||
public function setLast($name) {
|
||||
array_splice($this->parts, -1, 1, $this->prepareName($name));
|
||||
public static function concat($name1, $name2, array $attributes = []) {
|
||||
return new static(
|
||||
array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,7 +187,7 @@ class Name extends NodeAbstract
|
||||
*
|
||||
* @return array Prepared name
|
||||
*/
|
||||
protected function prepareName($name) {
|
||||
private static function prepareName($name) {
|
||||
if (is_string($name)) {
|
||||
return explode('\\', $name);
|
||||
} elseif (is_array($name)) {
|
||||
|
26
lib/PhpParser/Node/NullableType.php
Normal file
26
lib/PhpParser/Node/NullableType.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\Node;
|
||||
|
||||
use PhpParser\NodeAbstract;
|
||||
|
||||
class NullableType extends NodeAbstract
|
||||
{
|
||||
/** @var string|Name Type */
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Constructs a nullable type (wrapping another type).
|
||||
*
|
||||
* @param string|Name $type Type
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($type, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('type');
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ class Param extends NodeAbstract
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, Expr $default = null, $type = null, $byRef = false, $variadic = false, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->type = $type;
|
||||
$this->byRef = $byRef;
|
||||
$this->variadic = $variadic;
|
||||
|
@ -15,8 +15,8 @@ class DNumber extends Scalar
|
||||
* @param float $value Value of the number
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($value = 0.0, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
public function __construct($value, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@ class Encapsed extends Scalar
|
||||
* @param array $parts Encaps list
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $parts = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
public function __construct(array $parts, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->parts = $parts;
|
||||
}
|
||||
|
||||
|
26
lib/PhpParser/Node/Scalar/EncapsedStringPart.php
Normal file
26
lib/PhpParser/Node/Scalar/EncapsedStringPart.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
use PhpParser\Node\Scalar;
|
||||
|
||||
class EncapsedStringPart extends Scalar
|
||||
{
|
||||
/** @var string String value */
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Constructs a node representing a string part of an encapsed string.
|
||||
*
|
||||
* @param string $value String value
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($value, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('value');
|
||||
}
|
||||
}
|
@ -2,10 +2,17 @@
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Node\Scalar;
|
||||
|
||||
class LNumber extends Scalar
|
||||
{
|
||||
/* For use in "kind" attribute */
|
||||
const KIND_BIN = 2;
|
||||
const KIND_OCT = 8;
|
||||
const KIND_DEC = 10;
|
||||
const KIND_HEX = 16;
|
||||
|
||||
/** @var int Number value */
|
||||
public $value;
|
||||
|
||||
@ -15,8 +22,8 @@ class LNumber extends Scalar
|
||||
* @param int $value Value of the number
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($value = 0, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
public function __construct($value, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
@ -25,37 +32,36 @@ class LNumber extends Scalar
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Constructs an LNumber node from a string number literal.
|
||||
*
|
||||
* Parses an LNUMBER token (dec, hex, oct and bin notations) like PHP would.
|
||||
* @param string $str String number literal (decimal, octal, hex or binary)
|
||||
* @param array $attributes Additional attributes
|
||||
* @param bool $allowInvalidOctal Whether to allow invalid octal numbers (PHP 5)
|
||||
*
|
||||
* @param string $str A string number
|
||||
*
|
||||
* @return int The parsed number
|
||||
* @return LNumber The constructed LNumber, including kind attribute
|
||||
*/
|
||||
public static function parse($str) {
|
||||
// handle plain 0 specially
|
||||
if ('0' === $str) {
|
||||
return 0;
|
||||
public static function fromString($str, array $attributes = array(), $allowInvalidOctal = false) {
|
||||
if ('0' !== $str[0] || '0' === $str) {
|
||||
$attributes['kind'] = LNumber::KIND_DEC;
|
||||
return new LNumber((int) $str, $attributes);
|
||||
}
|
||||
|
||||
// if first char is 0 (and number isn't 0) it's a special syntax
|
||||
if ('0' === $str[0]) {
|
||||
// hex
|
||||
if ('x' === $str[1] || 'X' === $str[1]) {
|
||||
return hexdec($str);
|
||||
}
|
||||
|
||||
// bin
|
||||
if ('b' === $str[1] || 'B' === $str[1]) {
|
||||
return bindec($str);
|
||||
}
|
||||
|
||||
// oct (intval instead of octdec to get proper cutting behavior with malformed numbers)
|
||||
return intval($str, 8);
|
||||
if ('x' === $str[1] || 'X' === $str[1]) {
|
||||
$attributes['kind'] = LNumber::KIND_HEX;
|
||||
return new LNumber(hexdec($str), $attributes);
|
||||
}
|
||||
|
||||
// dec
|
||||
return (int) $str;
|
||||
if ('b' === $str[1] || 'B' === $str[1]) {
|
||||
$attributes['kind'] = LNumber::KIND_BIN;
|
||||
return new LNumber(bindec($str), $attributes);
|
||||
}
|
||||
|
||||
if (!$allowInvalidOctal && strpbrk($str, '89')) {
|
||||
throw new Error('Invalid numeric literal', $attributes);
|
||||
}
|
||||
|
||||
// use intval instead of octdec to get proper cutting behavior with malformed numbers
|
||||
$attributes['kind'] = LNumber::KIND_OCT;
|
||||
return new LNumber(intval($str, 8), $attributes);
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ abstract class MagicConst extends Scalar
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
|
@ -2,10 +2,17 @@
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Node\Scalar;
|
||||
|
||||
class String_ extends Scalar
|
||||
{
|
||||
/* For use in "kind" attribute */
|
||||
const KIND_SINGLE_QUOTED = 1;
|
||||
const KIND_DOUBLE_QUOTED = 2;
|
||||
const KIND_HEREDOC = 3;
|
||||
const KIND_NOWDOC = 4;
|
||||
|
||||
/** @var string String value */
|
||||
public $value;
|
||||
|
||||
@ -26,8 +33,8 @@ class String_ extends Scalar
|
||||
* @param string $value Value of the string
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($value = '', array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
public function __construct($value, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
@ -41,12 +48,13 @@ class String_ extends Scalar
|
||||
* Parses a string token.
|
||||
*
|
||||
* @param string $str String token content
|
||||
* @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
|
||||
*
|
||||
* @return string The parsed string
|
||||
*/
|
||||
public static function parse($str) {
|
||||
public static function parse($str, $parseUnicodeEscape = true) {
|
||||
$bLength = 0;
|
||||
if ('b' === $str[0]) {
|
||||
if ('b' === $str[0] || 'B' === $str[0]) {
|
||||
$bLength = 1;
|
||||
}
|
||||
|
||||
@ -57,7 +65,9 @@ class String_ extends Scalar
|
||||
substr($str, $bLength + 1, -1)
|
||||
);
|
||||
} else {
|
||||
return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"');
|
||||
return self::parseEscapeSequences(
|
||||
substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,31 +78,54 @@ class String_ extends Scalar
|
||||
*
|
||||
* @param string $str String without quotes
|
||||
* @param null|string $quote Quote type
|
||||
* @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
|
||||
*
|
||||
* @return string String with escape sequences parsed
|
||||
*/
|
||||
public static function parseEscapeSequences($str, $quote) {
|
||||
public static function parseEscapeSequences($str, $quote, $parseUnicodeEscape = true) {
|
||||
if (null !== $quote) {
|
||||
$str = str_replace('\\' . $quote, $quote, $str);
|
||||
}
|
||||
|
||||
$extra = '';
|
||||
if ($parseUnicodeEscape) {
|
||||
$extra = '|u\{([0-9a-fA-F]+)\}';
|
||||
}
|
||||
|
||||
return preg_replace_callback(
|
||||
'~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~',
|
||||
array(__CLASS__, 'parseCallback'),
|
||||
'~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~',
|
||||
function($matches) {
|
||||
$str = $matches[1];
|
||||
|
||||
if (isset(self::$replacements[$str])) {
|
||||
return self::$replacements[$str];
|
||||
} elseif ('x' === $str[0] || 'X' === $str[0]) {
|
||||
return chr(hexdec($str));
|
||||
} elseif ('u' === $str[0]) {
|
||||
return self::codePointToUtf8(hexdec($matches[2]));
|
||||
} else {
|
||||
return chr(octdec($str));
|
||||
}
|
||||
},
|
||||
$str
|
||||
);
|
||||
}
|
||||
|
||||
private static function parseCallback($matches) {
|
||||
$str = $matches[1];
|
||||
|
||||
if (isset(self::$replacements[$str])) {
|
||||
return self::$replacements[$str];
|
||||
} elseif ('x' === $str[0] || 'X' === $str[0]) {
|
||||
return chr(hexdec($str));
|
||||
} else {
|
||||
return chr(octdec($str));
|
||||
private static function codePointToUtf8($num) {
|
||||
if ($num <= 0x7F) {
|
||||
return chr($num);
|
||||
}
|
||||
if ($num <= 0x7FF) {
|
||||
return chr(($num>>6) + 0xC0) . chr(($num&0x3F) + 0x80);
|
||||
}
|
||||
if ($num <= 0xFFFF) {
|
||||
return chr(($num>>12) + 0xE0) . chr((($num>>6)&0x3F) + 0x80) . chr(($num&0x3F) + 0x80);
|
||||
}
|
||||
if ($num <= 0x1FFFFF) {
|
||||
return chr(($num>>18) + 0xF0) . chr((($num>>12)&0x3F) + 0x80)
|
||||
. chr((($num>>6)&0x3F) + 0x80) . chr(($num&0x3F) + 0x80);
|
||||
}
|
||||
throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,10 +135,11 @@ class String_ extends Scalar
|
||||
*
|
||||
* @param string $startToken Doc string start token content (<<<SMTHG)
|
||||
* @param string $str String token content
|
||||
* @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
|
||||
*
|
||||
* @return string Parsed string
|
||||
*/
|
||||
public static function parseDocString($startToken, $str) {
|
||||
public static function parseDocString($startToken, $str, $parseUnicodeEscape = true) {
|
||||
// strip last newline (thanks tokenizer for sticking it into the string!)
|
||||
$str = preg_replace('~(\r\n|\n|\r)\z~', '', $str);
|
||||
|
||||
@ -114,6 +148,6 @@ class String_ extends Scalar
|
||||
return $str;
|
||||
}
|
||||
|
||||
return self::parseEscapeSequences($str, null);
|
||||
return self::parseEscapeSequences($str, null, $parseUnicodeEscape);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class Break_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Node\Expr $num = null, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->num = $num;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class Case_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($cond, array $stmts = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->cond = $cond;
|
||||
$this->stmts = $stmts;
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use PhpParser\Node;
|
||||
|
||||
class Catch_ extends Node\Stmt
|
||||
{
|
||||
/** @var Node\Name Class of exception */
|
||||
public $type;
|
||||
/** @var Node\Name[] Types of exceptions to catch */
|
||||
public $types;
|
||||
/** @var string Variable for exception */
|
||||
public $var;
|
||||
/** @var Node[] Statements */
|
||||
@ -16,19 +16,19 @@ class Catch_ extends Node\Stmt
|
||||
/**
|
||||
* Constructs a catch node.
|
||||
*
|
||||
* @param Node\Name $type Class of exception
|
||||
* @param string $var Variable for exception
|
||||
* @param Node[] $stmts Statements
|
||||
* @param array $attributes Additional attributes
|
||||
* @param Node\Name[] $types Types of exceptions to catch
|
||||
* @param string $var Variable for exception
|
||||
* @param Node[] $stmts Statements
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Node\Name $type, $var, array $stmts = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
$this->type = $type;
|
||||
public function __construct(array $types, $var, array $stmts = array(), array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->types = $types;
|
||||
$this->var = $var;
|
||||
$this->stmts = $stmts;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('type', 'var', 'stmts');
|
||||
return array('types', 'var', 'stmts');
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,12 @@
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Error;
|
||||
|
||||
class ClassConst extends Node\Stmt
|
||||
{
|
||||
/** @var int Modifiers */
|
||||
public $flags;
|
||||
/** @var Node\Const_[] Constant declarations */
|
||||
public $consts;
|
||||
|
||||
@ -13,14 +16,43 @@ class ClassConst extends Node\Stmt
|
||||
* Constructs a class const list node.
|
||||
*
|
||||
* @param Node\Const_[] $consts Constant declarations
|
||||
* @param int $flags Modifiers
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $consts, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
public function __construct(array $consts, $flags = 0, array $attributes = array()) {
|
||||
if ($flags & Class_::MODIFIER_STATIC) {
|
||||
throw new Error("Cannot use 'static' as constant modifier");
|
||||
}
|
||||
if ($flags & Class_::MODIFIER_ABSTRACT) {
|
||||
throw new Error("Cannot use 'abstract' as constant modifier");
|
||||
}
|
||||
if ($flags & Class_::MODIFIER_FINAL) {
|
||||
throw new Error("Cannot use 'final' as constant modifier");
|
||||
}
|
||||
|
||||
parent::__construct($attributes);
|
||||
$this->flags = $flags;
|
||||
$this->consts = $consts;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('consts');
|
||||
return array('flags', 'consts');
|
||||
}
|
||||
|
||||
public function isPublic() {
|
||||
return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0
|
||||
|| ($this->flags & Class_::VISIBILITY_MODIFER_MASK) === 0;
|
||||
}
|
||||
|
||||
public function isProtected() {
|
||||
return (bool) ($this->flags & Class_::MODIFIER_PROTECTED);
|
||||
}
|
||||
|
||||
public function isPrivate() {
|
||||
return (bool) ($this->flags & Class_::MODIFIER_PRIVATE);
|
||||
}
|
||||
|
||||
public function isStatic() {
|
||||
return (bool) ($this->flags & Class_::MODIFIER_STATIC);
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ use PhpParser\Error;
|
||||
|
||||
class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
{
|
||||
/** @var int Type */
|
||||
public $type;
|
||||
/** @var int Flags */
|
||||
public $flags;
|
||||
/** @var bool Whether to return by reference */
|
||||
public $byRef;
|
||||
/** @var string Name */
|
||||
@ -21,12 +21,15 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
/** @var Node[] Statements */
|
||||
public $stmts;
|
||||
|
||||
/** @deprecated Use $flags instead */
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Constructs a class method node.
|
||||
*
|
||||
* @param string $name Name
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
* 'type' => MODIFIER_PUBLIC: Type
|
||||
* 'flags => MODIFIER_PUBLIC: Flags
|
||||
* 'byRef' => false : Whether to return by reference
|
||||
* 'params' => array() : Parameters
|
||||
* 'returnType' => null : Return type
|
||||
@ -34,15 +37,17 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
$this->type = isset($subNodes['type']) ? $subNodes['type'] : 0;
|
||||
parent::__construct($attributes);
|
||||
$this->flags = isset($subNodes['flags']) ? $subNodes['flags']
|
||||
: (isset($subNodes['type']) ? $subNodes['type'] : 0);
|
||||
$this->type = $this->flags;
|
||||
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
|
||||
$this->name = $name;
|
||||
$this->params = isset($subNodes['params']) ? $subNodes['params'] : array();
|
||||
$this->returnType = isset($subNodes['returnType']) ? $subNodes['returnType'] : null;
|
||||
$this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : array();
|
||||
|
||||
if ($this->type & Class_::MODIFIER_STATIC) {
|
||||
if ($this->flags & Class_::MODIFIER_STATIC) {
|
||||
switch (strtolower($this->name)) {
|
||||
case '__construct':
|
||||
throw new Error(sprintf('Constructor %s() cannot be static', $this->name));
|
||||
@ -55,7 +60,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('type', 'byRef', 'name', 'params', 'returnType', 'stmts');
|
||||
return array('flags', 'byRef', 'name', 'params', 'returnType', 'stmts');
|
||||
}
|
||||
|
||||
public function returnsByRef() {
|
||||
@ -75,27 +80,27 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
}
|
||||
|
||||
public function isPublic() {
|
||||
return ($this->type & Class_::MODIFIER_PUBLIC) !== 0
|
||||
|| ($this->type & Class_::VISIBILITY_MODIFER_MASK) === 0;
|
||||
return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0
|
||||
|| ($this->flags & Class_::VISIBILITY_MODIFER_MASK) === 0;
|
||||
}
|
||||
|
||||
public function isProtected() {
|
||||
return (bool) ($this->type & Class_::MODIFIER_PROTECTED);
|
||||
return (bool) ($this->flags & Class_::MODIFIER_PROTECTED);
|
||||
}
|
||||
|
||||
public function isPrivate() {
|
||||
return (bool) ($this->type & Class_::MODIFIER_PRIVATE);
|
||||
return (bool) ($this->flags & Class_::MODIFIER_PRIVATE);
|
||||
}
|
||||
|
||||
public function isAbstract() {
|
||||
return (bool) ($this->type & Class_::MODIFIER_ABSTRACT);
|
||||
return (bool) ($this->flags & Class_::MODIFIER_ABSTRACT);
|
||||
}
|
||||
|
||||
public function isFinal() {
|
||||
return (bool) ($this->type & Class_::MODIFIER_FINAL);
|
||||
return (bool) ($this->flags & Class_::MODIFIER_FINAL);
|
||||
}
|
||||
|
||||
public function isStatic() {
|
||||
return (bool) ($this->type & Class_::MODIFIER_STATIC);
|
||||
return (bool) ($this->flags & Class_::MODIFIER_STATIC);
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,15 @@ class Class_ extends ClassLike
|
||||
const VISIBILITY_MODIFER_MASK = 7; // 1 | 2 | 4
|
||||
|
||||
/** @var int Type */
|
||||
public $type;
|
||||
public $flags;
|
||||
/** @var null|Node\Name Name of extended class */
|
||||
public $extends;
|
||||
/** @var Node\Name[] Names of implemented interfaces */
|
||||
public $implements;
|
||||
|
||||
/** @deprecated Use $flags instead */
|
||||
public $type;
|
||||
|
||||
protected static $specialNames = array(
|
||||
'self' => true,
|
||||
'parent' => true,
|
||||
@ -34,15 +37,17 @@ class Class_ extends ClassLike
|
||||
*
|
||||
* @param string|null $name Name
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
* 'type' => 0 : Type
|
||||
* 'flags' => 0 : Flags
|
||||
* 'extends' => null : Name of extended class
|
||||
* 'implements' => array(): Names of implemented interfaces
|
||||
* 'stmts' => array(): Statements
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
$this->type = isset($subNodes['type']) ? $subNodes['type'] : 0;
|
||||
parent::__construct($attributes);
|
||||
$this->flags = isset($subNodes['flags']) ? $subNodes['flags']
|
||||
: (isset($subNodes['type']) ? $subNodes['type'] : 0);
|
||||
$this->type = $this->flags;
|
||||
$this->name = $name;
|
||||
$this->extends = isset($subNodes['extends']) ? $subNodes['extends'] : null;
|
||||
$this->implements = isset($subNodes['implements']) ? $subNodes['implements'] : array();
|
||||
@ -70,15 +75,15 @@ class Class_ extends ClassLike
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('type', 'name', 'extends', 'implements', 'stmts');
|
||||
return array('flags', 'name', 'extends', 'implements', 'stmts');
|
||||
}
|
||||
|
||||
public function isAbstract() {
|
||||
return (bool) ($this->type & self::MODIFIER_ABSTRACT);
|
||||
return (bool) ($this->flags & self::MODIFIER_ABSTRACT);
|
||||
}
|
||||
|
||||
public function isFinal() {
|
||||
return (bool) ($this->type & self::MODIFIER_FINAL);
|
||||
return (bool) ($this->flags & self::MODIFIER_FINAL);
|
||||
}
|
||||
|
||||
public function isAnonymous() {
|
||||
|
@ -16,7 +16,7 @@ class Const_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $consts, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->consts = $consts;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Continue_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Node\Expr $num = null, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->num = $num;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class DeclareDeclare extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($key, Node\Expr $value, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
@ -14,11 +14,11 @@ class Declare_ extends Node\Stmt
|
||||
* Constructs a declare node.
|
||||
*
|
||||
* @param DeclareDeclare[] $declares List of declares
|
||||
* @param Node[] $stmts Statements
|
||||
* @param Node[]|null $stmts Statements
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $declares, array $stmts, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
public function __construct(array $declares, array $stmts = null, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->declares = $declares;
|
||||
$this->stmts = $stmts;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user