mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-06-25 17:43:47 +02:00
Compare commits
125 Commits
v1.2.0
...
v2.0.0beta
Author | SHA1 | Date | |
---|---|---|---|
33602889c1 | |||
98d28d7aa0 | |||
e4b837e0c4 | |||
2b9c5a62cb | |||
99e89743bd | |||
39a039fa42 | |||
fcf23101dd | |||
c8898df3dd | |||
b2961915a6 | |||
6f3fd7834a | |||
eecaf1e93b | |||
950ada4cba | |||
0d4239ef56 | |||
e3a9356178 | |||
5118e21c6e | |||
40455b5c18 | |||
fe6755ff4c | |||
f57d217e91 | |||
e0a75ededa | |||
2496cd38ad | |||
965e53a164 | |||
dc85742034 | |||
196f177cfe | |||
b9afcdfd92 | |||
3bb874fcec | |||
feb82eed33 | |||
0ef15c111a | |||
d341d94976 | |||
0265c28e6e | |||
5bd8cb84de | |||
ebeeae19a6 | |||
5ede167835 | |||
49324ea412 | |||
21e51c8cf6 | |||
e7a2abb03b | |||
b862de1f5b | |||
8090531acd | |||
49253c5dbb | |||
c5ac17711d | |||
1d62e9d8cc | |||
e3195c246f | |||
2a3bc608dc | |||
90ab32f046 | |||
7434a682e5 | |||
0fbb5f90a1 | |||
5e6627c895 | |||
f872fa9b0b | |||
d5668f536d | |||
a5db176903 | |||
7fbbf83011 | |||
42e368e964 | |||
9c9a8cddce | |||
fd7ee2e083 | |||
179d32cfaf | |||
f2b7a31509 | |||
d8312a09a3 | |||
813c9f1545 | |||
ef9a154d09 | |||
0da72fad00 | |||
71fa7c6674 | |||
f3f24e03ae | |||
bc21514ecf | |||
04e05907c3 | |||
61e060694d | |||
602b9807eb | |||
f372a4c4ab | |||
d18dcc0c7f | |||
74c57eef0e | |||
fdbddc4b8c | |||
ca3b44bf60 | |||
dca46febc9 | |||
1a1bd1448d | |||
9620f79cdc | |||
583b560f71 | |||
db3dafd64d | |||
bb2c5303ae | |||
5038dcc251 | |||
69c00ebbe4 | |||
a0e7d5e0aa | |||
51ec2a25fe | |||
5d1e3be7d4 | |||
9d42e4a2e2 | |||
cc75dd3612 | |||
5f621c5adc | |||
29b9015f51 | |||
e6619f5514 | |||
6b4a17b3e0 | |||
30abe062f2 | |||
dff239267f | |||
8b64195cf2 | |||
9caa51b3a5 | |||
5513073a53 | |||
659d26c231 | |||
648800f07f | |||
23bf4f0c13 | |||
57ac7e39bf | |||
272ab6c8d8 | |||
0731b47655 | |||
3b7d8e8b5d | |||
a35c2a2067 | |||
55b2ead967 | |||
e1a0ec3724 | |||
66896dbde6 | |||
338bc1f8e7 | |||
ab80054e97 | |||
a6d2cd69f8 | |||
6996db1e3a | |||
e30c3ac01b | |||
a5477a4eaf | |||
7a40498cb4 | |||
65f1adbe65 | |||
c8454271e1 | |||
62f83a0dc2 | |||
611fa5c7f1 | |||
4defbc2174 | |||
33a39fae06 | |||
e4eab9ec0c | |||
6aaa87f143 | |||
08f97eb4ef | |||
8d18848fb0 | |||
805078e0a9 | |||
52dafafd10 | |||
dba7524b37 | |||
617bf0aa41 | |||
dce19b074b |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
grammar/kmyacc.exe
|
||||
grammar/y.output
|
10
.travis.yml
10
.travis.yml
@ -1,14 +1,20 @@
|
||||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- nightly
|
||||
- hhvm
|
||||
|
||||
install: composer install --prefer-source
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 7.0
|
||||
- php: nightly
|
||||
fast_finish: true
|
||||
|
||||
script: vendor/bin/phpunit
|
122
CHANGELOG.md
122
CHANGELOG.md
@ -1,8 +1,121 @@
|
||||
Version 1.2.1-dev
|
||||
-----------------
|
||||
Version 2.0.0-dev
|
||||
-----------------------
|
||||
|
||||
Nothing yet.
|
||||
|
||||
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)
|
||||
|
||||
### 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 `shortArraySyntax` option to pretty printer, to print all arrays using short syntax.
|
||||
|
||||
Version 2.0.0-alpha1 (2015-07-14)
|
||||
---------------------------------
|
||||
|
||||
A more detailed description of backwards incompatible changes can be found in the
|
||||
[upgrading guide](UPGRADE-2.0.md).
|
||||
|
||||
### Removed
|
||||
|
||||
* Removed support for running on PHP 5.4. 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
|
||||
|
||||
* 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
|
||||
|
||||
* `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)`.
|
||||
|
||||
Version 1.4.0 (2015-07-14)
|
||||
--------------------------
|
||||
|
||||
### 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.
|
||||
|
||||
### Fixed
|
||||
|
||||
* 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.
|
||||
|
||||
Version 1.3.0 (2015-05-02)
|
||||
--------------------------
|
||||
|
||||
### 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)
|
||||
--------------------------
|
||||
|
||||
@ -186,6 +299,5 @@ Version 1.0.0-beta1 (2014-03-27)
|
||||
|
||||
---
|
||||
|
||||
**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 1.0 and 2.0 series. For older changes see the
|
||||
[0.9 series changelog][https://github.com/nikic/PHP-Parser/blob/0.9/CHANGELOG.md].**
|
25
README.md
25
README.md
@ -4,9 +4,9 @@ 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
|
||||
manipulation.
|
||||
|
||||
[**Documentation for version 1.x**][doc_master] (stable; for running on PHP >= 5.3).
|
||||
[**Documentation for version 1.x**][doc_1_x] (stable; for running on PHP >= 5.3).
|
||||
|
||||
[Documentation for version 0.9.x][doc_0_9] (unsupported; for running on PHP 5.2).
|
||||
[Documentation for version 2.x-dev][doc_master] (dev; for running on PHP >= 5.4).
|
||||
|
||||
In a Nutshell
|
||||
-------------
|
||||
@ -22,7 +22,7 @@ hello\world('foo', 'bar' . 'baz');
|
||||
|
||||
You'll get a syntax tree looking roughly like this:
|
||||
|
||||
```
|
||||
```php
|
||||
array(
|
||||
0: Stmt_Echo(
|
||||
exprs: array(
|
||||
@ -70,18 +70,25 @@ 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_master]: https://github.com/nikic/PHP-Parser/tree/master/doc
|
||||
|
71
UPGRADE-2.0.md
Normal file
71
UPGRADE-2.0.md
Normal file
@ -0,0 +1,71 @@
|
||||
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)`.
|
59
bin/php-parse.php → bin/php-parse
Executable file → Normal file
59
bin/php-parse.php → bin/php-parse
Executable file → Normal file
@ -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);
|
||||
|
||||
@ -10,7 +15,7 @@ ini_set('xdebug.var_display_max_children', -1);
|
||||
ini_set('xdebug.var_display_max_data', -1);
|
||||
ini_set('xdebug.var_display_max_depth', -1);
|
||||
|
||||
list($operations, $files) = parseArgs($argv);
|
||||
list($operations, $files, $attributes) = parseArgs($argv);
|
||||
|
||||
/* Dump nodes by default */
|
||||
if (empty($operations)) {
|
||||
@ -21,7 +26,10 @@ if (empty($files)) {
|
||||
showHelp("Must specify at least one file.");
|
||||
}
|
||||
|
||||
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
|
||||
$lexer = new PhpParser\Lexer\Emulative(array('usedAttributes' => array(
|
||||
'startLine', 'endLine', 'startFilePos', 'endFilePos'
|
||||
)));
|
||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
|
||||
$dumper = new PhpParser\NodeDumper;
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$serializer = new PhpParser\Serializer\XML;
|
||||
@ -45,7 +53,17 @@ foreach ($files as $file) {
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
} catch (PhpParser\Error $e) {
|
||||
die("==> Parse Error: {$e->getMessage()}\n");
|
||||
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();
|
||||
}
|
||||
|
||||
die($message . "\n");
|
||||
}
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
@ -71,25 +89,21 @@ foreach ($files as $file) {
|
||||
function showHelp($error) {
|
||||
die($error . "\n\n" .
|
||||
<<<OUTPUT
|
||||
Usage:
|
||||
|
||||
php php-parse.php [operations] file1.php [file2.php ...]
|
||||
|
||||
The file arguments can also be replaced with a code string:
|
||||
|
||||
php php-parse.php [operations] "<?php code"
|
||||
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):
|
||||
|
||||
--dump -d Dump nodes using NodeDumper
|
||||
--pretty-print -p Pretty print file using PrettyPrinter\Standard
|
||||
--serialize-xml Serialize nodes using Serializer\XML
|
||||
--var-dump var_dump() nodes (for exact structure)
|
||||
--resolve-names -N Resolve names using NodeVisitor\NameResolver
|
||||
-d, --dump Dump nodes using NodeDumper
|
||||
-p, --pretty-print Pretty print file using PrettyPrinter\Standard
|
||||
--serialize-xml Serialize nodes using Serializer\XML
|
||||
--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)
|
||||
|
||||
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.
|
||||
|
||||
@ -101,6 +115,9 @@ OUTPUT
|
||||
function parseArgs($args) {
|
||||
$operations = array();
|
||||
$files = array();
|
||||
$attributes = array(
|
||||
'with-column-info' => false,
|
||||
);
|
||||
|
||||
array_shift($args);
|
||||
$parseOptions = true;
|
||||
@ -129,6 +146,10 @@ function parseArgs($args) {
|
||||
case '-N';
|
||||
$operations[] = 'resolve-names';
|
||||
break;
|
||||
case '--with-column-info':
|
||||
case '-c';
|
||||
$attributes['with-column-info'] = true;
|
||||
break;
|
||||
case '--':
|
||||
$parseOptions = false;
|
||||
break;
|
||||
@ -141,5 +162,5 @@ function parseArgs($args) {
|
||||
}
|
||||
}
|
||||
|
||||
return array($operations, $files);
|
||||
return array($operations, $files, $attributes);
|
||||
}
|
@ -10,15 +10,21 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"php": ">=5.4",
|
||||
"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.2-dev"
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 new tokens from 5.5, 5.6 and 7.0 is
|
||||
provided. This allows to parse PHP 7.0 source code running on PHP 5.4, 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,20 +6,15 @@ 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);
|
||||
```
|
||||
|
||||
@ -28,33 +23,41 @@ This ensures that there will be no errors when traversing highly nested node tre
|
||||
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 +140,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 +164,7 @@ try {
|
||||
$code = $prettyPrinter->prettyPrint($stmts);
|
||||
|
||||
echo $code;
|
||||
} catch (PhpParser\Error $e) {
|
||||
} catch (Error $e) {
|
||||
echo 'Parse Error: ', $e->getMessage();
|
||||
}
|
||||
```
|
||||
@ -188,11 +194,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 +226,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 +244,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
|
||||
@ -299,20 +309,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 +357,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) {
|
||||
@ -366,10 +380,10 @@ only the shortname (i.e. the last part of the name), but they need to contain th
|
||||
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 +406,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')
|
||||
@ -38,7 +41,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 +53,7 @@ $node = $factory->namespace('Name\Space')
|
||||
;
|
||||
|
||||
$stmts = array($node);
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard();
|
||||
$prettyPrinter = new PrettyPrinter\Standard();
|
||||
echo $prettyPrinter->prettyPrintFile($stmts);
|
||||
```
|
||||
|
||||
|
77
doc/component/Error.markdown
Normal file
77
doc/component/Error.markdown
Normal file
@ -0,0 +1,77 @@
|
||||
Error handling
|
||||
==============
|
||||
|
||||
Errors during parsing or analysis are represented using the `PhpParser\Error` exception class. In addition to an error
|
||||
message, an error can also store additional information about the location the error occurred at.
|
||||
|
||||
How much location information is available depends on the origin of the error and how many lexer attributes have been
|
||||
enabled. At a minimum the start line of the error is usually available.
|
||||
|
||||
Column information
|
||||
------------------
|
||||
|
||||
In order to receive information about not only the line, but also the column span an error occurred at, the file
|
||||
position attributes in the lexer need to be enabled:
|
||||
|
||||
```php
|
||||
$lexer = new PhpParser\Lexer(array(
|
||||
'usedAttributes' => array('comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'),
|
||||
));
|
||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
|
||||
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
// ...
|
||||
} catch (PhpParser\Error $e) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Before using column information its availability needs to be checked with `$e->hasColumnInfo()`, as the precise
|
||||
location of an error cannot always be determined. The methods for retrieving column information also have to be passed
|
||||
the source code of the parsed file. An example for printing an error:
|
||||
|
||||
```php
|
||||
if ($e->hasColumnInfo()) {
|
||||
echo $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code)
|
||||
. ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code);
|
||||
} else {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
```
|
||||
|
||||
Both line numbers and column numbers are 1-based. EOF errors will be located at the position one past the end of the
|
||||
file.
|
||||
|
||||
Error recovery
|
||||
--------------
|
||||
|
||||
> **EXPERIMENTAL**
|
||||
|
||||
By default the parser will throw an exception upon encountering the first error during parsing. An alternative mode is
|
||||
also supported, in which the parser will remember the error, but try to continue parsing the rest of the source code.
|
||||
|
||||
To enable this mode the `throwOnError` parser option needs to be disabled. Any errors that occurred during parsing can
|
||||
then be retrieved using `$parser->getErrors()`. The `$parser->parse()` method will either return a partial syntax tree
|
||||
or `null` if recovery fails.
|
||||
|
||||
A usage example:
|
||||
|
||||
```php
|
||||
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, null, array(
|
||||
'throwOnError' => false,
|
||||
));
|
||||
|
||||
$stmts = $parser->parse($code);
|
||||
$errors = $parser->getErrors();
|
||||
|
||||
foreach ($errors as $error) {
|
||||
// $error is an ordinary PhpParser\Error
|
||||
}
|
||||
|
||||
if (null !== $stmts) {
|
||||
// $stmts is a best-effort partial AST
|
||||
}
|
||||
```
|
||||
|
||||
The error recovery implementation is experimental -- it currently won't be able to recover from many types of errors.
|
@ -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,23 +1,23 @@
|
||||
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
|
||||
* `analyze.php`: Analyzes the grammer and outputs some info about it
|
||||
* `rebuildParser.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 Node\Name(..., ..., $attributes)`
|
||||
* `Name::abc` is transformed to `Node\Name::abc`
|
||||
`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, ...)`
|
||||
|
||||
Building the parser
|
||||
===================
|
||||
@ -25,5 +25,5 @@ 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.
|
||||
|
||||
By default only the `Parser.php` is built. If you want to additionally build `Parser/Debug.php` and `y.output` run the
|
||||
script with `--debug`. If you want to retain the preprocessed grammar pass `--keep-tmp-grammar`.
|
||||
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,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
const GRAMMAR_FILE = './zend_language_parser.phpy';
|
||||
const GRAMMAR_FILE = './php5.y';
|
||||
|
||||
const LIB = '(?(DEFINE)
|
||||
(?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
|
||||
@ -93,4 +93,4 @@ foreach ($ruleBlocksMatches as $match) {
|
||||
}
|
||||
|
||||
echo $ruleBlockName, ':', "\n", ' ', implode("\n" . ' | ', $rules), "\n", ';', "\n\n";
|
||||
}
|
||||
}
|
||||
|
@ -2,35 +2,39 @@
|
||||
$meta #
|
||||
#semval($) $this->semValue
|
||||
#semval($,%t) $this->semValue
|
||||
#semval(%n) $this->semStack[$this->stackPos-(%l-%n)]
|
||||
#semval(%n,%t) $this->semStack[$this->stackPos-(%l-%n)]
|
||||
#include;
|
||||
#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);
|
||||
protected $gotoTableSize = #(YYGLAST);
|
||||
|
||||
protected $invalidToken = #(YYBADCH);
|
||||
protected $invalidSymbol = #(YYBADCH);
|
||||
protected $errorSymbol = #(YYINTERRTOK);
|
||||
protected $defaultAction = #(YYDEFAULT);
|
||||
protected $unexpectedTokenRule = #(YYUNEXPECTED);
|
||||
|
||||
protected $YY2TBLSTATE = #(YY2TBLSTATE);
|
||||
protected $YYNLSTATES = #(YYNLSTATES);
|
||||
|
||||
#tokenval
|
||||
const %s = %n;
|
||||
#endtokenval
|
||||
|
||||
protected $symbolToName = array(
|
||||
#listvar terminals
|
||||
);
|
||||
@ -86,7 +90,7 @@ class Parser extends ParserAbstract
|
||||
#endif
|
||||
#reduce
|
||||
|
||||
protected function reduceRule%n($attributes) {
|
||||
protected function reduceRule%n() {
|
||||
%b
|
||||
}
|
||||
#noact
|
@ -1,114 +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
|
||||
%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
|
||||
%tokens
|
||||
|
||||
%%
|
||||
|
||||
@ -121,6 +14,25 @@ top_statement_list:
|
||||
| /* empty */ { init(); }
|
||||
;
|
||||
|
||||
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
|
||||
;
|
||||
|
||||
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); }
|
||||
@ -140,21 +52,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:
|
||||
@ -163,7 +111,16 @@ constant_declaration_list:
|
||||
;
|
||||
|
||||
constant_declaration:
|
||||
T_STRING '=' static_scalar { $$ = Const_[$1, $3]; }
|
||||
T_STRING '=' static_scalar { $$ = Node\Const_[$1, $3]; }
|
||||
;
|
||||
|
||||
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:
|
||||
@ -175,19 +132,20 @@ inner_statement:
|
||||
statement { $$ = $1; }
|
||||
| function_declaration_statement { $$ = $1; }
|
||||
| class_declaration_statement { $$ = $1; }
|
||||
| T_HALT_COMPILER { error('__HALT_COMPILER() can only be used from the outermost scope'); }
|
||||
| T_HALT_COMPILER
|
||||
{ throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); }
|
||||
;
|
||||
|
||||
statement:
|
||||
'{' inner_statement_list '}' { $$ = $2; }
|
||||
| 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]; }
|
||||
@ -203,9 +161,9 @@ 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
|
||||
@ -213,6 +171,7 @@ statement:
|
||||
| T_THROW expr ';' { $$ = Stmt\Throw_[$2]; }
|
||||
| T_GOTO T_STRING ';' { $$ = Stmt\Goto_[$2]; }
|
||||
| T_STRING ':' { $$ = Stmt\Label[$1]; }
|
||||
| error { $$ = array(); /* means: no statement */ }
|
||||
;
|
||||
|
||||
catches:
|
||||
@ -247,14 +206,14 @@ 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]; }
|
||||
;
|
||||
@ -382,9 +341,9 @@ non_empty_parameter_list:
|
||||
|
||||
parameter:
|
||||
optional_param_type optional_ref optional_ellipsis T_VARIABLE
|
||||
{ $$ = Param[parseVar($4), null, $1, $2, $3]; }
|
||||
{ $$ = Node\Param[parseVar($4), null, $1, $2, $3]; }
|
||||
| optional_param_type optional_ref optional_ellipsis T_VARIABLE '=' static_scalar
|
||||
{ $$ = Param[parseVar($4), $6, $1, $2, $3]; }
|
||||
{ $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; }
|
||||
;
|
||||
|
||||
type:
|
||||
@ -406,7 +365,7 @@ optional_return_type:
|
||||
argument_list:
|
||||
'(' ')' { $$ = array(); }
|
||||
| '(' non_empty_argument_list ')' { $$ = $2; }
|
||||
| '(' yield_expr ')' { $$ = array(Arg[$2, false, false]); }
|
||||
| '(' yield_expr ')' { $$ = array(Node\Arg[$2, false, false]); }
|
||||
;
|
||||
|
||||
non_empty_argument_list:
|
||||
@ -415,9 +374,9 @@ non_empty_argument_list:
|
||||
;
|
||||
|
||||
argument:
|
||||
expr { $$ = Arg[$1, false, false]; }
|
||||
| '&' variable { $$ = Arg[$2, true, false]; }
|
||||
| T_ELLIPSIS expr { $$ = Arg[$2, false, true]; }
|
||||
expr { $$ = Node\Arg[$1, false, false]; }
|
||||
| '&' variable { $$ = Node\Arg[$2, true, false]; }
|
||||
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
||||
;
|
||||
|
||||
global_var_list:
|
||||
@ -448,9 +407,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]; }
|
||||
| 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]; }
|
||||
;
|
||||
|
||||
@ -467,20 +426,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:
|
||||
@ -615,12 +576,13 @@ expr:
|
||||
| '`' backticks_expr '`' { $$ = Expr\ShellExec[$2]; }
|
||||
| T_PRINT expr { $$ = Expr\Print_[$2]; }
|
||||
| T_YIELD { $$ = Expr\Yield_[null, null]; }
|
||||
| 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:
|
||||
@ -641,14 +603,20 @@ array_expr:
|
||||
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]; }
|
||||
{ $$ = Expr\ArrayDimFetch[Scalar\String_[Scalar\String_::parse($1, false)], $3]; }
|
||||
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
/* alternative array syntax missing intentionally */
|
||||
;
|
||||
|
||||
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:
|
||||
@ -667,7 +635,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]; }
|
||||
@ -735,8 +703,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\String_::parseEscapeSequences($1, '`', false)); }
|
||||
| encaps_list { parseEncapsed($1, '`', false); $$ = $1; }
|
||||
;
|
||||
|
||||
ctor_arguments:
|
||||
@ -747,7 +716,7 @@ ctor_arguments:
|
||||
common_scalar:
|
||||
T_LNUMBER { $$ = Scalar\LNumber[Scalar\LNumber::parse($1)]; }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_[Scalar\String_::parse($1)]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_[Scalar\String_::parse($1, false)]; }
|
||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||
@ -757,14 +726,14 @@ 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)]; }
|
||||
{ $$ = Scalar\String_[Scalar\String_::parseDocString($1, $2, false)]; }
|
||||
| T_START_HEREDOC T_END_HEREDOC
|
||||
{ $$ = Scalar\String_['']; }
|
||||
;
|
||||
|
||||
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]; }
|
||||
@ -809,7 +778,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]; }
|
||||
;
|
||||
|
||||
@ -817,14 +786,9 @@ scalar:
|
||||
common_scalar { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| '"' encaps_list '"'
|
||||
{ parseEncapsed($2, '"'); $$ = Scalar\Encapsed[$2]; }
|
||||
{ parseEncapsed($2, '"', false); $$ = Scalar\Encapsed[$2]; }
|
||||
| T_START_HEREDOC encaps_list T_END_HEREDOC
|
||||
{ parseEncapsedDoc($2); $$ = Scalar\Encapsed[$2]; }
|
||||
;
|
||||
|
||||
class_const_name:
|
||||
T_STRING { $$ = $1; }
|
||||
| T_CLASS { $$ = 'class'; }
|
||||
{ parseEncapsedDoc($2, false); $$ = Scalar\Encapsed[$2]; }
|
||||
;
|
||||
|
||||
static_array_pair_list:
|
808
grammar/php7.y
Normal file
808
grammar/php7.y
Normal file
@ -0,0 +1,808 @@
|
||||
%pure_parser
|
||||
%expect 2
|
||||
|
||||
%tokens
|
||||
|
||||
%%
|
||||
|
||||
start:
|
||||
top_statement_list { $$ = $this->handleNamespaces($1); }
|
||||
;
|
||||
|
||||
top_statement_list:
|
||||
top_statement_list top_statement { pushNormalizing($1, $2); }
|
||||
| /* empty */ { init(); }
|
||||
;
|
||||
|
||||
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
|
||||
;
|
||||
|
||||
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:
|
||||
inner_statement_list inner_statement { pushNormalizing($1, $2); }
|
||||
| /* empty */ { init(); }
|
||||
;
|
||||
|
||||
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()); }
|
||||
;
|
||||
|
||||
statement:
|
||||
'{' inner_statement_list '}' { $$ = $2; }
|
||||
| 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]; }
|
||||
| ';' { $$ = 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]; }
|
||||
| error { $$ = array(); /* means: no statement */ }
|
||||
;
|
||||
|
||||
catches:
|
||||
/* empty */ { init(); }
|
||||
| catches catch { push($1, $2); }
|
||||
;
|
||||
|
||||
catch:
|
||||
T_CATCH '(' name T_VARIABLE ')' '{' inner_statement_list '}'
|
||||
{ $$ = Stmt\Catch_[$3, parseVar($4), $7]; }
|
||||
;
|
||||
|
||||
optional_finally:
|
||||
/* empty */ { $$ = null; }
|
||||
| T_FINALLY '{' inner_statement_list '}' { $$ = $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, $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:
|
||||
statement { $$ = toArray($1); }
|
||||
| ':' 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); }
|
||||
;
|
||||
|
||||
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:
|
||||
name { $$ = $this->handleScalarTypes($1); }
|
||||
| T_ARRAY { $$ = 'array'; }
|
||||
| T_CALLABLE { $$ = 'callable'; }
|
||||
;
|
||||
|
||||
optional_param_type:
|
||||
/* empty */ { $$ = null; }
|
||||
| type { $$ = $1; }
|
||||
;
|
||||
|
||||
optional_return_type:
|
||||
/* empty */ { $$ = null; }
|
||||
| ':' type { $$ = $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]; }
|
||||
| T_CONST class_const_list ';' { $$ = Stmt\ClassConst[$2]; }
|
||||
| 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]; }
|
||||
| variable '=' expr { $$ = Expr\Assign[$1, $3]; }
|
||||
| variable '=' '&' variable { $$ = Expr\AssignRef[$1, $4]; }
|
||||
| variable '=' '&' new_expr { $$ = 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 { $$ = Expr\Exit_ [$2]; }
|
||||
| '@' 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\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]; }
|
||||
;
|
||||
|
||||
dereferencable_scalar:
|
||||
T_ARRAY '(' array_pair_list ')' { $$ = Expr\Array_[$3]; }
|
||||
| '[' array_pair_list ']' { $$ = Expr\Array_[$2]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_[Scalar\String_::parse($1)]; }
|
||||
;
|
||||
|
||||
scalar:
|
||||
T_LNUMBER { $$ = Scalar\LNumber[Scalar\LNumber::parse($1)]; }
|
||||
| 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
|
||||
{ $$ = Scalar\String_[Scalar\String_::parseDocString($1, $2)]; }
|
||||
| T_START_HEREDOC T_END_HEREDOC
|
||||
{ $$ = Scalar\String_['']; }
|
||||
| '"' encaps_list '"'
|
||||
{ parseEncapsed($2, '"', true); $$ = Scalar\Encapsed[$2]; }
|
||||
| T_START_HEREDOC encaps_list T_END_HEREDOC
|
||||
{ parseEncapsedDoc($2, true); $$ = Scalar\Encapsed[$2]; }
|
||||
;
|
||||
|
||||
optional_comma:
|
||||
/* empty */
|
||||
| ','
|
||||
;
|
||||
|
||||
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]; }
|
||||
;
|
||||
|
||||
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 { $$ = $1; }
|
||||
| list_expr { $$ = $1; }
|
||||
| /* empty */ { $$ = null; }
|
||||
;
|
||||
|
||||
array_pair_list:
|
||||
/* empty */ { $$ = array(); }
|
||||
| non_empty_array_pair_list optional_comma { $$ = $1; }
|
||||
;
|
||||
|
||||
non_empty_array_pair_list:
|
||||
non_empty_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]; }
|
||||
;
|
||||
|
||||
encaps_list:
|
||||
encaps_list encaps_var { push($1, $2); }
|
||||
| encaps_list T_ENCAPSED_AND_WHITESPACE { push($1, $2); }
|
||||
| encaps_var { init($1); }
|
||||
| T_ENCAPSED_AND_WHITESPACE encaps_var { init($1, $2); }
|
||||
;
|
||||
|
||||
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,37 +42,47 @@ 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 = resolveConstants($grammarCode);
|
||||
$grammarCode = resolveNodes($grammarCode);
|
||||
$grammarCode = resolveMacros($grammarCode);
|
||||
$grammarCode = resolveArrays($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' : '';
|
||||
|
||||
moveFileWithDirCheck($tmpResultFile, $parserResultFile);
|
||||
echo "Building $name parser.\n";
|
||||
$output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile -p $name $tmpGrammarFile 2>&1"));
|
||||
echo "Output: \"$output\"\n";
|
||||
|
||||
if (!$optionKeepTmpGrammar) {
|
||||
unlink($tmpGrammarFile);
|
||||
$resultCode = file_get_contents($tmpResultFile);
|
||||
$resultCode = removeTrailingWhitespace($resultCode);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
/// Preprocessing functions ///
|
||||
///////////////////////////////
|
||||
|
||||
function resolveConstants($code) {
|
||||
return preg_replace('~[A-Z][a-zA-Z_\\\\]++::~', 'Node\\\\$0', $code);
|
||||
}
|
||||
|
||||
function resolveNodes($code) {
|
||||
return preg_replace_callback(
|
||||
'~(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
|
||||
@ -83,7 +100,7 @@ function resolveNodes($code) {
|
||||
$paramCode .= $param . ', ';
|
||||
}
|
||||
|
||||
return 'new Node\\' . $matches['name'] . '(' . $paramCode . '$attributes)';
|
||||
return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())';
|
||||
},
|
||||
$code
|
||||
);
|
||||
@ -102,10 +119,9 @@ function resolveMacros($code) {
|
||||
$matches['args']
|
||||
);
|
||||
|
||||
if ('error' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return 'throw new Error(' . $args[0] . ')';
|
||||
if ('attributes' == $name) {
|
||||
assertArgs(0, $args, $name);
|
||||
return '$this->startAttributeStack[#1] + $this->endAttributes';
|
||||
}
|
||||
|
||||
if ('init' == $name) {
|
||||
@ -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,18 +154,22 @@ 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 (is_string($s)) {'
|
||||
. ' $s = Node\Scalar\String_::parseEscapeSequences($s, ' . $args[1] . ', ' . $args[2] . '); } }';
|
||||
}
|
||||
|
||||
if ('parseEncapsedDoc' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
assertArgs(2, $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)$~\', \'\', $s); if (\'\' === $s) array_pop(' . $args[0] . ');';
|
||||
return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) {'
|
||||
. ' $s = Node\Scalar\String_::parseEscapeSequences($s, null, ' . $args[1] . '); } }'
|
||||
. ' $s = preg_replace(\'~(\r\n|\n|\r)\z~\', \'\', $s);'
|
||||
. ' if (\'\' === $s) array_pop(' . $args[0] . ');';
|
||||
}
|
||||
|
||||
throw new Exception(sprintf('Unknown macro "%s"', $name));
|
||||
return $matches[0];
|
||||
},
|
||||
$code
|
||||
);
|
||||
@ -160,43 +181,22 @@ 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);
|
||||
return $code;
|
||||
}
|
||||
|
||||
function moveFileWithDirCheck($fromPath, $toPath) {
|
||||
$dir = dirname($toPath);
|
||||
function removeTrailingWhitespace($code) {
|
||||
$lines = explode("\n", $code);
|
||||
$lines = array_map('rtrim', $lines);
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
function ensureDirExists($dir) {
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
rename($fromPath, $toPath);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
@ -214,5 +214,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.
|
||||
*
|
||||
@ -23,10 +20,8 @@ class Autoloader
|
||||
return;
|
||||
}
|
||||
|
||||
ini_set('unserialize_callback_func', 'spl_autoload_call');
|
||||
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
|
||||
self::$registered = true;
|
||||
self::$runningOnPhp7 = version_compare(PHP_VERSION, '7.0-dev', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,223 +31,10 @@ class Autoloader
|
||||
*/
|
||||
static public function autoload($class) {
|
||||
if (0 === strpos($class, 'PhpParser\\')) {
|
||||
if (isset(self::$php7CompatAliases[$class])) {
|
||||
if (!self::$runningOnPhp7) {
|
||||
// Register aliases only on PHP 5.x, otherwise this call will fatal
|
||||
class_alias(self::$php7CompatAliases[$class], $class);
|
||||
}
|
||||
}
|
||||
|
||||
$fileName = dirname(__DIR__) . '/' . strtr($class, '\\', '/') . '.php';
|
||||
if (file_exists($fileName)) {
|
||||
require $fileName;
|
||||
}
|
||||
} 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 $php7CompatAliases = 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');
|
||||
|
@ -9,6 +9,7 @@ use PhpParser\Node\Stmt;
|
||||
class Trait_ extends Declaration
|
||||
{
|
||||
protected $name;
|
||||
protected $properties = array();
|
||||
protected $methods = array();
|
||||
|
||||
/**
|
||||
@ -29,12 +30,15 @@ class Trait_ extends Declaration
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$stmt = $this->normalizeNode($stmt);
|
||||
if (!$stmt instanceof Stmt\ClassMethod) {
|
||||
|
||||
if ($stmt instanceof Stmt\Property) {
|
||||
$this->properties[] = $stmt;
|
||||
} else if ($stmt instanceof Stmt\ClassMethod) {
|
||||
$this->methods[] = $stmt;
|
||||
} else {
|
||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||
}
|
||||
|
||||
$this->methods[] = $stmt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -44,6 +48,8 @@ class Trait_ extends Declaration
|
||||
* @return Stmt\Trait_ The built interface node
|
||||
*/
|
||||
public function getNode() {
|
||||
return new Stmt\Trait_($this->name, $this->methods, $this->attributes);
|
||||
return new Stmt\Trait_(
|
||||
$this->name, array_merge($this->properties, $this->methods), $this->attributes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,12 @@ class Use_ extends BuilderAbstract {
|
||||
$this->alias = $alias;
|
||||
return $this;
|
||||
}
|
||||
public function __call($method, $args) {
|
||||
return call_user_func_array(array($this, $method . '_'), $args);
|
||||
public function __call($name, $args) {
|
||||
if (method_exists($this, $name . '_')) {
|
||||
return call_user_func_array(array($this, $name . '_'), $args);
|
||||
}
|
||||
|
||||
throw new \LogicException(sprintf('Method "%s" does not exist', $name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,17 +5,22 @@ namespace PhpParser;
|
||||
class Error extends \RuntimeException
|
||||
{
|
||||
protected $rawMessage;
|
||||
protected $rawLine;
|
||||
protected $attributes;
|
||||
|
||||
/**
|
||||
* Creates an Exception signifying a parse error.
|
||||
*
|
||||
* @param string $message Error message
|
||||
* @param int $line Error line in PHP file
|
||||
* @param string $message Error message
|
||||
* @param array|int $attributes Attributes of node/token where error occurred
|
||||
* (or start line of error -- deprecated)
|
||||
*/
|
||||
public function __construct($message, $line = -1) {
|
||||
public function __construct($message, $attributes = array()) {
|
||||
$this->rawMessage = (string) $message;
|
||||
$this->rawLine = (int) $line;
|
||||
if (is_array($attributes)) {
|
||||
$this->attributes = $attributes;
|
||||
} else {
|
||||
$this->attributes = array('startLine' => $attributes);
|
||||
}
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
@ -28,6 +33,34 @@ class Error extends \RuntimeException
|
||||
return $this->rawMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line the error starts in.
|
||||
*
|
||||
* @return int Error start line
|
||||
*/
|
||||
public function getStartLine() {
|
||||
return isset($this->attributes['startLine']) ? $this->attributes['startLine'] : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line the error ends in.
|
||||
*
|
||||
* @return int Error end line
|
||||
*/
|
||||
public function getEndLine() {
|
||||
return isset($this->attributes['endLine']) ? $this->attributes['endLine'] : -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the attributes of the node/token the error occurred at.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes() {
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line of the PHP file the error occurred in.
|
||||
*
|
||||
@ -39,22 +72,65 @@ class Error extends \RuntimeException
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error line in the PHP file.
|
||||
* Sets the line the error starts in.
|
||||
*
|
||||
* @return int Error line in the PHP file
|
||||
* @param int $line Error start line
|
||||
*/
|
||||
public function getRawLine() {
|
||||
return $this->rawLine;
|
||||
public function setStartLine($line) {
|
||||
$this->attributes['startLine'] = (int) $line;
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line of the PHP file the error occurred in.
|
||||
* Returns whether the error has start and end column information.
|
||||
*
|
||||
* @param int $line Error line in the PHP file
|
||||
* For column information enable the startFilePos and endFilePos in the lexer options.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setRawLine($line) {
|
||||
$this->rawLine = (int) $line;
|
||||
$this->updateMessage();
|
||||
public function hasColumnInfo() {
|
||||
return isset($this->attributes['startFilePos']) && isset($this->attributes['endFilePos']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start column (1-based) into the line where the error started.
|
||||
*
|
||||
* @param string $code Source code of the file
|
||||
* @return int
|
||||
*/
|
||||
public function getStartColumn($code) {
|
||||
if (!$this->hasColumnInfo()) {
|
||||
throw new \RuntimeException('Error does not have column information');
|
||||
}
|
||||
|
||||
return $this->toColumn($code, $this->attributes['startFilePos']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the end column (1-based) into the line where the error ended.
|
||||
*
|
||||
* @param string $code Source code of the file
|
||||
* @return int
|
||||
*/
|
||||
public function getEndColumn($code) {
|
||||
if (!$this->hasColumnInfo()) {
|
||||
throw new \RuntimeException('Error does not have column information');
|
||||
}
|
||||
|
||||
return $this->toColumn($code, $this->attributes['endFilePos']);
|
||||
}
|
||||
|
||||
private function toColumn($code, $pos) {
|
||||
if ($pos > strlen($code)) {
|
||||
throw new \RuntimeException('Invalid position information');
|
||||
}
|
||||
|
||||
$lineStartPos = strrpos($code, "\n", $pos - strlen($code));
|
||||
if (false === $lineStartPos) {
|
||||
$lineStartPos = -1;
|
||||
}
|
||||
|
||||
return $pos - $lineStartPos;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,10 +139,20 @@ class Error extends \RuntimeException
|
||||
protected function updateMessage() {
|
||||
$this->message = $this->rawMessage;
|
||||
|
||||
if (-1 === $this->rawLine) {
|
||||
if (-1 === $this->getStartLine()) {
|
||||
$this->message .= ' on unknown line';
|
||||
} else {
|
||||
$this->message .= ' on line ' . $this->rawLine;
|
||||
$this->message .= ' on line ' . $this->getStartLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Use getStartLine() instead */
|
||||
public function getRawLine() {
|
||||
return $this->getStartLine();
|
||||
}
|
||||
|
||||
/** @deprecated Use setStartLine() instead */
|
||||
public function setRawLine($line) {
|
||||
$this->setStartLine($line);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
use PhpParser\Parser\Tokens;
|
||||
|
||||
class Lexer
|
||||
{
|
||||
protected $code;
|
||||
@ -48,13 +50,15 @@ class Lexer
|
||||
* @throws Error on lexing errors (unterminated comment or unexpected character)
|
||||
*/
|
||||
public function startLexing($code) {
|
||||
$scream = ini_set('xdebug.scream', 0);
|
||||
$scream = ini_set('xdebug.scream', '0');
|
||||
|
||||
$this->resetErrors();
|
||||
$this->tokens = @token_get_all($code);
|
||||
$this->handleErrors();
|
||||
|
||||
ini_set('xdebug.scream', $scream);
|
||||
if (false !== $scream) {
|
||||
ini_set('xdebug.scream', $scream);
|
||||
}
|
||||
|
||||
$this->code = $code; // keep the code around for __halt_compiler() handling
|
||||
$this->pos = -1;
|
||||
@ -76,7 +80,7 @@ class Lexer
|
||||
'~^Unterminated comment starting line ([0-9]+)$~',
|
||||
$error['message'], $matches
|
||||
)) {
|
||||
throw new Error('Unterminated comment', $matches[1]);
|
||||
throw new Error('Unterminated comment', (int) $matches[1]);
|
||||
}
|
||||
|
||||
if (preg_match(
|
||||
@ -121,8 +125,13 @@ class Lexer
|
||||
$startAttributes = array();
|
||||
$endAttributes = array();
|
||||
|
||||
while (isset($this->tokens[++$this->pos])) {
|
||||
$token = $this->tokens[$this->pos];
|
||||
while (1) {
|
||||
if (isset($this->tokens[++$this->pos])) {
|
||||
$token = $this->tokens[$this->pos];
|
||||
} else {
|
||||
// EOF token with ID 0
|
||||
$token = "\0";
|
||||
}
|
||||
|
||||
if (isset($this->usedAttributes['startTokenPos'])) {
|
||||
$startAttributes['startTokenPos'] = $this->pos;
|
||||
@ -190,10 +199,7 @@ class Lexer
|
||||
}
|
||||
}
|
||||
|
||||
$startAttributes['startLine'] = $this->line;
|
||||
|
||||
// 0 is the EOF token
|
||||
return 0;
|
||||
throw new \RuntimeException('Reached end of lexer loop');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,23 +222,13 @@ class Lexer
|
||||
* @return string Remaining text
|
||||
*/
|
||||
public function handleHaltCompiler() {
|
||||
// get the length of the text before the T_HALT_COMPILER token
|
||||
$textBefore = '';
|
||||
for ($i = 0; $i <= $this->pos; ++$i) {
|
||||
if (is_string($this->tokens[$i])) {
|
||||
$textBefore .= $this->tokens[$i];
|
||||
} else {
|
||||
$textBefore .= $this->tokens[$i][1];
|
||||
}
|
||||
}
|
||||
|
||||
// text after T_HALT_COMPILER, still including ();
|
||||
$textAfter = substr($this->code, strlen($textBefore));
|
||||
$textAfter = substr($this->code, $this->filePos);
|
||||
|
||||
// ensure that it is followed by ();
|
||||
// this simplifies the situation, by not allowing any comments
|
||||
// in between of the tokens.
|
||||
if (!preg_match('~\s*\(\s*\)\s*(?:;|\?>\r?\n?)~', $textAfter, $matches)) {
|
||||
if (!preg_match('~^\s*\(\s*\)\s*(?:;|\?>\r?\n?)~', $textAfter, $matches)) {
|
||||
throw new Error('__HALT_COMPILER must be followed by "();"');
|
||||
}
|
||||
|
||||
@ -260,18 +256,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);
|
||||
}
|
||||
@ -280,7 +276,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] = 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.
|
||||
@ -12,30 +12,24 @@ class Emulative extends \PhpParser\Lexer
|
||||
protected $newKeywords;
|
||||
protected $inObjectAccess;
|
||||
|
||||
const T_ELLIPSIS = 1001;
|
||||
const T_POW = 1002;
|
||||
const T_POW_EQUAL = 1003;
|
||||
const T_COALESCE = 1004;
|
||||
const T_SPACESHIP = 1005;
|
||||
const T_ELLIPSIS = 1001;
|
||||
const T_POW = 1002;
|
||||
const T_POW_EQUAL = 1003;
|
||||
const T_COALESCE = 1004;
|
||||
const T_SPACESHIP = 1005;
|
||||
const T_YIELD_FROM = 1006;
|
||||
|
||||
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,
|
||||
'finally' => Tokens::T_FINALLY,
|
||||
'yield' => Tokens::T_YIELD,
|
||||
),
|
||||
);
|
||||
|
||||
@ -51,15 +45,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_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) {
|
||||
@ -91,6 +86,10 @@ class Emulative extends \PhpParser\Lexer
|
||||
|
||||
$code = str_replace('??', '~__EMU__COALESCE__~', $code);
|
||||
$code = str_replace('<=>', '~__EMU__SPACESHIP__~', $code);
|
||||
$code = preg_replace_callback('(yield[ \n\r\t]+from)', function($matches) {
|
||||
// Encoding $0 in order to preserve exact whitespace
|
||||
return '~__EMU__YIELDFROM__' . bin2hex($matches[0]) . '__~';
|
||||
}, $code);
|
||||
|
||||
if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
|
||||
return $code;
|
||||
@ -100,12 +99,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;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -124,13 +118,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
|
||||
$replace = array(
|
||||
array(is_int(bindec($matches[2])) ? 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])
|
||||
);
|
||||
@ -150,9 +138,13 @@ class Emulative extends \PhpParser\Lexer
|
||||
$replace = array(
|
||||
array(self::T_SPACESHIP, '<=>', $this->tokens[$i + 1][2]),
|
||||
);
|
||||
} else if ('YIELDFROM' === $matches[1]) {
|
||||
$content = hex2bin($matches[2]);
|
||||
$replace = array(
|
||||
array(self::T_YIELD_FROM, $content, $this->tokens[$i + 1][2] - substr_count($content, "\n"))
|
||||
);
|
||||
} else {
|
||||
// just ignore all other __EMU__ sequences
|
||||
continue;
|
||||
throw new \RuntimeException('Invalid __EMU__ sequence');
|
||||
}
|
||||
|
||||
array_splice($this->tokens, $i, 3, $replace);
|
||||
@ -176,9 +168,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 '**';
|
||||
@ -188,6 +178,8 @@ class Emulative extends \PhpParser\Lexer
|
||||
return '??';
|
||||
} else if ('SPACESHIP' === $matches[1]) {
|
||||
return '<=>';
|
||||
} else if ('YIELDFROM' === $matches[1]) {
|
||||
return hex2bin($matches[2]);
|
||||
} else {
|
||||
return $matches[0];
|
||||
}
|
||||
@ -199,15 +191,13 @@ class Emulative extends \PhpParser\Lexer
|
||||
// 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)];
|
||||
}
|
||||
// keep track of whether we currently are in an object access (after ->)
|
||||
} elseif (Parser::T_OBJECT_OPERATOR === $token) {
|
||||
$this->inObjectAccess = true;
|
||||
} else {
|
||||
$this->inObjectAccess = false;
|
||||
// keep track of whether we currently are in an object access (after ->)
|
||||
$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;
|
||||
|
@ -16,7 +16,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;
|
||||
}
|
||||
|
@ -23,7 +23,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;
|
||||
}
|
||||
|
@ -23,7 +23,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;
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,9 @@ namespace PhpParser\Node\Expr;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
|
||||
class Closure extends Expr
|
||||
class Closure extends Expr implements FunctionLike
|
||||
{
|
||||
/** @var bool Whether the closure is static */
|
||||
public $static;
|
||||
@ -15,7 +16,7 @@ class Closure extends Expr
|
||||
public $params;
|
||||
/** @var ClosureUse[] use()s */
|
||||
public $uses;
|
||||
/** @var null|string|Node\Name[] Return type */
|
||||
/** @var null|string|Node\Name Return type */
|
||||
public $returnType;
|
||||
/** @var Node[] Statements */
|
||||
public $stmts;
|
||||
@ -33,7 +34,7 @@ class Closure extends Expr
|
||||
* @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();
|
||||
@ -45,4 +46,20 @@ class Closure extends Expr
|
||||
public function getSubNodeNames() {
|
||||
return array('static', 'byRef', 'params', 'uses', 'returnType', 'stmts');
|
||||
}
|
||||
|
||||
public function returnsByRef() {
|
||||
return $this->byRef;
|
||||
}
|
||||
|
||||
public function getParams() {
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function getReturnType() {
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
public function getStmts() {
|
||||
return $this->stmts;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,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;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class List_ 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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -7,7 +7,7 @@ use PhpParser\Node\Expr;
|
||||
|
||||
class New_ extends Expr
|
||||
{
|
||||
/** @var Node\Name|Expr Class name */
|
||||
/** @var Node\Name|Expr|Node\Stmt\Class_ Class name */
|
||||
public $class;
|
||||
/** @var Node\Arg[] Arguments */
|
||||
public $args;
|
||||
@ -15,12 +15,12 @@ class New_ extends Expr
|
||||
/**
|
||||
* Constructs a function call node.
|
||||
*
|
||||
* @param Node\Name|Expr $class Class name
|
||||
* @param Node\Arg[] $args Arguments
|
||||
* @param array $attributes Additional attributes
|
||||
* @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes)
|
||||
* @param Node\Arg[] $args Arguments
|
||||
* @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;
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ class ShellExec extends Expr
|
||||
* @param array $parts Encapsed string array
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($parts, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
public function __construct(array $parts, array $attributes = array()) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
26
lib/PhpParser/Node/Expr/YieldFrom.php
Normal file
26
lib/PhpParser/Node/Expr/YieldFrom.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\Node\Expr;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
|
||||
class YieldFrom extends Expr
|
||||
{
|
||||
/** @var Expr Expression to yield from */
|
||||
public $expr;
|
||||
|
||||
/**
|
||||
* Constructs an "yield from" node.
|
||||
*
|
||||
* @param Expr $expr Expression
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Expr $expr, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('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;
|
||||
}
|
||||
|
36
lib/PhpParser/Node/FunctionLike.php
Normal file
36
lib/PhpParser/Node/FunctionLike.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\Node;
|
||||
|
||||
use PhpParser\Node;
|
||||
|
||||
interface FunctionLike extends Node
|
||||
{
|
||||
/**
|
||||
* Whether to return by reference
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function returnsByRef();
|
||||
|
||||
/**
|
||||
* List of parameters
|
||||
*
|
||||
* @return Node\Param[]
|
||||
*/
|
||||
public function getParams();
|
||||
|
||||
/**
|
||||
* Get the declared return type or null
|
||||
*
|
||||
* @return null|string|Node\Name
|
||||
*/
|
||||
public function getReturnType();
|
||||
|
||||
/**
|
||||
* The function body
|
||||
*
|
||||
* @return Node\Stmt[]
|
||||
*/
|
||||
public function getStmts();
|
||||
}
|
@ -20,7 +20,7 @@ class Name extends NodeAbstract
|
||||
$parts = explode('\\', $parts);
|
||||
}
|
||||
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->parts = $parts;
|
||||
}
|
||||
|
||||
@ -106,37 +106,45 @@ class Name extends NodeAbstract
|
||||
/**
|
||||
* Sets the whole name.
|
||||
*
|
||||
* @deprecated Create a new Name instead, or manually modify the $parts property
|
||||
*
|
||||
* @param string|array|self $name The name to set the whole name to
|
||||
*/
|
||||
public function set($name) {
|
||||
$this->parts = $this->prepareName($name);
|
||||
$this->parts = self::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.
|
||||
*
|
||||
* @deprecated Use concat($first, $name->slice(1)) instead
|
||||
*
|
||||
* @param string|array|self $name The name to set the first part to
|
||||
*/
|
||||
public function setFirst($name) {
|
||||
array_splice($this->parts, 0, 1, $this->prepareName($name));
|
||||
array_splice($this->parts, 0, 1, self::prepareName($name));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,7 +153,47 @@ class Name extends NodeAbstract
|
||||
* @param string|array|self $name The name to set the last part to
|
||||
*/
|
||||
public function setLast($name) {
|
||||
array_splice($this->parts, -1, 1, $this->prepareName($name));
|
||||
array_splice($this->parts, -1, 1, self::prepareName($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a slice of a name (similar to array_slice).
|
||||
*
|
||||
* 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().
|
||||
*
|
||||
* @param int $offset Offset to start the slice at
|
||||
*
|
||||
* @return static Sliced name
|
||||
*/
|
||||
public function slice($offset) {
|
||||
// TODO negative offset and length
|
||||
if ($offset < 0 || $offset > count($this->parts)) {
|
||||
throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset));
|
||||
}
|
||||
|
||||
return new static(array_slice($this->parts, $offset), $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate two names, yielding a new Name instance.
|
||||
*
|
||||
* 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 static function concat($name1, $name2, array $attributes = []) {
|
||||
return new static(
|
||||
array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,7 +204,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)) {
|
||||
|
@ -28,8 +28,8 @@ class Param extends NodeAbstract
|
||||
* @param bool $variadic Whether this is a variadic argument
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, $default = null, $type = null, $byRef = false, $variadic = false, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
public function __construct($name, Expr $default = null, $type = null, $byRef = false, $variadic = false, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->type = $type;
|
||||
$this->byRef = $byRef;
|
||||
$this->variadic = $variadic;
|
||||
@ -37,7 +37,7 @@ class Param extends NodeAbstract
|
||||
$this->default = $default;
|
||||
|
||||
if ($variadic && null !== $default) {
|
||||
throw new Error('Variadic parameter cannot have a default value');
|
||||
throw new Error('Variadic parameter cannot have a default value', $default->getAttributes());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,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;
|
||||
}
|
||||
|
||||
|
@ -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,6 +2,7 @@
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Node\Scalar;
|
||||
|
||||
class String_ extends Scalar
|
||||
@ -26,8 +27,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,10 +42,11 @@ 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]) {
|
||||
$bLength = 1;
|
||||
@ -57,7 +59,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 +72,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,18 +129,19 @@ 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)$~', '', $str);
|
||||
$str = preg_replace('~(\r\n|\n|\r)\z~', '', $str);
|
||||
|
||||
// nowdoc string
|
||||
if (false !== strpos($startToken, '\'')) {
|
||||
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;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class Catch_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Node\Name $type, $var, array $stmts = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->type = $type;
|
||||
$this->var = $var;
|
||||
$this->stmts = $stmts;
|
||||
|
@ -16,7 +16,7 @@ class ClassConst 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;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,11 @@ abstract class ClassLike extends Node\Stmt {
|
||||
/** @var Node[] Statements */
|
||||
public $stmts;
|
||||
|
||||
/**
|
||||
* Gets all methods defined directly in this class/interface/trait
|
||||
*
|
||||
* @return ClassMethod[]
|
||||
*/
|
||||
public function getMethods() {
|
||||
$methods = array();
|
||||
foreach ($this->stmts as $stmt) {
|
||||
@ -19,4 +24,21 @@ abstract class ClassLike extends Node\Stmt {
|
||||
}
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets method with the given name defined directly in this class/interface/trait.
|
||||
*
|
||||
* @param string $name Name of the method (compared case-insensitively)
|
||||
*
|
||||
* @return ClassMethod|null Method node or null if the method does not exist
|
||||
*/
|
||||
public function getMethod($name) {
|
||||
$lowerName = strtolower($name);
|
||||
foreach ($this->stmts as $stmt) {
|
||||
if ($stmt instanceof ClassMethod && $lowerName === strtolower($stmt->name)) {
|
||||
return $stmt;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,10 @@
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Error;
|
||||
|
||||
class ClassMethod extends Node\Stmt
|
||||
class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
{
|
||||
/** @var int Type */
|
||||
public $type;
|
||||
@ -15,7 +16,7 @@ class ClassMethod extends Node\Stmt
|
||||
public $name;
|
||||
/** @var Node\Param[] Parameters */
|
||||
public $params;
|
||||
/** @var null|string|Node\Name[] Return type */
|
||||
/** @var null|string|Node\Name Return type */
|
||||
public $returnType;
|
||||
/** @var Node[] Statements */
|
||||
public $stmts;
|
||||
@ -33,7 +34,7 @@ class ClassMethod extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->type = isset($subNodes['type']) ? $subNodes['type'] : 0;
|
||||
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
|
||||
$this->name = $name;
|
||||
@ -57,8 +58,25 @@ class ClassMethod extends Node\Stmt
|
||||
return array('type', 'byRef', 'name', 'params', 'returnType', 'stmts');
|
||||
}
|
||||
|
||||
public function returnsByRef() {
|
||||
return $this->byRef;
|
||||
}
|
||||
|
||||
public function getParams() {
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function getReturnType() {
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
public function getStmts() {
|
||||
return $this->stmts;
|
||||
}
|
||||
|
||||
public function isPublic() {
|
||||
return ($this->type & Class_::MODIFIER_PUBLIC) !== 0 || $this->type === 0;
|
||||
return ($this->type & Class_::MODIFIER_PUBLIC) !== 0
|
||||
|| ($this->type & Class_::VISIBILITY_MODIFER_MASK) === 0;
|
||||
}
|
||||
|
||||
public function isProtected() {
|
||||
|
@ -32,7 +32,7 @@ class Class_ extends ClassLike
|
||||
/**
|
||||
* Constructs a class node.
|
||||
*
|
||||
* @param string $name Name
|
||||
* @param string|null $name Name
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
* 'type' => 0 : Type
|
||||
* 'extends' => null : Name of extended class
|
||||
@ -41,24 +41,30 @@ class Class_ extends ClassLike
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->type = isset($subNodes['type']) ? $subNodes['type'] : 0;
|
||||
$this->name = $name;
|
||||
$this->extends = isset($subNodes['extends']) ? $subNodes['extends'] : null;
|
||||
$this->implements = isset($subNodes['implements']) ? $subNodes['implements'] : array();
|
||||
$this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
|
||||
|
||||
if (isset(self::$specialNames[(string) $this->name])) {
|
||||
if (null !== $this->name && isset(self::$specialNames[strtolower($this->name)])) {
|
||||
throw new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $this->name));
|
||||
}
|
||||
|
||||
if (isset(self::$specialNames[(string) $this->extends])) {
|
||||
throw new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $this->extends));
|
||||
if (isset(self::$specialNames[strtolower($this->extends)])) {
|
||||
throw new Error(
|
||||
sprintf('Cannot use \'%s\' as class name as it is reserved', $this->extends),
|
||||
$this->extends->getAttributes()
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->implements as $interface) {
|
||||
if (isset(self::$specialNames[(string) $interface])) {
|
||||
throw new Error(sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface));
|
||||
if (isset(self::$specialNames[strtolower($interface)])) {
|
||||
throw new Error(
|
||||
sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
|
||||
$interface->getAttributes()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,6 +81,10 @@ class Class_ extends ClassLike
|
||||
return (bool) ($this->type & self::MODIFIER_FINAL);
|
||||
}
|
||||
|
||||
public function isAnonymous() {
|
||||
return null === $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class Declare_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $declares, array $stmts, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->declares = $declares;
|
||||
$this->stmts = $stmts;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class Do_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Node\Expr $cond, array $stmts = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->cond = $cond;
|
||||
$this->stmts = $stmts;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class Echo_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $exprs, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->exprs = $exprs;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class ElseIf_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Node\Expr $cond, array $stmts = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->cond = $cond;
|
||||
$this->stmts = $stmts;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class Else_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $stmts = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->stmts = $stmts;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ class For_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $subNodes = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->init = isset($subNodes['init']) ? $subNodes['init'] : array();
|
||||
$this->cond = isset($subNodes['cond']) ? $subNodes['cond'] : array();
|
||||
$this->loop = isset($subNodes['loop']) ? $subNodes['loop'] : array();
|
||||
|
@ -29,7 +29,7 @@ class Foreach_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->expr = $expr;
|
||||
$this->keyVar = isset($subNodes['keyVar']) ? $subNodes['keyVar'] : null;
|
||||
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
|
||||
|
@ -3,8 +3,9 @@
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
|
||||
class Function_ extends Node\Stmt
|
||||
class Function_ extends Node\Stmt implements FunctionLike
|
||||
{
|
||||
/** @var bool Whether function returns by reference */
|
||||
public $byRef;
|
||||
@ -12,7 +13,7 @@ class Function_ extends Node\Stmt
|
||||
public $name;
|
||||
/** @var Node\Param[] Parameters */
|
||||
public $params;
|
||||
/** @var null|string|Node\Name[] Return type */
|
||||
/** @var null|string|Node\Name Return type */
|
||||
public $returnType;
|
||||
/** @var Node[] Statements */
|
||||
public $stmts;
|
||||
@ -29,7 +30,7 @@ class Function_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
|
||||
$this->name = $name;
|
||||
$this->params = isset($subNodes['params']) ? $subNodes['params'] : array();
|
||||
@ -40,4 +41,20 @@ class Function_ extends Node\Stmt
|
||||
public function getSubNodeNames() {
|
||||
return array('byRef', 'name', 'params', 'returnType', 'stmts');
|
||||
}
|
||||
|
||||
public function returnsByRef() {
|
||||
return $this->byRef;
|
||||
}
|
||||
|
||||
public function getParams() {
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function getReturnType() {
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
public function getStmts() {
|
||||
return $this->stmts;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class Global_ extends Node\Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $vars, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->vars = $vars;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Goto_ extends Stmt
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($name, array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
parent::__construct($attributes);
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
|
35
lib/PhpParser/Node/Stmt/GroupUse.php
Normal file
35
lib/PhpParser/Node/Stmt/GroupUse.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\Node\Stmt;
|
||||
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Name;
|
||||
|
||||
class GroupUse extends Stmt
|
||||
{
|
||||
/** @var int Type of group use */
|
||||
public $type;
|
||||
/** @var Name Prefix for uses */
|
||||
public $prefix;
|
||||
/** @var UseUse[] Uses */
|
||||
public $uses;
|
||||
|
||||
/**
|
||||
* Constructs a group use node.
|
||||
*
|
||||
* @param Name $prefix Prefix for uses
|
||||
* @param UseUse[] $uses Uses
|
||||
* @param int $type Type of group use
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(Name $prefix, array $uses, $type = Use_::TYPE_NORMAL, array $attributes = array()) {
|
||||
parent::__construct($attributes);
|
||||
$this->type = $type;
|
||||
$this->prefix = $prefix;
|
||||
$this->uses = $uses;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() {
|
||||
return array('type', 'prefix', 'uses');
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user