Compare commits

..

2 Commits
v2.0.0 ... 1.x

Author SHA1 Message Date
c4bbc8e236 Support hashbang before namespace declaration
Fixes issue #243.
2016-01-15 22:03:42 +01:00
f78af2c9c8 Release PHP-Parser 1.4.1 2015-09-19 22:15:08 +08:00
203 changed files with 4035 additions and 10511 deletions

4
.gitignore vendored
View File

@ -1,4 +0,0 @@
vendor/
composer.lock
grammar/kmyacc.exe
grammar/y.output

View File

@ -1,20 +1,14 @@
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: nightly
- php: 7.0
fast_finish: true
script: vendor/bin/phpunit

View File

@ -1,77 +1,16 @@
Version 2.0.1-dev
Version 1.4.2-dev
-----------------
Nothing yet.
Version 2.0.0 (2015-12-04)
Version 1.4.1 (2015-09-19)
--------------------------
### Changed
* String parts of encapsed strings are now represented using `Scalar\EncapsStringPart` nodes.
Previously raw strings were used. This affects the `parts` child of `Scalar\Encaps` and
`Expr\ShellExec`. The change has been done to allow assignment of attributes to encapsed string
parts.
Version 2.0.0-beta1 (2015-10-21)
--------------------------------
### Fixed
* Fixed issue with too many newlines being stripped at the end of heredoc/nowdoc strings in some
cases. (#227)
### 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)
--------------------------
@ -309,5 +248,6 @@ Version 1.0.0-beta1 (2014-03-27)
---
**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].**
**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

View File

@ -1,13 +1,12 @@
PHP Parser
==========
This is a PHP 5.2 to PHP 7.0 parser written in PHP. Its purpose is to simplify static code analysis and
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_1_x] (stable; for running on PHP >= 5.3).
[**Documentation for version 2.x**][doc_master] (stable; for running on PHP >= 5.4; for parsing PHP 5.2 to PHP 7.0).
[Documentation for version 1.x][doc_1_x] (stable; for running on PHP >= 5.3; for parsing PHP 5.2 to PHP 5.6).
[Documentation for version 0.9.x][doc_0_9] (unsupported; for running on PHP 5.2).
In a Nutshell
-------------
@ -71,25 +70,18 @@ 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. [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)
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)
Component documentation:
1. [Error](doc/component/Error.markdown)
2. [Lexer](doc/component/Lexer.markdown)
1. [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

View File

@ -1,74 +0,0 @@
Upgrading from PHP-Parser 1.x to 2.0
====================================
### PHP version requirements
PHP-Parser now requires PHP 5.4 or newer to run. It is however still possible to *parse* PHP 5.2 and
PHP 5.3 source code, while running on a newer version.
### Creating a parser instance
Parser instances should now be created through the `ParserFactory`. Old direct instantiation code
will not work, because the parser class was renamed.
Old:
```php
use PhpParser\Parser, PhpParser\Lexer;
$parser = new Parser(new Lexer\Emulative);
```
New:
```php
use PhpParser\ParserFactory;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
```
The first argument to `ParserFactory` determines how different PHP versions are handled. The
possible values are:
* `ParserFactory::PREFER_PHP7`: Try to parse code as PHP 7. If this fails, try to parse it as PHP 5.
* `ParserFactory::PREFER_PHP5`: Try to parse code as PHP 5. If this fails, try to parse it as PHP 7.
* `ParserFactory::ONLY_PHP7`: Parse code as PHP 7.
* `ParserFactory::ONLY_PHP5`: Parse code as PHP 5.
For most practical purposes the difference between `PREFER_PHP7` and `PREFER_PHP5` is mainly whether
a scalar type hint like `string` will be stored as `'string'` (PHP 7) or as `new Name('string')`
(PHP 5).
To use a custom lexer, pass it as the second argument to the `create()` method:
```php
use PhpParser\ParserFactory;
$lexer = new MyLexer;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7, $lexer);
```
### Rename of the `PhpParser\Parser` class
`PhpParser\Parser` is now an interface, which is implemented by `Parser\Php5`, `Parser\Php7` and
`Parser\Multiple`. Parser tokens are now defined in `Parser\Tokens`. If you use the `ParserFactory`
described above to create your parser instance, these changes should have no further impact on you.
### Removal of legacy aliases
All legacy aliases for classes have been removed. This includes the old non-namespaced `PHPParser_`
classes, as well as the classes that had to be renamed for PHP 7 support.
### Deprecations
The `set()`, `setFirst()`, `append()` and `prepend()` methods of the `Node\Name` class have been
deprecated. Instead `Name::concat()` and `Name->slice()` should be used.
### Miscellaneous
* The `NodeTraverser` no longer clones nodes by default. If you want to restore the old behavior,
pass `true` to the constructor.
* The legacy node format has been removed. If you use custom nodes, they are now expected to
implement a `getSubNodeNames()` method.
* The default value for `Scalar` node constructors was removed. This means that something like
`new LNumber()` should be replaced by `new LNumber(0)`.
* String parts of encapsed strings are now represented using `Scalar\EncapsStringPart` nodes, while
previously raw strings were used. This affects the `parts` child of `Scalar\Encaps` and
`Expr\ShellExec`.

15
bin/php-parse → bin/php-parse.php Normal file → Executable file
View File

@ -1,12 +1,7 @@
#!/usr/bin/env php
<?php
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
if (file_exists($file)) {
require $file;
break;
}
}
require __DIR__ . '/../lib/bootstrap.php';
ini_set('xdebug.max_nesting_level', 3000);
@ -29,7 +24,7 @@ if (empty($files)) {
$lexer = new PhpParser\Lexer\Emulative(array('usedAttributes' => array(
'startLine', 'endLine', 'startFilePos', 'endFilePos'
)));
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
$parser = new PhpParser\Parser($lexer);
$dumper = new PhpParser\NodeDumper;
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$serializer = new PhpParser\Serializer\XML;
@ -89,8 +84,8 @@ foreach ($files as $file) {
function showHelp($error) {
die($error . "\n\n" .
<<<OUTPUT
Usage: php-parse [operations] file1.php [file2.php ...]
or: php-parse [operations] "<?php code"
Usage: php php-parse.php [operations] file1.php [file2.php ...]
or: php php-parse.php [operations] "<?php code"
Turn PHP source code into an abstract syntax tree.
Operations is a list of the following options (--dump by default):
@ -103,7 +98,7 @@ Operations is a list of the following options (--dump by default):
-c, --with-column-info Show column-numbers for errors (if available)
Example:
php-parse -d -p -N -d file.php
php php-parse.php -d -p -N -d file.php
Dumps nodes, pretty prints them, then resolves names and dumps them again.

View File

@ -10,21 +10,15 @@
}
],
"require": {
"php": ">=5.4",
"php": ">=5.3",
"ext-tokenizer": "*"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-4": {
"PhpParser\\": "lib/PhpParser"
}
"files": ["lib/bootstrap.php"]
},
"bin": ["bin/php-parse"],
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
"dev-master": "1.4-dev"
}
}
}

View File

@ -1,7 +1,7 @@
Introduction
============
This project is a PHP 5.2 to PHP 7.0 parser **written in PHP itself**.
This project is a PHP 5.2 to PHP 5.6 parser **written in PHP itself**.
What is this for?
-----------------
@ -26,12 +26,13 @@ programmatic PHP code analysis are incidentally PHP developers, not C developers
What can it parse?
------------------
The parser supports parsing PHP 5.2-5.6 and PHP 7.
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).
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.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.
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.
What output does it produce?
----------------------------

View File

@ -0,0 +1,31 @@
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

View File

