Compare commits

..

24 Commits

Author SHA1 Message Date
e612609022 Release PHP-Parser 4.2.3 2019-08-12 22:17:41 +02:00
4fd36b9946 Fix compatibility with T_BAD_CHARACTER in PHP 7.4 2019-08-12 22:10:02 +02:00
a1f72690ef Fix harmless typos in documentation 2019-07-23 12:32:37 +02:00
2e2954ccdf Avoid notices in php 7.4 with hexdec/base_convert (#619)
This is made to avoid notices caused by
https://wiki.php.net/rfc/base_convert_improvements

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

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

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

Fixes #589.
2019-05-11 20:01:25 +02:00
b2cecec6bc Remove bogus exprStmt mode test
We're always generating expression statements nowadays, this flag
hasn't existed for a long while now...
2019-05-11 18:51:37 +02:00
8012faea54 [PHP 7.4] Add array spread 2019-05-09 19:15:35 +02:00
f3b19c19ef [PHP 7.4] Add support for arrow functions (#602)
Per RFC https://wiki.php.net/rfc/arrow_functions_v2.
2019-05-09 14:17:28 +02:00
78d9985d11 Print messages to stderr in bin/php-parse and fix exit status
Close #605.
2019-04-28 22:06:06 +02:00
57b8673ea7 Use --ignore-platform-reqs on Travis
Allows testing on nightly, which is PHP 8.
2019-02-16 21:58:22 +01:00
159 changed files with 4603 additions and 2966 deletions

View File

@ -16,7 +16,12 @@ php:
install:
- if [ $TRAVIS_PHP_VERSION = '7.0' ]; then composer require satooshi/php-coveralls '~1.0'; fi
- composer install --prefer-dist
- |
if [ $TRAVIS_PHP_VERSION = 'nightly' ]; then
composer install --prefer-dist --ignore-platform-reqs;
else
composer install --prefer-dist;
fi
matrix:
allow_failures:

View File

@ -1,8 +1,39 @@
Version 4.2.2-dev
Version 4.2.4-dev
-----------------
Nothing yet.
Version 4.2.3 (2019-08-12)
--------------------------
### Added
* [PHP 7.4] Add support for numeric literal separators. (#615)
### Fixed
* Fixed resolution of return types for arrow functions. (#613)
* Fixed compatibility with PHP 7.4.
Version 4.2.2 (2019-05-25)
--------------------------
### Added
* [PHP 7.4] Add support for arrow functions using a new `Expr\ArrowFunction` node. (#602)
* [PHP 7.4] Add support for array spreads, using a new `unpack` subnode on `ArrayItem`. (#609)
* Added support for inserting into empty list nodes in the formatting preserving pretty printer.
### Changed
* `php-parse` will now print messages to stderr, so that stdout only contains the actual result of
the operation (such as a JSON dump). (#605)
### Fixed
* Fixed attribute assignment for zero-length nop statements, and a related assertion failure in
the formatting-preserving pretty printer. (#589)
Version 4.2.1 (2019-02-16)
--------------------------

View File

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

View File

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

View File

@ -17,7 +17,7 @@
"ext-tokenizer": "*"
},
"require-dev": {
"phpunit/phpunit": "^6.5 || ^7.0"
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0"
},
"extra": {
"branch-alias": {

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ top_statement_list_ex:
top_statement_list:
top_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
;
@ -27,7 +27,7 @@ reserved_non_modifiers:
| T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN
;
semi_reserved:
@ -160,7 +160,7 @@ inner_statement_list_ex:
inner_statement_list:
inner_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
;
@ -461,7 +461,7 @@ class_statement_list_ex:
class_statement_list:
class_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
;
@ -988,6 +988,7 @@ array_pair:
| expr { $$ = Expr\ArrayItem[$1, null, false]; }
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
;
encaps_list:

View File

@ -16,7 +16,7 @@ top_statement_list_ex:
top_statement_list:
top_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
;
@ -27,7 +27,7 @@ reserved_non_modifiers:
| T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN
;
semi_reserved:
@ -196,7 +196,7 @@ inner_statement_list_ex:
inner_statement_list:
inner_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
;
@ -530,7 +530,7 @@ class_statement_list_ex:
class_statement_list:
class_statement_list_ex
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
;
@ -729,6 +729,12 @@ expr:
| 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_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
{ $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8]]; }
| T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
{ $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9]]; }
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
block_or_error
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8]]; }
@ -977,6 +983,7 @@ array_pair:
| expr { $$ = Expr\ArrayItem[$1, null, false]; }
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
| /* empty */ { $$ = null; }
;

View File

@ -13,9 +13,12 @@ $tmpResultFile = __DIR__ . '/tmp_parser.php';
$resultDir = __DIR__ . '/../lib/PhpParser/Parser';
$tokensResultsFile = $resultDir . '/Tokens.php';
// check for kmyacc.exe binary in this directory, otherwise fall back to global name
$kmyacc = __DIR__ . '/kmyacc.exe';
if (!file_exists($kmyacc)) {
// check for kmyacc binary in this directory, otherwise fall back to global name
if (file_exists(__DIR__ . '/kmyacc.exe')) {
$kmyacc = __DIR__ . '/kmyacc.exe';
} else if (file_exists(__DIR__ . '/kmyacc')) {
$kmyacc = __DIR__ . '/kmyacc';
} else {
$kmyacc = 'kmyacc';
}
@ -175,6 +178,15 @@ function resolveMacros($code) {
. ' else { ' . $args[0] . ' = null; }';
}
if ('makeZeroLengthNop' == $name) {
assertArgs(2, $args, $name);
return '$startAttributes = ' . $args[1] . ';'
. ' if (isset($startAttributes[\'comments\']))'
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); }'
. ' else { ' . $args[0] . ' = null; }';
}
if ('strKind' == $name) {
assertArgs(1, $args, $name);

View File

@ -64,6 +64,7 @@
%token T_CONTINUE
%token T_GOTO
%token T_FUNCTION
%token T_FN
%token T_CONST
%token T_RETURN
%token T_TRY

View File

@ -16,7 +16,13 @@ class Lexer
protected $tokenMap;
protected $dropTokens;
protected $usedAttributes;
private $attributeStartLineUsed;
private $attributeEndLineUsed;
private $attributeStartTokenPosUsed;
private $attributeEndTokenPosUsed;
private $attributeStartFilePosUsed;
private $attributeEndFilePosUsed;
private $attributeCommentsUsed;
/**
* Creates a Lexer.
@ -31,18 +37,28 @@ class Lexer
// map from internal tokens to PhpParser tokens
$this->tokenMap = $this->createTokenMap();
// Compatibility define for PHP < 7.4
if (!defined('T_BAD_CHARACTER')) {
\define('T_BAD_CHARACTER', -1);
}
// map of tokens to drop while lexing (the map is only used for isset lookup,
// that's why the value is simply set to 1; the value is never actually used.)
$this->dropTokens = array_fill_keys(
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT], 1
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1
);
// the usedAttributes member is a map of the used attribute names to a dummy
// value (here "true")
$options += [
'usedAttributes' => ['comments', 'startLine', 'endLine'],
];
$this->usedAttributes = array_fill_keys($options['usedAttributes'], true);
$defaultAttributes = ['comments', 'startLine', 'endLine'];
$usedAttributes = array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, true);
// Create individual boolean properties to make these checks faster.
$this->attributeStartLineUsed = isset($usedAttributes['startLine']);
$this->attributeEndLineUsed = isset($usedAttributes['endLine']);
$this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']);
$this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']);
$this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']);
$this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']);
$this->attributeCommentsUsed = isset($usedAttributes['comments']);
}
/**
@ -81,13 +97,9 @@ class Lexer
}
private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) {
$tokens = [];
for ($i = $start; $i < $end; $i++) {
$chr = $this->code[$i];
if ($chr === 'b' || $chr === 'B') {
// HHVM does not treat b" tokens correctly, so ignore these
continue;
}
if ($chr === "\0") {
// PHP cuts error message after null byte, so need special case
$errorMsg = 'Unexpected null byte';
@ -97,6 +109,7 @@ class Lexer
);
}
$tokens[] = [\T_BAD_CHARACTER, $chr, $line];
$errorHandler->handleError(new Error($errorMsg, [
'startLine' => $line,
'endLine' => $line,
@ -104,6 +117,7 @@ class Lexer
'endFilePos' => $i,
]));
}
return $tokens;
}
/**
@ -144,16 +158,29 @@ class Lexer
$filePos = 0;
$line = 1;
foreach ($this->tokens as $token) {
$numTokens = \count($this->tokens);
for ($i = 0; $i < $numTokens; $i++) {
$token = $this->tokens[$i];
// Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token.
// In this case we only need to emit an error.
if ($token[0] === \T_BAD_CHARACTER) {
$this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler);
}
$tokenValue = \is_string($token) ? $token : $token[1];
$tokenLen = \strlen($tokenValue);
if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) {
// Something is missing, must be an invalid character
$nextFilePos = strpos($this->code, $tokenValue, $filePos);
$this->handleInvalidCharacterRange(
$badCharTokens = $this->handleInvalidCharacterRange(
$filePos, $nextFilePos, $line, $errorHandler);
$filePos = (int) $nextFilePos;
array_splice($this->tokens, $i, 0, $badCharTokens);
$numTokens += \count($badCharTokens);
$i += \count($badCharTokens);
}
$filePos += $tokenLen;
@ -176,8 +203,9 @@ class Lexer
$this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line];
} else {
// Invalid characters at the end of the input
$this->handleInvalidCharacterRange(
$badCharTokens = $this->handleInvalidCharacterRange(
$filePos, \strlen($this->code), $line, $errorHandler);
$this->tokens = array_merge($this->tokens, $badCharTokens);
}
return;
}
@ -230,13 +258,13 @@ class Lexer
$token = "\0";
}
if (isset($this->usedAttributes['startLine'])) {
if ($this->attributeStartLineUsed) {
$startAttributes['startLine'] = $this->line;
}
if (isset($this->usedAttributes['startTokenPos'])) {
if ($this->attributeStartTokenPosUsed) {
$startAttributes['startTokenPos'] = $this->pos;
}
if (isset($this->usedAttributes['startFilePos'])) {
if ($this->attributeStartFilePosUsed) {
$startAttributes['startFilePos'] = $this->filePos;
}
@ -263,7 +291,7 @@ class Lexer
$this->filePos += \strlen($value);
} else {
if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
if (isset($this->usedAttributes['comments'])) {
if ($this->attributeCommentsUsed) {
$comment = \T_DOC_COMMENT === $token[0]
? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos)
: new Comment($token[1], $this->line, $this->filePos, $this->pos);
@ -276,13 +304,13 @@ class Lexer
continue;
}
if (isset($this->usedAttributes['endLine'])) {
if ($this->attributeEndLineUsed) {
$endAttributes['endLine'] = $this->line;
}
if (isset($this->usedAttributes['endTokenPos'])) {
if ($this->attributeEndTokenPosUsed) {
$endAttributes['endTokenPos'] = $this->pos;
}
if (isset($this->usedAttributes['endFilePos'])) {
if ($this->attributeEndFilePosUsed) {
$endAttributes['endFilePos'] = $this->filePos - 1;
}

View File

@ -4,26 +4,33 @@ namespace PhpParser\Lexer;
use PhpParser\Error;
use PhpParser\ErrorHandler;
use PhpParser\Parser;
use PhpParser\Lexer;
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulatorInterface;
use PhpParser\Parser\Tokens;
class Emulative extends \PhpParser\Lexer
class Emulative extends Lexer
{
const PHP_7_3 = '7.3.0dev';
const PHP_7_4 = '7.4.0dev';
const T_COALESCE_EQUAL = 1007;
const T_FN = 1008;
const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX'
/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n
(?:.*\r?\n)*?
(?<indentation>\h*)\2(?![a-zA-Z_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
REGEX;
const T_COALESCE_EQUAL = 1007;
/**
* @var mixed[] Patches used to reverse changes introduced in the code
*/
/** @var mixed[] Patches used to reverse changes introduced in the code */
private $patches = [];
/** @var TokenEmulatorInterface[] */
private $tokenEmulators = [];
/**
* @param mixed[] $options
*/
@ -31,8 +38,12 @@ REGEX;
{
parent::__construct($options);
// add emulated tokens here
$this->tokenMap[self::T_COALESCE_EQUAL] = Parser\Tokens::T_COALESCE_EQUAL;
$this->tokenEmulators[] = new FnTokenEmulator();
$this->tokenEmulators[] = new CoaleseEqualTokenEmulator();
$this->tokenEmulators[] = new NumericLiteralSeparatorEmulator();
$this->tokenMap[self::T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL;
$this->tokenMap[self::T_FN] = Tokens::T_FN;
}
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
@ -49,9 +60,6 @@ REGEX;
// 1. emulation of heredoc and nowdoc new syntax
$preparedCode = $this->processHeredocNowdoc($code);
parent::startLexing($preparedCode, $collector);
// 2. emulation of ??= token
$this->processCoaleseEqual($code);
$this->fixupTokens();
$errors = $collector->getErrors();
@ -61,39 +69,11 @@ REGEX;
$errorHandler->handleError($error);
}
}
}
private function isCoalesceEqualEmulationNeeded(string $code): bool
{
// skip version where this works without emulation
if (version_compare(\PHP_VERSION, self::PHP_7_4, '>=')) {
return false;
}
return strpos($code, '??=') !== false;
}
private function processCoaleseEqual(string $code)
{
if ($this->isCoalesceEqualEmulationNeeded($code) === false) {
return;
}
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way
$line = 1;
for ($i = 0, $c = count($this->tokens); $i < $c; ++$i) {
if (isset($this->tokens[$i + 1])) {
if ($this->tokens[$i][0] === T_COALESCE && $this->tokens[$i + 1] === '=') {
array_splice($this->tokens, $i, 2, [
[self::T_COALESCE_EQUAL, '??=', $line]
]);
$c--;
continue;
}
}
if (\is_array($this->tokens[$i])) {
$line += substr_count($this->tokens[$i][1], "\n");
// add token emulation
foreach ($this->tokenEmulators as $emulativeToken) {
if ($emulativeToken->isEmulationNeeded($code)) {
$this->tokens = $emulativeToken->emulate($code, $this->tokens);
}
}
}
@ -155,15 +135,13 @@ REGEX;
private function isEmulationNeeded(string $code): bool
{
if ($this->isHeredocNowdocEmulationNeeded($code)) {
return true;
foreach ($this->tokenEmulators as $emulativeToken) {
if ($emulativeToken->isEmulationNeeded($code)) {
return true;
}
}
if ($this->isCoalesceEqualEmulationNeeded($code)) {
return true;
}
return false;
return $this->isHeredocNowdocEmulationNeeded($code);
}
private function fixupTokens()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ class Clone_ extends Expr
* @param array $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->attributes = $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 $attributes = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->static = $subNodes['static'] ?? false;
$this->byRef = $subNodes['byRef'] ?? false;
$this->params = $subNodes['params'] ?? [];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ class Param extends NodeAbstract
$var, Expr $default = null, $type = null,
bool $byRef = false, bool $variadic = false, array $attributes = []
) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->type = \is_string($type) ? new Identifier($type) : $type;
$this->byRef = $byRef;
$this->variadic = $variadic;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ class String_ extends Scalar
* @param array $attributes Additional attributes
*/
public function __construct(string $value, array $attributes = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->value = $value;
}
@ -100,7 +100,7 @@ class String_ extends Scalar
if (isset(self::$replacements[$str])) {
return self::$replacements[$str];
} elseif ('x' === $str[0] || 'X' === $str[0]) {
return chr(hexdec($str));
return chr(hexdec(substr($str, 1)));
} elseif ('u' === $str[0]) {
return self::codePointToUtf8(hexdec($matches[2]));
} else {

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 = []) {
parent::__construct($attributes);
$this->attributes = $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 $attributes = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ class Class_ extends ClassLike
* @param array $attributes Additional attributes
*/
public function __construct($name, array $subNodes = [], array $attributes = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->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 = []) {
parent::__construct($attributes);
$this->attributes = $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 = []) {
parent::__construct($attributes);
$this->attributes = $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 = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->key = \is_string($key) ? new Node\Identifier($key) : $key;
$this->value = $value;
}

View File

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

View File

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

View File

@ -16,7 +16,7 @@ class Finally_ extends Node\Stmt
* @param array $attributes Additional attributes
*/
public function __construct(array $stmts = [], array $attributes = []) {
parent::__construct($attributes);
$this->attributes = $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 $attributes = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->init = $subNodes['init'] ?? [];
$this->cond = $subNodes['cond'] ?? [];
$this->loop = $subNodes['loop'] ?? [];

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

View File

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

View File

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

View File

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

View File

@ -23,7 +23,7 @@ class GroupUse extends Stmt
* @param array $attributes Additional attributes
*/
public function __construct(Name $prefix, array $uses, int $type = Use_::TYPE_NORMAL, array $attributes = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->type = $type;
$this->prefix = $prefix;
$this->uses = $uses;

View File

@ -16,7 +16,7 @@ class HaltCompiler extends Stmt
* @param array $attributes Additional attributes
*/
public function __construct(string $remaining, array $attributes = []) {
parent::__construct($attributes);
$this->attributes = $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 $attributes = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $subNodes['stmts'] ?? [];
$this->elseifs = $subNodes['elseifs'] ?? [];

View File

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

View File

@ -19,7 +19,7 @@ class Interface_ extends ClassLike
* @param array $attributes Additional attributes
*/
public function __construct($name, array $subNodes = [], array $attributes = []) {
parent::__construct($attributes);
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->extends = $subNodes['extends'] ?? [];
$this->stmts = $subNodes['stmts'] ?? [];

View File

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

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