@ -6,15 +6,20 @@ This document explains how to use the parser, the pretty printer and the node tr
Bootstrapping
-------------
To bootstrap the library, include the autoloader generated by composer:
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:
```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);
```
@ -23,41 +28,33 @@ This ensures that there will be no errors when traversing highly nested node tre
Parsing
-------
In order to parse code, you first have to create a parser instance:
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:
```php
use PhpParser\ParserFactory;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
<?php
$parser = new PhpParser\Parser(new PhpParser\Lexer);
// or
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
```
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).
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.
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
use PhpParser\Error;
use PhpParser\ParserFactory;
<?php
$code = '<?php // some code';
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
try {
$stmts = $parser->parse($code);
// $stmts is an array of statement nodes
} catch (Error $e) {
} catch (PhpParser\Error $e) {
echo 'Parse Error: ', $e->getMessage();
}
```
@ -140,14 +137,11 @@ information the formatting is done using a specified scheme. Currently there is
namely `PhpParser\PrettyPrinter\Standard`.
```php
use PhpParser\Error;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
<?php
$code = "<?php echo 'Hi ', hi\\getTarget();";
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$prettyPrinter = new PrettyPrinter\Standard;
$parser = new PhpParser\Parser(new PhpParser\Lexer);
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
try {
// parse
@ -164,7 +158,7 @@ try {
$code = $prettyPrinter->prettyPrint($stmts);
echo $code;
} catch (Error $e) {
} catch (PhpParser\Error $e) {
echo 'Parse Error: ', $e->getMessage();
}
```
@ -194,13 +188,11 @@ 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
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
<?php
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$traverser = new NodeTraverser;
$prettyPrinter = new PrettyPrinter\Standard;
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
$traverser = new PhpParser\NodeTraverser;
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
// add your visitor
$traverser->addVisitor(new MyNodeVisitor);
@ -226,10 +218,10 @@ try {
The corresponding node visitor might look like this:
```php
<?php
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
class MyNodeVisitor extends NodeVisitorAbstract
class MyNodeVisitor extends PhpParser\NodeVisitorAbstract
{
public function leaveNode(Node $node) {
if ($node instanceof Node\Scalar\String_) {
@ -244,12 +236,10 @@ 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:
```php
public function beforeTraverse(array $nodes);
public function enterNode(\PhpParser\Node $node);
public function leaveNode(\PhpParser\Node $node);
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
@ -309,24 +299,20 @@ assume that no dynamic features are used.
We start off with the following base code:
```php
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
<?php
$inDir = '/some/path';
$outDir = '/some/other/path';
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$traverser = new NodeTraverser;
$prettyPrinter = new PrettyPrinter\Standard;
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
$traverser = new PhpParser\NodeTraverser;
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$traverser->addVisitor(new NameResolver); // we will need resolved names
$traverser->addVisitor(new NamespaceConverter); // our own node visitor
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver); // we will need resolved names
$traverser->addVisitor(new NodeVisitor\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 {
@ -357,9 +343,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 NamespaceConverter extends \PhpParser\NodeVisitorAbstract
class NodeVisitor_NamespaceConverter extends PhpParser\NodeVisitorAbstract
{
public function leaveNode(Node $node) {
if ($node instanceof Node\Name) {
@ -380,10 +366,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) {
@ -406,10 +392,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) {

View File

@ -17,6 +17,7 @@ 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
@ -27,7 +28,7 @@ function printLine($msg) {
printLine('Hello World!!!');
CODE;
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7);
$parser = new PhpParser\Parser(new PhpParser\Lexer);
$nodeDumper = new PhpParser\NodeDumper;
try {
@ -105,7 +106,7 @@ function printLine($msg) {
printLine('Hello World!!!');
CODE;
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7);
$parser = new PhpParser\Parser(new PhpParser\Lexer);
$serializer = new PhpParser\Serializer\XML;
try {

View File

@ -14,11 +14,8 @@ the following syntactic elements:
Here is an example:
```php
use PhpParser\BuilderFactory;
use PhpParser\PrettyPrinter;
use PhpParser\Node;
$factory = new BuilderFactory;
<?php
$factory = new PhpParser\BuilderFactory;
$node = $factory->namespace('Name\Space')
->addStmt($factory->use('Some\Other\Thingy')->as('SomeOtherClass'))
->addStmt($factory->class('SomeClass')
@ -41,7 +38,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 Node\Expr\Print_(new Node\Expr\Variable('someParam')))
->addStmt(new PhpParser\Node\Expr\Print_(new PhpParser\Node\Expr\Variable('someParam')))
)
// properties will be correctly reordered above the methods
@ -53,7 +50,7 @@ $node = $factory->namespace('Name\Space')
;
$stmts = array($node);
$prettyPrinter = new PrettyPrinter\Standard();
$prettyPrinter = new PhpParser\PrettyPrinter\Standard();
echo $prettyPrinter->prettyPrintFile($stmts);
```

View File

@ -17,7 +17,7 @@ position attributes in the lexer need to be enabled:
$lexer = new PhpParser\Lexer(array(
'usedAttributes' => array('comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'),
));
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
$parser = new PhpParser\Parser($lexer);
try {
$stmts = $parser->parse($code);
@ -58,7 +58,7 @@ or `null` if recovery fails.
A usage example:
```php
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, null, array(
$parser = new PhpParser\Parser(new PhpParser\Lexer, array(
'throwOnError' => false,
));

View File

@ -72,7 +72,7 @@ $lexer = new PhpParser\Lexer(array(
'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'
)
));
$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
$parser = new PhpParser\Parser($lexer);
$visitor = new MyNodeVisitor();
$traverser = new PhpParser\NodeTraverser();
@ -127,17 +127,14 @@ 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
use PhpParser\Lexer;
use PhpParser\Parser\Tokens;
class KeepOriginalValueLexer extends Lexer // or Lexer\Emulative
class KeepOriginalValueLexer extends PHPParser\Lexer // or PHPParser\Lexer\Emulative
{
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
$tokenId = parent::getNextToken($value, $startAttributes, $endAttributes);
if ($tokenId == Tokens::T_CONSTANT_ENCAPSED_STRING // non-interpolated string
|| $tokenId == Tokens::T_LNUMBER // integer
|| $tokenId == Tokens::T_DNUMBER // floating point number
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
) {
// could also use $startAttributes, doesn't really matter here
$endAttributes['originalValue'] = $value;

View File

@ -1,23 +1,22 @@
What do all those files mean?
=============================
* `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`
* `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
.phpy pseudo language
=====================
The `.y` file is a normal grammer in `kmyacc` (`yacc`) style, with some transformations
The `.phpy` file is a normal grammer in `kmyacc` (`yacc`) style, with some transformations
applied to it:
* Nodes are created using the syntax `Name[..., ...]`. This is transformed into
`new Name(..., ..., attributes())`
* Some function-like constructs are resolved (see `rebuildParser.php` for a list)
* Associative arrays are written as `[key: value, ...]`, which is transformed to
`array('key' => value, ...)`
Building the parser
===================

View File

@ -1,6 +1,6 @@
<?php
const GRAMMAR_FILE = './php5.y';
const GRAMMAR_FILE = './zend_language_parser.phpy';
const LIB = '(?(DEFINE)
(?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')

View File

@ -5,23 +5,16 @@ $meta #
#semval(%n) $this->stackPos-(%l-%n)
#semval(%n,%t) $this->stackPos-(%l-%n)
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;
namespace PhpParser;
#include;
/* This is an automatically GENERATED file, which should not be manually edited.
* Instead edit one of the following:
* * the grammar files grammar/php5.y or grammar/php7.y
* * the skeleton file grammar/parser.template
* * the preprocessing script grammar/rebuildParsers.php
* * the grammar file grammar/zend_language_parser.phpy
* * the skeleton file grammar/kymacc.php.parser
* * the preprocessing script grammar/rebuildParser.php
*/
class #(-p) extends \PhpParser\ParserAbstract
class Parser extends ParserAbstract
{
protected $tokenToSymbolMapSize = #(YYMAXLEX);
protected $actionTableSize = #(YYLAST);
@ -35,6 +28,10 @@ class #(-p) extends \PhpParser\ParserAbstract
protected $YY2TBLSTATE = #(YY2TBLSTATE);
protected $YYNLSTATES = #(YYNLSTATES);
#tokenval
const %s = %n;
#endtokenval
protected $symbolToName = array(
#listvar terminals
);

View File

@ -1,813 +0,0 @@
%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\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`')]); }
| encaps_list { parseEncapsed($1, '`', true); $$ = $1; }
;
ctor_arguments:
/* empty */ { $$ = array(); }
| argument_list { $$ = $1; }
;
constant:
name { $$ = Expr\ConstFetch[$1]; }
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier
{ $$ = Expr\ClassConstFetch[$1, $3]; }
;
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 encaps_string_part { push($1, $2); }
| encaps_var { init($1); }
| encaps_string_part encaps_var { init($1, $2); }
;
encaps_string_part:
T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart[$1]; }
;
encaps_var:
T_VARIABLE { $$ = Expr\Variable[parseVar($1)]; }
| T_VARIABLE '[' encaps_var_offset ']' { $$ = Expr\ArrayDimFetch[Expr\Variable[parseVar($1)], $3]; }
| T_VARIABLE T_OBJECT_OPERATOR T_STRING { $$ = Expr\PropertyFetch[Expr\Variable[parseVar($1)], $3]; }
| T_DOLLAR_OPEN_CURLY_BRACES expr '}' { $$ = Expr\Variable[$2]; }
| T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}' { $$ = Expr\Variable[$2]; }
| T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}'
{ $$ = Expr\ArrayDimFetch[Expr\Variable[$2], $4]; }
| T_CURLY_OPEN variable '}' { $$ = $2; }
;
encaps_var_offset:
T_STRING { $$ = Scalar\String_[$1]; }
| T_NUM_STRING { $$ = Scalar\String_[$1]; }
| T_VARIABLE { $$ = Expr\Variable[parseVar($1)]; }
;
%%

View File

@ -1,17 +1,10 @@
<?php
$grammarFileToName = [
__DIR__ . '/php5.y' => 'Php5',
__DIR__ . '/php7.y' => 'Php7',
];
$tokensFile = __DIR__ . '/tokens.y';
$tokensTemplate = __DIR__ . '/tokens.template';
$skeletonFile = __DIR__ . '/parser.template';
$grammarFile = __DIR__ . '/zend_language_parser.phpy';
$skeletonFile = __DIR__ . '/kmyacc.php.parser';
$tmpGrammarFile = __DIR__ . '/tmp_parser.phpy';
$tmpResultFile = __DIR__ . '/tmp_parser.php';
$resultDir = __DIR__ . '/../lib/PhpParser/Parser';
$tokensResultsFile = $resultDir . '/Tokens.php';
$parserResultFile = __DIR__ . '/../lib/PhpParser/Parser.php';
// check for kmyacc.exe binary in this directory, otherwise fall back to global name
$kmyacc = __DIR__ . '/kmyacc.exe';
@ -42,42 +35,33 @@ const ARGS = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
/// Main script ///
///////////////////
$tokens = file_get_contents($tokensFile);
foreach ($grammarFileToName as $grammarFile => $name) {
echo "Building temporary $name grammar file.\n";
echo 'Building temporary preproprocessed grammar file.', "\n";
$grammarCode = file_get_contents($grammarFile);
$grammarCode = str_replace('%tokens', $tokens, $grammarCode);
$grammarCode = resolveNodes($grammarCode);
$grammarCode = resolveMacros($grammarCode);
$grammarCode = resolveArrays($grammarCode);
$grammarCode = resolveStackAccess($grammarCode);
file_put_contents($tmpGrammarFile, $grammarCode);
$additionalArgs = $optionDebug ? '-t -v' : '';
echo "Building $name parser.\n";
$output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile -p $name $tmpGrammarFile 2>&1"));
echo "Building parser.\n";
$output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile $tmpGrammarFile 2>&1"));
echo "Output: \"$output\"\n";
$resultCode = file_get_contents($tmpResultFile);
$resultCode = removeTrailingWhitespace($resultCode);
ensureDirExists($resultDir);
file_put_contents("$resultDir/$name.php", $resultCode);
ensureDirExists(dirname($parserResultFile));
file_put_contents($parserResultFile, $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 ///
@ -137,8 +121,7 @@ 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) {
@ -154,19 +137,15 @@ function resolveMacros($code) {
}
if ('parseEncapsed' == $name) {
assertArgs(3, $args, $name);
assertArgs(2, $args, $name);
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = Node\Scalar\String_::parseEscapeSequences($s, ' . $args[1] . '); } }';
}
if ('parseEncapsedDoc' == $name) {
assertArgs(2, $args, $name);
assertArgs(1, $args, $name);
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, ' . $args[1] . '); } }'
. ' $s->value = preg_replace(\'~(\r\n|\n|\r)\z~\', \'\', $s->value);'
. ' if (\'\' === $s->value) array_pop(' . $args[0] . ');';
return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = Node\Scalar\String_::parseEscapeSequences($s, null); } } $s = preg_replace(\'~(\r\n|\n|\r)\z~\', \'\', $s); if (\'\' === $s) array_pop(' . $args[0] . ');';
}
return $matches[0];
@ -181,6 +160,37 @@ 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);
@ -214,9 +224,5 @@ function magicSplit($regex, $string) {
$piece = trim($piece);
}
if ($pieces === ['']) {
return [];
}
return $pieces;
return array_filter($pieces);
}

View File

@ -1,17 +0,0 @@
<?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
}

View File

@ -1,113 +0,0 @@
/* 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

View File

@ -1,7 +1,122 @@
%pure_parser
%expect 6
%expect 2
%tokens
%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
%left ','
%left T_LOGICAL_OR
%left T_LOGICAL_XOR
%left T_LOGICAL_AND
%right T_PRINT
%right T_YIELD
%right T_YIELD_FROM
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL
%left '?' ':'
%right T_COALESCE
%left T_BOOLEAN_OR
%left T_BOOLEAN_AND
%left '|'
%left '^'
%left '&'
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
%left T_SL T_SR
%left '+' '-' '.'
%left '*' '/' '%'
%right '!'
%nonassoc T_INSTANCEOF
%right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
%right T_POW
%right '['
%nonassoc T_NEW T_CLONE
%token T_EXIT
%token T_IF
%left T_ELSEIF
%left T_ELSE
%left T_ENDIF
%token T_LNUMBER
%token T_DNUMBER
%token T_STRING
%token T_STRING_VARNAME
%token T_VARIABLE
%token T_NUM_STRING
%token T_INLINE_HTML
%token T_CHARACTER
%token T_BAD_CHARACTER
%token T_ENCAPSED_AND_WHITESPACE
%token T_CONSTANT_ENCAPSED_STRING
%token T_ECHO
%token T_DO
%token T_WHILE
%token T_ENDWHILE
%token T_FOR
%token T_ENDFOR
%token T_FOREACH
%token T_ENDFOREACH
%token T_DECLARE
%token T_ENDDECLARE
%token T_AS
%token T_SWITCH
%token T_ENDSWITCH
%token T_CASE
%token T_DEFAULT
%token T_BREAK
%token T_CONTINUE
%token T_GOTO
%token T_FUNCTION
%token T_CONST
%token T_RETURN
%token T_TRY
%token T_CATCH
%token T_FINALLY
%token T_THROW
%token T_USE
%token T_INSTEADOF
%token T_GLOBAL
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
%token T_VAR
%token T_UNSET
%token T_ISSET
%token T_EMPTY
%token T_HALT_COMPILER
%token T_CLASS
%token T_TRAIT
%token T_INTERFACE
%token T_EXTENDS
%token T_IMPLEMENTS
%token T_OBJECT_OPERATOR
%token T_DOUBLE_ARROW
%token T_LIST
%token T_ARRAY
%token T_CALLABLE
%token T_CLASS_C
%token T_TRAIT_C
%token T_METHOD_C
%token T_FUNC_C
%token T_LINE
%token T_FILE
%token T_COMMENT
%token T_DOC_COMMENT
%token T_OPEN_TAG
%token T_OPEN_TAG_WITH_ECHO
%token T_CLOSE_TAG
%token T_WHITESPACE
%token T_START_HEREDOC
%token T_END_HEREDOC
%token T_DOLLAR_OPEN_CURLY_BRACES
%token T_CURLY_OPEN
%token T_PAAMAYIM_NEKUDOTAYIM
%token T_NAMESPACE
%token T_NS_C
%token T_DIR
%token T_NS_SEPARATOR
%token T_ELLIPSIS
%{
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
%}
%%
@ -14,25 +129,6 @@ 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); }
@ -52,57 +148,21 @@ 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 use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; }
| group_use_declaration ';' { $$ = $1; }
| 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_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; }
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]; }
;
constant_declaration_list:
@ -114,15 +174,6 @@ constant_declaration:
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:
inner_statement_list inner_statement { pushNormalizing($1, $2); }
| /* empty */ { init(); }
@ -139,13 +190,13 @@ inner_statement:
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]; }
@ -161,9 +212,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
@ -206,14 +257,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]; }
;
@ -407,9 +458,9 @@ class_statement_list:
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_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_USE name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; }
;
@ -426,22 +477,20 @@ 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 identifier ';'
| trait_method_reference T_AS member_modifier T_STRING ';'
{ $$ = 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); }
name T_PAAMAYIM_NEKUDOTAYIM T_STRING { $$ = array($1, $3); }
;
trait_method_reference:
trait_method_reference_fully_qualified { $$ = $1; }
| identifier { $$ = array(null, $1); }
| T_STRING { $$ = array(null, $1); }
;
method_body:
@ -579,10 +628,10 @@ expr:
| T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; }
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
'{' inner_statement_list '}'
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $9]]; }
{ $$ = Expr\Closure[[static: false, byRef: $2, params: $4, uses: $6, returnType: $7, stmts: $9]]; }
| T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
'{' inner_statement_list '}'
{ $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $10]]; }
{ $$ = Expr\Closure[[static: true, byRef: $3, params: $5, uses: $7, returnType: $8, stmts: $10]]; }
;
parentheses_expr:
@ -603,7 +652,7 @@ 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, false)], $3]; }
{ $$ = Expr\ArrayDimFetch[Scalar\String_[Scalar\String_::parse($1)], $3]; }
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
/* alternative array syntax missing intentionally */
@ -611,7 +660,7 @@ scalar_dereference:
anonymous_class:
T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); }
{ $$ = array(Stmt\Class_[null, [type: 0, extends: $3, implements: $4, stmts: $6]], $2); }
new_expr:
T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; }
@ -635,7 +684,7 @@ lexical_var:
function_call:
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier argument_list
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM T_STRING argument_list
{ $$ = Expr\StaticCall[$1, $3, $4]; }
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list
{ $$ = Expr\StaticCall[$1, $4, $6]; }
@ -703,9 +752,8 @@ exit_expr:
backticks_expr:
/* empty */ { $$ = array(); }
| T_ENCAPSED_AND_WHITESPACE
{ $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`', false)]); }
| encaps_list { parseEncapsed($1, '`', false); $$ = $1; }
| T_ENCAPSED_AND_WHITESPACE { $$ = array(Scalar\String_::parseEscapeSequences($1, '`')); }
| encaps_list { parseEncapsed($1, '`'); $$ = $1; }
;
ctor_arguments:
@ -716,7 +764,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, false)]; }
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_[Scalar\String_::parse($1)]; }
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
| T_FILE { $$ = Scalar\MagicConst\File[]; }
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
@ -726,14 +774,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, false)]; }
{ $$ = Scalar\String_[Scalar\String_::parseDocString($1, $2)]; }
| T_START_HEREDOC T_END_HEREDOC
{ $$ = Scalar\String_['']; }
;
static_scalar:
common_scalar { $$ = $1; }
| class_name T_PAAMAYIM_NEKUDOTAYIM identifier { $$ = Expr\ClassConstFetch[$1, $3]; }
| class_name T_PAAMAYIM_NEKUDOTAYIM class_const_name { $$ = Expr\ClassConstFetch[$1, $3]; }
| name { $$ = Expr\ConstFetch[$1]; }
| T_ARRAY '(' static_array_pair_list ')' { $$ = Expr\Array_[$3]; }
| '[' static_array_pair_list ']' { $$ = Expr\Array_[$2]; }
@ -778,7 +826,7 @@ static_operation:
constant:
name { $$ = Expr\ConstFetch[$1]; }
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM class_const_name
{ $$ = Expr\ClassConstFetch[$1, $3]; }
;
@ -786,9 +834,14 @@ scalar:
common_scalar { $$ = $1; }
| constant { $$ = $1; }
| '"' encaps_list '"'
{ parseEncapsed($2, '"', false); $$ = Scalar\Encapsed[$2]; }
{ parseEncapsed($2, '"'); $$ = Scalar\Encapsed[$2]; }
| T_START_HEREDOC encaps_list T_END_HEREDOC
{ parseEncapsedDoc($2, false); $$ = Scalar\Encapsed[$2]; }
{ parseEncapsedDoc($2); $$ = Scalar\Encapsed[$2]; }
;
class_const_name:
T_STRING { $$ = $1; }
| T_CLASS { $$ = 'class'; }
;
static_array_pair_list:
@ -916,13 +969,9 @@ array_pair:
encaps_list:
encaps_list encaps_var { push($1, $2); }
| encaps_list encaps_string_part { push($1, $2); }
| encaps_list T_ENCAPSED_AND_WHITESPACE { push($1, $2); }
| encaps_var { init($1); }
| encaps_string_part encaps_var { init($1, $2); }
;
encaps_string_part:
T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart[$1]; }
| T_ENCAPSED_AND_WHITESPACE encaps_var { init($1, $2); }
;
encaps_var:

View File

@ -10,6 +10,9 @@ 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.
*
@ -22,6 +25,7 @@ class Autoloader
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
self::$registered = true;
self::$runningOnPhp7 = version_compare(PHP_VERSION, '7.0-dev', '>=');
}
/**
@ -31,10 +35,241 @@ class Autoloader
*/
static public function autoload($class) {
if (0 === strpos($class, 'PhpParser\\')) {
if (isset(self::$php7AliasesOldToNew[$class])) {
if (self::$runningOnPhp7) {
return;
}
// Load the new class, alias will be registered afterwards
$class = self::$php7AliasesOldToNew[$class];
}
$fileName = dirname(__DIR__) . '/' . strtr($class, '\\', '/') . '.php';
if (file_exists($fileName)) {
require $fileName;
}
if (isset(self::$php7AliasesNewToOld[$class])) {
// New class name was used, register alias for old one, otherwise
// it won't be usable in "instanceof" and other non-autoloading places.
if (!self::$runningOnPhp7) {
class_alias($class, self::$php7AliasesNewToOld[$class]);
}
}
} else if (0 === strpos($class, 'PHPParser_')) {
if (isset(self::$nonNamespacedAliases[$class])) {
// Register all aliases at once to avoid dependency issues
self::registerNonNamespacedAliases();
}
}
}
private static function registerNonNamespacedAliases() {
foreach (self::$nonNamespacedAliases as $old => $new) {
class_alias($new, $old);
}
}
private static $php7AliasesOldToNew = array(
'PhpParser\Node\Expr\Cast\Bool' => 'PhpParser\Node\Expr\Cast\Bool_',
'PhpParser\Node\Expr\Cast\Int' => 'PhpParser\Node\Expr\Cast\Int_',
'PhpParser\Node\Expr\Cast\Object' => 'PhpParser\Node\Expr\Cast\Object_',
'PhpParser\Node\Expr\Cast\String' => 'PhpParser\Node\Expr\Cast\String_',
'PhpParser\Node\Scalar\String' => 'PhpParser\Node\Scalar\String_',
);
private static $php7AliasesNewToOld = array(
'PhpParser\Node\Expr\Cast\Bool_' => 'PhpParser\Node\Expr\Cast\Bool',
'PhpParser\Node\Expr\Cast\Int_' => 'PhpParser\Node\Expr\Cast\Int',
'PhpParser\Node\Expr\Cast\Object_' => 'PhpParser\Node\Expr\Cast\Object',
'PhpParser\Node\Expr\Cast\String_' => 'PhpParser\Node\Expr\Cast\String',
'PhpParser\Node\Scalar\String_' => 'PhpParser\Node\Scalar\String',
);
private static $nonNamespacedAliases = array(
'PHPParser_Builder' => 'PhpParser\Builder',
'PHPParser_BuilderAbstract' => 'PhpParser\BuilderAbstract',
'PHPParser_BuilderFactory' => 'PhpParser\BuilderFactory',
'PHPParser_Comment' => 'PhpParser\Comment',
'PHPParser_Comment_Doc' => 'PhpParser\Comment\Doc',
'PHPParser_Error' => 'PhpParser\Error',
'PHPParser_Lexer' => 'PhpParser\Lexer',
'PHPParser_Lexer_Emulative' => 'PhpParser\Lexer\Emulative',
'PHPParser_Node' => 'PhpParser\Node',
'PHPParser_NodeAbstract' => 'PhpParser\NodeAbstract',
'PHPParser_NodeDumper' => 'PhpParser\NodeDumper',
'PHPParser_NodeTraverser' => 'PhpParser\NodeTraverser',
'PHPParser_NodeTraverserInterface' => 'PhpParser\NodeTraverserInterface',
'PHPParser_NodeVisitor' => 'PhpParser\NodeVisitor',
'PHPParser_NodeVisitor_NameResolver' => 'PhpParser\NodeVisitor\NameResolver',
'PHPParser_NodeVisitorAbstract' => 'PhpParser\NodeVisitorAbstract',
'PHPParser_Parser' => 'PhpParser\Parser',
'PHPParser_PrettyPrinterAbstract' => 'PhpParser\PrettyPrinterAbstract',
'PHPParser_PrettyPrinter_Default' => 'PhpParser\PrettyPrinter\Standard',
'PHPParser_PrettyPrinter_Zend' => 'PhpParser\PrettyPrinter\Standard',
'PHPParser_Serializer' => 'PhpParser\Serializer',
'PHPParser_Serializer_XML' => 'PhpParser\Serializer\XML',
'PHPParser_Unserializer' => 'PhpParser\Unserializer',
'PHPParser_Unserializer_XML' => 'PhpParser\Unserializer\XML',
'PHPParser_Builder_Class' => 'PhpParser\Builder\Class_',
'PHPParser_Builder_Function' => 'PhpParser\Builder\Function_',
'PHPParser_Builder_Interface' => 'PhpParser\Builder\Interface_',
'PHPParser_Builder_Method' => 'PhpParser\Builder\Method',
'PHPParser_Builder_Param' => 'PhpParser\Builder\Param',
'PHPParser_Builder_Property' => 'PhpParser\Builder\Property',
'PHPParser_Node_Arg' => 'PhpParser\Node\Arg',
'PHPParser_Node_Const' => 'PhpParser\Node\Const_',
'PHPParser_Node_Expr' => 'PhpParser\Node\Expr',
'PHPParser_Node_Name' => 'PhpParser\Node\Name',
'PHPParser_Node_Name_FullyQualified' => 'PhpParser\Node\Name\FullyQualified',
'PHPParser_Node_Name_Relative' => 'PhpParser\Node\Name\Relative',
'PHPParser_Node_Param' => 'PhpParser\Node\Param',
'PHPParser_Node_Scalar' => 'PhpParser\Node\Scalar',
'PHPParser_Node_Stmt' => 'PhpParser\Node\Stmt',
'PHPParser_Node_Stmt_Break' => 'PhpParser\Node\Stmt\Break_',
'PHPParser_Node_Stmt_Case' => 'PhpParser\Node\Stmt\Case_',
'PHPParser_Node_Stmt_Catch' => 'PhpParser\Node\Stmt\Catch_',
'PHPParser_Node_Stmt_Class' => 'PhpParser\Node\Stmt\Class_',
'PHPParser_Node_Stmt_ClassConst' => 'PhpParser\Node\Stmt\ClassConst',
'PHPParser_Node_Stmt_ClassMethod' => 'PhpParser\Node\Stmt\ClassMethod',
'PHPParser_Node_Stmt_Const' => 'PhpParser\Node\Stmt\Const_',
'PHPParser_Node_Stmt_Continue' => 'PhpParser\Node\Stmt\Continue_',
'PHPParser_Node_Stmt_Declare' => 'PhpParser\Node\Stmt\Declare_',
'PHPParser_Node_Stmt_DeclareDeclare' => 'PhpParser\Node\Stmt\DeclareDeclare',
'PHPParser_Node_Stmt_Do' => 'PhpParser\Node\Stmt\Do_',
'PHPParser_Node_Stmt_Echo' => 'PhpParser\Node\Stmt\Echo_',
'PHPParser_Node_Stmt_Else' => 'PhpParser\Node\Stmt\Else_',
'PHPParser_Node_Stmt_ElseIf' => 'PhpParser\Node\Stmt\ElseIf_',
'PHPParser_Node_Stmt_For' => 'PhpParser\Node\Stmt\For_',
'PHPParser_Node_Stmt_Foreach' => 'PhpParser\Node\Stmt\Foreach_',
'PHPParser_Node_Stmt_Function' => 'PhpParser\Node\Stmt\Function_',
'PHPParser_Node_Stmt_Global' => 'PhpParser\Node\Stmt\Global_',
'PHPParser_Node_Stmt_Goto' => 'PhpParser\Node\Stmt\Goto_',
'PHPParser_Node_Stmt_HaltCompiler' => 'PhpParser\Node\Stmt\HaltCompiler',
'PHPParser_Node_Stmt_If' => 'PhpParser\Node\Stmt\If_',
'PHPParser_Node_Stmt_InlineHTML' => 'PhpParser\Node\Stmt\InlineHTML',
'PHPParser_Node_Stmt_Interface' => 'PhpParser\Node\Stmt\Interface_',
'PHPParser_Node_Stmt_Label' => 'PhpParser\Node\Stmt\Label',
'PHPParser_Node_Stmt_Namespace' => 'PhpParser\Node\Stmt\Namespace_',
'PHPParser_Node_Stmt_Property' => 'PhpParser\Node\Stmt\Property',
'PHPParser_Node_Stmt_PropertyProperty' => 'PhpParser\Node\Stmt\PropertyProperty',
'PHPParser_Node_Stmt_Return' => 'PhpParser\Node\Stmt\Return_',
'PHPParser_Node_Stmt_Static' => 'PhpParser\Node\Stmt\Static_',
'PHPParser_Node_Stmt_StaticVar' => 'PhpParser\Node\Stmt\StaticVar',
'PHPParser_Node_Stmt_Switch' => 'PhpParser\Node\Stmt\Switch_',
'PHPParser_Node_Stmt_Throw' => 'PhpParser\Node\Stmt\Throw_',
'PHPParser_Node_Stmt_Trait' => 'PhpParser\Node\Stmt\Trait_',
'PHPParser_Node_Stmt_TraitUse' => 'PhpParser\Node\Stmt\TraitUse',
'PHPParser_Node_Stmt_TraitUseAdaptation' => 'PhpParser\Node\Stmt\TraitUseAdaptation',
'PHPParser_Node_Stmt_TraitUseAdaptation_Alias' => 'PhpParser\Node\Stmt\TraitUseAdaptation\Alias',
'PHPParser_Node_Stmt_TraitUseAdaptation_Precedence' => 'PhpParser\Node\Stmt\TraitUseAdaptation\Precedence',
'PHPParser_Node_Stmt_TryCatch' => 'PhpParser\Node\Stmt\TryCatch',
'PHPParser_Node_Stmt_Unset' => 'PhpParser\Node\Stmt\Unset_',
'PHPParser_Node_Stmt_UseUse' => 'PhpParser\Node\Stmt\UseUse',
'PHPParser_Node_Stmt_Use' => 'PhpParser\Node\Stmt\Use_',
'PHPParser_Node_Stmt_While' => 'PhpParser\Node\Stmt\While_',
'PHPParser_Node_Expr_AssignBitwiseAnd' => 'PhpParser\Node\Expr\AssignOp\BitwiseAnd',
'PHPParser_Node_Expr_AssignBitwiseOr' => 'PhpParser\Node\Expr\AssignOp\BitwiseOr',
'PHPParser_Node_Expr_AssignBitwiseXor' => 'PhpParser\Node\Expr\AssignOp\BitwiseXor',
'PHPParser_Node_Expr_AssignConcat' => 'PhpParser\Node\Expr\AssignOp\Concat',
'PHPParser_Node_Expr_AssignDiv' => 'PhpParser\Node\Expr\AssignOp\Div',
'PHPParser_Node_Expr_AssignMinus' => 'PhpParser\Node\Expr\AssignOp\Minus',
'PHPParser_Node_Expr_AssignMod' => 'PhpParser\Node\Expr\AssignOp\Mod',
'PHPParser_Node_Expr_AssignMul' => 'PhpParser\Node\Expr\AssignOp\Mul',
'PHPParser_Node_Expr_AssignPlus' => 'PhpParser\Node\Expr\AssignOp\Plus',
'PHPParser_Node_Expr_AssignShiftLeft' => 'PhpParser\Node\Expr\AssignOp\ShiftLeft',
'PHPParser_Node_Expr_AssignShiftRight' => 'PhpParser\Node\Expr\AssignOp\ShiftRight',
'PHPParser_Node_Expr_Cast' => 'PhpParser\Node\Expr\Cast',
'PHPParser_Node_Expr_Cast_Array' => 'PhpParser\Node\Expr\Cast\Array_',
'PHPParser_Node_Expr_Cast_Bool' => 'PhpParser\Node\Expr\Cast\Bool_',
'PHPParser_Node_Expr_Cast_Double' => 'PhpParser\Node\Expr\Cast\Double',
'PHPParser_Node_Expr_Cast_Int' => 'PhpParser\Node\Expr\Cast\Int_',
'PHPParser_Node_Expr_Cast_Object' => 'PhpParser\Node\Expr\Cast\Object_',
'PHPParser_Node_Expr_Cast_String' => 'PhpParser\Node\Expr\Cast\String_',
'PHPParser_Node_Expr_Cast_Unset' => 'PhpParser\Node\Expr\Cast\Unset_',
'PHPParser_Node_Expr_BitwiseAnd' => 'PhpParser\Node\Expr\BinaryOp\BitwiseAnd',
'PHPParser_Node_Expr_BitwiseOr' => 'PhpParser\Node\Expr\BinaryOp\BitwiseOr',
'PHPParser_Node_Expr_BitwiseXor' => 'PhpParser\Node\Expr\BinaryOp\BitwiseXor',
'PHPParser_Node_Expr_BooleanAnd' => 'PhpParser\Node\Expr\BinaryOp\BooleanAnd',
'PHPParser_Node_Expr_BooleanOr' => 'PhpParser\Node\Expr\BinaryOp\BooleanOr',
'PHPParser_Node_Expr_Concat' => 'PhpParser\Node\Expr\BinaryOp\Concat',
'PHPParser_Node_Expr_Div' => 'PhpParser\Node\Expr\BinaryOp\Div',
'PHPParser_Node_Expr_Equal' => 'PhpParser\Node\Expr\BinaryOp\Equal',
'PHPParser_Node_Expr_Greater' => 'PhpParser\Node\Expr\BinaryOp\Greater',
'PHPParser_Node_Expr_GreaterOrEqual' => 'PhpParser\Node\Expr\BinaryOp\GreaterOrEqual',
'PHPParser_Node_Expr_Identical' => 'PhpParser\Node\Expr\BinaryOp\Identical',
'PHPParser_Node_Expr_LogicalAnd' => 'PhpParser\Node\Expr\BinaryOp\LogicalAnd',
'PHPParser_Node_Expr_LogicalOr' => 'PhpParser\Node\Expr\BinaryOp\LogicalOr',
'PHPParser_Node_Expr_LogicalXor' => 'PhpParser\Node\Expr\BinaryOp\LogicalXor',
'PHPParser_Node_Expr_Minus' => 'PhpParser\Node\Expr\BinaryOp\Minus',
'PHPParser_Node_Expr_Mod' => 'PhpParser\Node\Expr\BinaryOp\Mod',
'PHPParser_Node_Expr_Mul' => 'PhpParser\Node\Expr\BinaryOp\Mul',
'PHPParser_Node_Expr_NotEqual' => 'PhpParser\Node\Expr\BinaryOp\NotEqual',
'PHPParser_Node_Expr_NotIdentical' => 'PhpParser\Node\Expr\BinaryOp\NotIdentical',
'PHPParser_Node_Expr_Plus' => 'PhpParser\Node\Expr\BinaryOp\Plus',
'PHPParser_Node_Expr_ShiftLeft' => 'PhpParser\Node\Expr\BinaryOp\ShiftLeft',
'PHPParser_Node_Expr_ShiftRight' => 'PhpParser\Node\Expr\BinaryOp\ShiftRight',
'PHPParser_Node_Expr_Smaller' => 'PhpParser\Node\Expr\BinaryOp\Smaller',
'PHPParser_Node_Expr_SmallerOrEqual' => 'PhpParser\Node\Expr\BinaryOp\SmallerOrEqual',
'PHPParser_Node_Expr_Array' => 'PhpParser\Node\Expr\Array_',
'PHPParser_Node_Expr_ArrayDimFetch' => 'PhpParser\Node\Expr\ArrayDimFetch',
'PHPParser_Node_Expr_ArrayItem' => 'PhpParser\Node\Expr\ArrayItem',
'PHPParser_Node_Expr_Assign' => 'PhpParser\Node\Expr\Assign',
'PHPParser_Node_Expr_AssignRef' => 'PhpParser\Node\Expr\AssignRef',
'PHPParser_Node_Expr_BitwiseNot' => 'PhpParser\Node\Expr\BitwiseNot',
'PHPParser_Node_Expr_BooleanNot' => 'PhpParser\Node\Expr\BooleanNot',
'PHPParser_Node_Expr_ClassConstFetch' => 'PhpParser\Node\Expr\ClassConstFetch',
'PHPParser_Node_Expr_Clone' => 'PhpParser\Node\Expr\Clone_',
'PHPParser_Node_Expr_Closure' => 'PhpParser\Node\Expr\Closure',
'PHPParser_Node_Expr_ClosureUse' => 'PhpParser\Node\Expr\ClosureUse',
'PHPParser_Node_Expr_ConstFetch' => 'PhpParser\Node\Expr\ConstFetch',
'PHPParser_Node_Expr_Empty' => 'PhpParser\Node\Expr\Empty_',
'PHPParser_Node_Expr_ErrorSuppress' => 'PhpParser\Node\Expr\ErrorSuppress',
'PHPParser_Node_Expr_Eval' => 'PhpParser\Node\Expr\Eval_',
'PHPParser_Node_Expr_Exit' => 'PhpParser\Node\Expr\Exit_',
'PHPParser_Node_Expr_FuncCall' => 'PhpParser\Node\Expr\FuncCall',
'PHPParser_Node_Expr_Include' => 'PhpParser\Node\Expr\Include_',
'PHPParser_Node_Expr_Instanceof' => 'PhpParser\Node\Expr\Instanceof_',
'PHPParser_Node_Expr_Isset' => 'PhpParser\Node\Expr\Isset_',
'PHPParser_Node_Expr_List' => 'PhpParser\Node\Expr\List_',
'PHPParser_Node_Expr_MethodCall' => 'PhpParser\Node\Expr\MethodCall',
'PHPParser_Node_Expr_New' => 'PhpParser\Node\Expr\New_',
'PHPParser_Node_Expr_PostDec' => 'PhpParser\Node\Expr\PostDec',
'PHPParser_Node_Expr_PostInc' => 'PhpParser\Node\Expr\PostInc',
'PHPParser_Node_Expr_PreDec' => 'PhpParser\Node\Expr\PreDec',
'PHPParser_Node_Expr_PreInc' => 'PhpParser\Node\Expr\PreInc',
'PHPParser_Node_Expr_Print' => 'PhpParser\Node\Expr\Print_',
'PHPParser_Node_Expr_PropertyFetch' => 'PhpParser\Node\Expr\PropertyFetch',
'PHPParser_Node_Expr_ShellExec' => 'PhpParser\Node\Expr\ShellExec',
'PHPParser_Node_Expr_StaticCall' => 'PhpParser\Node\Expr\StaticCall',
'PHPParser_Node_Expr_StaticPropertyFetch' => 'PhpParser\Node\Expr\StaticPropertyFetch',
'PHPParser_Node_Expr_Ternary' => 'PhpParser\Node\Expr\Ternary',
'PHPParser_Node_Expr_UnaryMinus' => 'PhpParser\Node\Expr\UnaryMinus',
'PHPParser_Node_Expr_UnaryPlus' => 'PhpParser\Node\Expr\UnaryPlus',
'PHPParser_Node_Expr_Variable' => 'PhpParser\Node\Expr\Variable',
'PHPParser_Node_Expr_Yield' => 'PhpParser\Node\Expr\Yield_',
'PHPParser_Node_Scalar_ClassConst' => 'PhpParser\Node\Scalar\MagicConst\Class_',
'PHPParser_Node_Scalar_DirConst' => 'PhpParser\Node\Scalar\MagicConst\Dir',
'PHPParser_Node_Scalar_FileConst' => 'PhpParser\Node\Scalar\MagicConst\File',
'PHPParser_Node_Scalar_FuncConst' => 'PhpParser\Node\Scalar\MagicConst\Function_',
'PHPParser_Node_Scalar_LineConst' => 'PhpParser\Node\Scalar\MagicConst\Line',
'PHPParser_Node_Scalar_MethodConst' => 'PhpParser\Node\Scalar\MagicConst\Method',
'PHPParser_Node_Scalar_NSConst' => 'PhpParser\Node\Scalar\MagicConst\Namespace_',
'PHPParser_Node_Scalar_TraitConst' => 'PhpParser\Node\Scalar\MagicConst\Trait_',
'PHPParser_Node_Scalar_DNumber' => 'PhpParser\Node\Scalar\DNumber',
'PHPParser_Node_Scalar_Encapsed' => 'PhpParser\Node\Scalar\Encapsed',
'PHPParser_Node_Scalar_LNumber' => 'PhpParser\Node\Scalar\LNumber',
'PHPParser_Node_Scalar_String' => 'PhpParser\Node\Scalar\String_',
);
}
class_alias('PhpParser\Autoloader', 'PHPParser_Autoloader');

View File

@ -2,8 +2,6 @@
namespace PhpParser;
use PhpParser\Parser\Tokens;
class Lexer
{
protected $code;
@ -67,21 +65,14 @@ class Lexer
}
protected function resetErrors() {
if (function_exists('error_clear_last')) {
error_clear_last();
} else {
// set error_get_last() to defined state by forcing an undefined variable error
set_error_handler(function() { return false; }, 0);
@$undefinedVariable;
restore_error_handler();
}
}
protected function handleErrors() {
$error = error_get_last();
if (null === $error) {
return;
}
if (preg_match(
'~^Unterminated comment starting line ([0-9]+)$~',
@ -263,18 +254,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] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
$tokenMap[$i] = Parser::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] = Tokens::T_ECHO;
$tokenMap[$i] = Parser::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] = Tokens::T_INLINE_HTML;
} else if (defined($name = 'PhpParser\Parser\Tokens::' . $name)) {
$tokenMap[$i] = Parser::T_INLINE_HTML;
} else if (defined($name = 'PhpParser\Parser::' . $name)) {
// Other tokens can be mapped directly
$tokenMap[$i] = constant($name);
}
@ -283,11 +274,11 @@ class Lexer
// HHVM uses a special token for numbers that overflow to double
if (defined('T_ONUMBER')) {
$tokenMap[T_ONUMBER] = Tokens::T_DNUMBER;
$tokenMap[T_ONUMBER] = Parser::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;
$tokenMap[T_COMPILER_HALT_OFFSET] = Parser::T_STRING;
}
return $tokenMap;

View File

@ -2,7 +2,7 @@
namespace PhpParser\Lexer;
use PhpParser\Parser\Tokens;
use PhpParser\Parser;
/**
* ATTENTION: This code is WRITE-ONLY. Do not try to read it.
@ -22,14 +22,21 @@ class Emulative extends \PhpParser\Lexer
const PHP_7_0 = '7.0.0dev';
const PHP_5_6 = '5.6.0rc1';
const PHP_5_5 = '5.5.0beta1';
const PHP_5_4 = '5.4.0beta1';
public function __construct(array $options = array()) {
parent::__construct($options);
$newKeywordsPerVersion = array(
self::PHP_5_5 => array(
'finally' => Tokens::T_FINALLY,
'yield' => Tokens::T_YIELD,
'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,
),
);
@ -45,16 +52,16 @@ class Emulative extends \PhpParser\Lexer
if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
return;
}
$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;
$this->tokenMap[self::T_COALESCE] = Parser::T_COALESCE;
$this->tokenMap[self::T_SPACESHIP] = Parser::T_SPACESHIP;
$this->tokenMap[self::T_YIELD_FROM] = Parser::T_YIELD_FROM;
if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
return;
}
$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;
$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;
}
public function startLexing($code) {
@ -99,9 +106,14 @@ 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);
}
/*
* Replaces the ~__EMU__...~ sequences with real tokens or their original
* value.
@ -118,7 +130,14 @@ 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 ('ELLIPSIS' === $matches[1]) {
if ('BINARY' === $matches[1]) {
// the binary number can either be an integer or a double, so return a LNUMBER
// or DNUMBER respectively
$isInt = is_int(bindec($matches[2]));
$replace = array(
array($isInt ? T_LNUMBER : T_DNUMBER, $matches[2], $this->tokens[$i + 1][2])
);
} else if ('ELLIPSIS' === $matches[1]) {
$replace = array(
array(self::T_ELLIPSIS, '...', $this->tokens[$i + 1][2])
);
@ -139,7 +158,7 @@ class Emulative extends \PhpParser\Lexer
array(self::T_SPACESHIP, '<=>', $this->tokens[$i + 1][2]),
);
} else if ('YIELDFROM' === $matches[1]) {
$content = hex2bin($matches[2]);
$content = $this->hex2bin($matches[2]);
$replace = array(
array(self::T_YIELD_FROM, $content, $this->tokens[$i + 1][2] - substr_count($content, "\n"))
);
@ -168,7 +187,9 @@ class Emulative extends \PhpParser\Lexer
* multichar tokens (like strings) to their original value.
*/
public function restoreContentCallback(array $matches) {
if ('ELLIPSIS' === $matches[1]) {
if ('BINARY' === $matches[1]) {
return $matches[2];
} else if ('ELLIPSIS' === $matches[1]) {
return '...';
} else if ('POW' === $matches[1]) {
return '**';
@ -179,25 +200,30 @@ class Emulative extends \PhpParser\Lexer
} else if ('SPACESHIP' === $matches[1]) {
return '<=>';
} else if ('YIELDFROM' === $matches[1]) {
return hex2bin($matches[2]);
return $this->hex2bin($matches[2]);
} else {
return $matches[0];
}
}
private function hex2bin($str) {
// TODO Drop when removing support for PHP 5.3
return pack('H*', $str);
}
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
$token = parent::getNextToken($value, $startAttributes, $endAttributes);
// replace new keywords by their respective tokens. This is not done
// if we currently are in an object access (e.g. in $obj->namespace
// "namespace" stays a T_STRING tokens and isn't converted to T_NAMESPACE)
if (Tokens::T_STRING === $token && !$this->inObjectAccess) {
if (Parser::T_STRING === $token && !$this->inObjectAccess) {
if (isset($this->newKeywords[strtolower($value)])) {
return $this->newKeywords[strtolower($value)];
}
} else {
// keep track of whether we currently are in an object access (after ->)
$this->inObjectAccess = Tokens::T_OBJECT_OPERATOR === $token;
$this->inObjectAccess = Parser::T_OBJECT_OPERATOR === $token;
}
return $token;

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->value = $value;
$this->byRef = $byRef;
$this->unpack = $unpack;

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->var = $var;
$this->dim = $dim;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->key = $key;
$this->value = $value;
$this->byRef = $byRef;

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->var = $var;
$this->expr = $expr;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->var = $var;
$this->expr = $expr;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->var = $var;
$this->expr = $expr;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->left = $left;
$this->right = $right;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ class Closure extends Expr implements FunctionLike
* @param array $attributes Additional attributes
*/
public function __construct(array $subNodes = array(), array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->name = $name;
$this->args = $args;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->var = $var;
$this->name = $name;
$this->args = $args;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->class = $class;
$this->name = $name;
$this->args = $args;

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->cond = $cond;
$this->if = $if;
$this->else = $else;

View File

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

View File

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

View File

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

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->key = $key;
$this->value = $value;
}

View File

@ -20,7 +20,7 @@ class Name extends NodeAbstract
$parts = explode('\\', $parts);
}
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->parts = $parts;
}
@ -106,45 +106,37 @@ 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 = self::prepareName($name);
$this->parts = $this->prepareName($name);
}
/**
* Prepends a name to this name.
*
* @deprecated Use Name::concat($name1, $name2) instead
*
* @param string|array|self $name Name to prepend
*/
public function prepend($name) {
$this->parts = array_merge(self::prepareName($name), $this->parts);
$this->parts = array_merge($this->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, self::prepareName($name));
$this->parts = array_merge($this->parts, $this->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, self::prepareName($name));
array_splice($this->parts, 0, 1, $this->prepareName($name));
}
/**
@ -153,47 +145,7 @@ 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, 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
);
array_splice($this->parts, -1, 1, $this->prepareName($name));
}
/**
@ -204,7 +156,7 @@ class Name extends NodeAbstract
*
* @return array Prepared name
*/
private static function prepareName($name) {
protected function prepareName($name) {
if (is_string($name)) {
return explode('\\', $name);
} elseif (is_array($name)) {

View File

@ -29,7 +29,7 @@ class Param extends NodeAbstract
* @param array $attributes Additional attributes
*/
public function __construct($name, Expr $default = null, $type = null, $byRef = false, $variadic = false, array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->type = $type;
$this->byRef = $byRef;
$this->variadic = $variadic;

View File

@ -15,8 +15,8 @@ class DNumber extends Scalar
* @param float $value Value of the number
* @param array $attributes Additional attributes
*/
public function __construct($value, array $attributes = array()) {
parent::__construct($attributes);
public function __construct($value = 0.0, array $attributes = array()) {
parent::__construct(null, $attributes);
$this->value = $value;
}

View File

@ -15,8 +15,8 @@ class Encapsed extends Scalar
* @param array $parts Encaps list
* @param array $attributes Additional attributes
*/
public function __construct(array $parts, array $attributes = array()) {
parent::__construct($attributes);
public function __construct(array $parts = array(), array $attributes = array()) {
parent::__construct(null, $attributes);
$this->parts = $parts;
}

View File

@ -1,26 +0,0 @@
<?php
namespace PhpParser\Node\Scalar;
use PhpParser\Node\Scalar;
class EncapsedStringPart extends Scalar
{
/** @var string String value */
public $value;
/**
* Constructs a node representing a string part of an encapsed string.
*
* @param string $value String value
* @param array $attributes Additional attributes
*/
public function __construct($value, array $attributes = array()) {
parent::__construct($attributes);
$this->value = $value;
}
public function getSubNodeNames() {
return array('value');
}
}

View File

@ -15,8 +15,8 @@ class LNumber extends Scalar
* @param int $value Value of the number
* @param array $attributes Additional attributes
*/
public function __construct($value, array $attributes = array()) {
parent::__construct($attributes);
public function __construct($value = 0, array $attributes = array()) {
parent::__construct(null, $attributes);
$this->value = $value;
}

View File

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

View File

@ -2,7 +2,6 @@
namespace PhpParser\Node\Scalar;
use PhpParser\Error;
use PhpParser\Node\Scalar;
class String_ extends Scalar
@ -27,8 +26,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($attributes);
public function __construct($value = '', array $attributes = array()) {
parent::__construct(null, $attributes);
$this->value = $value;
}
@ -42,11 +41,10 @@ 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, $parseUnicodeEscape = true) {
public static function parse($str) {
$bLength = 0;
if ('b' === $str[0]) {
$bLength = 1;
@ -59,9 +57,7 @@ class String_ extends Scalar
substr($str, $bLength + 1, -1)
);
} else {
return self::parseEscapeSequences(
substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape
);
return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"');
}
}
@ -72,54 +68,31 @@ 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, $parseUnicodeEscape = true) {
public static function parseEscapeSequences($str, $quote) {
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'),
$str
);
}
return preg_replace_callback(
'~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~',
function($matches) {
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));
} elseif ('u' === $str[0]) {
return self::codePointToUtf8(hexdec($matches[2]));
} else {
return chr(octdec($str));
}
},
$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');
}
/**
@ -129,11 +102,10 @@ 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, $parseUnicodeEscape = true) {
public static function parseDocString($startToken, $str) {
// strip last newline (thanks tokenizer for sticking it into the string!)
$str = preg_replace('~(\r\n|\n|\r)\z~', '', $str);
@ -142,6 +114,6 @@ class String_ extends Scalar
return $str;
}
return self::parseEscapeSequences($str, null, $parseUnicodeEscape);
return self::parseEscapeSequences($str, null);
}
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->num = $num;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->cond = $cond;
$this->stmts = $stmts;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->type = $type;
$this->var = $var;
$this->stmts = $stmts;

View File

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

View File

@ -34,7 +34,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
* @param array $attributes Additional attributes
*/
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->type = isset($subNodes['type']) ? $subNodes['type'] : 0;
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
$this->name = $name;

View File

@ -41,7 +41,7 @@ class Class_ extends ClassLike
* @param array $attributes Additional attributes
*/
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->type = isset($subNodes['type']) ? $subNodes['type'] : 0;
$this->name = $name;
$this->extends = isset($subNodes['extends']) ? $subNodes['extends'] : null;

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->num = $num;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->key = $key;
$this->value = $value;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->declares = $declares;
$this->stmts = $stmts;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->cond = $cond;
$this->stmts = $stmts;
}

View File

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

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->cond = $cond;
$this->stmts = $stmts;
}

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->stmts = $stmts;
}

View File

@ -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($attributes);
parent::__construct(null, $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();

View File

@ -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($attributes);
parent::__construct(null, $attributes);
$this->expr = $expr;
$this->keyVar = isset($subNodes['keyVar']) ? $subNodes['keyVar'] : null;
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;

View File

@ -30,7 +30,7 @@ class Function_ extends Node\Stmt implements FunctionLike
* @param array $attributes Additional attributes
*/
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
$this->name = $name;
$this->params = isset($subNodes['params']) ? $subNodes['params'] : array();

View File

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

View File

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

View File

@ -1,35 +0,0 @@
<?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');
}
}

View File

@ -16,7 +16,7 @@ class HaltCompiler extends Stmt
* @param array $attributes Additional attributes
*/
public function __construct($remaining, array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->remaining = $remaining;
}

View File

@ -26,7 +26,7 @@ class If_ extends Node\Stmt
* @param array $attributes Additional attributes
*/
public function __construct(Node\Expr $cond, array $subNodes = array(), array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->cond = $cond;
$this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
$this->elseifs = isset($subNodes['elseifs']) ? $subNodes['elseifs'] : array();

View File

@ -16,7 +16,7 @@ class InlineHTML extends Stmt
* @param array $attributes Additional attributes
*/
public function __construct($value, array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->value = $value;
}

View File

@ -26,7 +26,7 @@ class Interface_ extends ClassLike
* @param array $attributes Additional attributes
*/
public function __construct($name, array $subNodes = array(), array $attributes = array()) {
parent::__construct($attributes);
parent::__construct(null, $attributes);
$this->name = $name;
$this->extends = isset($subNodes['extends']) ? $subNodes['extends'] : array();
$this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();

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