mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-23 15:21:23 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c724dde741 | ||
|
7c4f7ca987 | ||
|
c5216ac220 | ||
|
507fa7632e | ||
|
3e74153456 | ||
|
b815a165bd | ||
|
c1f6c4c8d8 | ||
|
66d5018bb7 | ||
|
dc47765670 | ||
|
8ab65b4adc | ||
|
7fc3bcf970 | ||
|
acf8f5ef01 |
@@ -37,7 +37,7 @@ The `ParserFactory::create()` method has been removed in favor of three new meth
|
|||||||
* `createForHostVersion()`: Use this if you're parsing code for the PHP version you're running on.
|
* `createForHostVersion()`: Use this if you're parsing code for the PHP version you're running on.
|
||||||
* `createForVersion()`: Use this if you know the PHP version of the code you want to parse.
|
* `createForVersion()`: Use this if you know the PHP version of the code you want to parse.
|
||||||
|
|
||||||
The `createForNewestSupportedVersion()` and `creatForHostVersion()` are available since PHP-Parser 4.18.0, to allow libraries to support PHP-Parser 4 and 5 at the same time more easily.
|
The `createForNewestSupportedVersion()` and `createForHostVersion()` are available since PHP-Parser 4.18.0, to allow libraries to support PHP-Parser 4 and 5 at the same time more easily.
|
||||||
|
|
||||||
In all cases, the PHP version is a fairly weak hint that is only used on a best-effort basis. The parser will usually accept code for newer versions if it does not have any backwards-compatibility implications.
|
In all cases, the PHP version is a fairly weak hint that is only used on a best-effort basis. The parser will usually accept code for newer versions if it does not have any backwards-compatibility implications.
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ PhpParser\Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK -> PhpParser\Modifiers::VIS
|
|||||||
|
|
||||||
### Changes to node constructors
|
### Changes to node constructors
|
||||||
|
|
||||||
Node constructor arguments accepting types now longer accept plain strings. Either an `Identifier` or `Name` (or `ComplexType`) should be passed instead. This affects the following constructor arguments:
|
Node constructor arguments accepting types no longer accept plain strings. Either an `Identifier` or `Name` (or `ComplexType`) should be passed instead. This affects the following constructor arguments:
|
||||||
|
|
||||||
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\ArrowFunction`.
|
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\ArrowFunction`.
|
||||||
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\Closure`.
|
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\Closure`.
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
%pure_parser
|
%pure_parser
|
||||||
%expect 2
|
%expect 2
|
||||||
|
|
||||||
|
%right T_VOID_CAST
|
||||||
%right T_THROW
|
%right T_THROW
|
||||||
%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
|
%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
|
||||||
%left ','
|
%left ','
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
%left '+' '-' '.'
|
%left '+' '-' '.'
|
||||||
#endif
|
#endif
|
||||||
#if PHP8
|
#if PHP8
|
||||||
|
%left T_PIPE
|
||||||
%left '.'
|
%left '.'
|
||||||
%left T_SL T_SR
|
%left T_SL T_SR
|
||||||
%left '+' '-'
|
%left '+' '-'
|
||||||
@@ -471,6 +473,7 @@ fn_identifier:
|
|||||||
identifier_not_reserved
|
identifier_not_reserved
|
||||||
| T_READONLY { $$ = Node\Identifier[$1]; }
|
| T_READONLY { $$ = Node\Identifier[$1]; }
|
||||||
| T_EXIT { $$ = Node\Identifier[$1]; }
|
| T_EXIT { $$ = Node\Identifier[$1]; }
|
||||||
|
| T_CLONE { $$ = Node\Identifier[$1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
function_declaration_statement:
|
function_declaration_statement:
|
||||||
@@ -683,6 +686,7 @@ property_modifier:
|
|||||||
| T_PROTECTED_SET { $$ = Modifiers::PROTECTED_SET; }
|
| T_PROTECTED_SET { $$ = Modifiers::PROTECTED_SET; }
|
||||||
| T_PRIVATE_SET { $$ = Modifiers::PRIVATE_SET; }
|
| T_PRIVATE_SET { $$ = Modifiers::PRIVATE_SET; }
|
||||||
| T_READONLY { $$ = Modifiers::READONLY; }
|
| T_READONLY { $$ = Modifiers::READONLY; }
|
||||||
|
| T_FINAL { $$ = Modifiers::FINAL; }
|
||||||
;
|
;
|
||||||
|
|
||||||
parameter:
|
parameter:
|
||||||
@@ -784,6 +788,22 @@ argument_list:
|
|||||||
| '(' variadic_placeholder ')' { init($2); }
|
| '(' variadic_placeholder ')' { init($2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
clone_argument_list:
|
||||||
|
'(' ')' { $$ = array(); }
|
||||||
|
| '(' non_empty_clone_argument_list optional_comma ')' { $$ = $2; }
|
||||||
|
| '(' expr ',' ')' { init(Node\Arg[$2, false, false]); }
|
||||||
|
| '(' variadic_placeholder ')' { init($2); }
|
||||||
|
;
|
||||||
|
|
||||||
|
non_empty_clone_argument_list:
|
||||||
|
expr ',' argument
|
||||||
|
{ init(new Node\Arg($1, false, false, stackAttributes(#1)), $3); }
|
||||||
|
| argument_no_expr
|
||||||
|
{ init($1); }
|
||||||
|
| non_empty_clone_argument_list ',' argument
|
||||||
|
{ push($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
variadic_placeholder:
|
variadic_placeholder:
|
||||||
T_ELLIPSIS { $$ = Node\VariadicPlaceholder[]; }
|
T_ELLIPSIS { $$ = Node\VariadicPlaceholder[]; }
|
||||||
;
|
;
|
||||||
@@ -793,14 +813,18 @@ non_empty_argument_list:
|
|||||||
| non_empty_argument_list ',' argument { push($1, $3); }
|
| non_empty_argument_list ',' argument { push($1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
argument:
|
argument_no_expr:
|
||||||
expr { $$ = Node\Arg[$1, false, false]; }
|
ampersand variable { $$ = Node\Arg[$2, true, false]; }
|
||||||
| ampersand variable { $$ = Node\Arg[$2, true, false]; }
|
|
||||||
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
||||||
| identifier_maybe_reserved ':' expr
|
| identifier_maybe_reserved ':' expr
|
||||||
{ $$ = new Node\Arg($3, false, false, attributes(), $1); }
|
{ $$ = new Node\Arg($3, false, false, attributes(), $1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
argument:
|
||||||
|
expr { $$ = Node\Arg[$1, false, false]; }
|
||||||
|
| argument_no_expr { $$ = $1; }
|
||||||
|
;
|
||||||
|
|
||||||
global_var_list:
|
global_var_list:
|
||||||
non_empty_global_var_list no_comma
|
non_empty_global_var_list no_comma
|
||||||
;
|
;
|
||||||
@@ -1014,6 +1038,7 @@ expr:
|
|||||||
}
|
}
|
||||||
| new_expr
|
| new_expr
|
||||||
| match
|
| match
|
||||||
|
| T_CLONE clone_argument_list { $$ = Expr\FuncCall[new Node\Name($1, stackAttributes(#1)), $2]; }
|
||||||
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
||||||
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
|
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
|
||||||
| variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; }
|
| variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; }
|
||||||
@@ -1063,6 +1088,9 @@ expr:
|
|||||||
| expr T_IS_SMALLER_OR_EQUAL expr { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
|
| expr T_IS_SMALLER_OR_EQUAL expr { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
|
||||||
| expr '>' expr { $$ = Expr\BinaryOp\Greater [$1, $3]; }
|
| expr '>' expr { $$ = Expr\BinaryOp\Greater [$1, $3]; }
|
||||||
| expr T_IS_GREATER_OR_EQUAL expr { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
|
| expr T_IS_GREATER_OR_EQUAL expr { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
|
||||||
|
#if PHP8
|
||||||
|
| expr T_PIPE expr { $$ = Expr\BinaryOp\Pipe[$1, $3]; }
|
||||||
|
#endif
|
||||||
| expr T_INSTANCEOF class_name_reference { $$ = Expr\Instanceof_[$1, $3]; }
|
| expr T_INSTANCEOF class_name_reference { $$ = Expr\Instanceof_[$1, $3]; }
|
||||||
| '(' expr ')' { $$ = $2; }
|
| '(' expr ')' { $$ = $2; }
|
||||||
| expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; }
|
| expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; }
|
||||||
@@ -1085,6 +1113,7 @@ expr:
|
|||||||
| T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; }
|
| T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; }
|
||||||
| T_BOOL_CAST expr { $$ = Expr\Cast\Bool_ [$2]; }
|
| T_BOOL_CAST expr { $$ = Expr\Cast\Bool_ [$2]; }
|
||||||
| T_UNSET_CAST expr { $$ = Expr\Cast\Unset_ [$2]; }
|
| T_UNSET_CAST expr { $$ = Expr\Cast\Unset_ [$2]; }
|
||||||
|
| T_VOID_CAST expr { $$ = Expr\Cast\Void_ [$2]; }
|
||||||
| T_EXIT ctor_arguments
|
| T_EXIT ctor_arguments
|
||||||
{ $$ = $this->createExitExpr($1, #1, $2, attributes()); }
|
{ $$ = $this->createExitExpr($1, #1, $2, attributes()); }
|
||||||
| '@' expr { $$ = Expr\ErrorSuppress[$2]; }
|
| '@' expr { $$ = Expr\ErrorSuppress[$2]; }
|
||||||
|
@@ -215,6 +215,9 @@ class ConstExprEvaluator {
|
|||||||
case '<': return $this->evaluate($l) < $this->evaluate($r);
|
case '<': return $this->evaluate($l) < $this->evaluate($r);
|
||||||
case '<=': return $this->evaluate($l) <= $this->evaluate($r);
|
case '<=': return $this->evaluate($l) <= $this->evaluate($r);
|
||||||
case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
|
case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
|
||||||
|
case '|>':
|
||||||
|
$lval = $this->evaluate($l);
|
||||||
|
return $this->evaluate($r)($lval);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \Exception('Should not happen');
|
throw new \Exception('Should not happen');
|
||||||
|
@@ -11,11 +11,13 @@ use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
|
|||||||
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
|
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
||||||
|
use PhpParser\Lexer\TokenEmulator\PipeOperatorEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\PropertyTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\PropertyTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
||||||
|
use PhpParser\Lexer\TokenEmulator\VoidCastEmulator;
|
||||||
use PhpParser\PhpVersion;
|
use PhpParser\PhpVersion;
|
||||||
use PhpParser\Token;
|
use PhpParser\Token;
|
||||||
|
|
||||||
@@ -47,6 +49,8 @@ class Emulative extends Lexer {
|
|||||||
new ReadonlyFunctionTokenEmulator(),
|
new ReadonlyFunctionTokenEmulator(),
|
||||||
new PropertyTokenEmulator(),
|
new PropertyTokenEmulator(),
|
||||||
new AsymmetricVisibilityTokenEmulator(),
|
new AsymmetricVisibilityTokenEmulator(),
|
||||||
|
new PipeOperatorEmulator(),
|
||||||
|
new VoidCastEmulator(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Collect emulators that are relevant for the PHP version we're running
|
// Collect emulators that are relevant for the PHP version we're running
|
||||||
|
45
lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php
Normal file
45
lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Lexer\TokenEmulator;
|
||||||
|
|
||||||
|
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
||||||
|
use PhpParser\PhpVersion;
|
||||||
|
use PhpParser\Token;
|
||||||
|
|
||||||
|
class PipeOperatorEmulator extends TokenEmulator {
|
||||||
|
public function getPhpVersion(): PhpVersion {
|
||||||
|
return PhpVersion::fromComponents(8, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmulationNeeded(string $code): bool {
|
||||||
|
return \strpos($code, '|>') !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emulate(string $code, array $tokens): array {
|
||||||
|
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||||
|
$token = $tokens[$i];
|
||||||
|
if ($token->text === '|' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '>') {
|
||||||
|
array_splice($tokens, $i, 2, [
|
||||||
|
new Token(\T_PIPE, '|>', $token->line, $token->pos),
|
||||||
|
]);
|
||||||
|
$c--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseEmulate(string $code, array $tokens): array {
|
||||||
|
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||||
|
$token = $tokens[$i];
|
||||||
|
if ($token->id === \T_PIPE) {
|
||||||
|
array_splice($tokens, $i, 1, [
|
||||||
|
new Token(\ord('|'), '|', $token->line, $token->pos),
|
||||||
|
new Token(\ord('>'), '>', $token->line, $token->pos + 1),
|
||||||
|
]);
|
||||||
|
$i++;
|
||||||
|
$c++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
}
|
98
lib/PhpParser/Lexer/TokenEmulator/VoidCastEmulator.php
Normal file
98
lib/PhpParser/Lexer/TokenEmulator/VoidCastEmulator.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Lexer\TokenEmulator;
|
||||||
|
|
||||||
|
use PhpParser\PhpVersion;
|
||||||
|
use PhpParser\Token;
|
||||||
|
|
||||||
|
class VoidCastEmulator extends TokenEmulator {
|
||||||
|
public function getPhpVersion(): PhpVersion {
|
||||||
|
return PhpVersion::fromComponents(8, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmulationNeeded(string $code): bool {
|
||||||
|
return (bool)\preg_match('/\([ \t]*void[ \t]*\)/i', $code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emulate(string $code, array $tokens): array {
|
||||||
|
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||||
|
$token = $tokens[$i];
|
||||||
|
if ($token->text !== '(') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$numTokens = 1;
|
||||||
|
$text = '(';
|
||||||
|
$j = $i + 1;
|
||||||
|
if ($j < $c && $tokens[$j]->id === \T_WHITESPACE && preg_match('/[ \t]+/', $tokens[$j]->text)) {
|
||||||
|
$text .= $tokens[$j]->text;
|
||||||
|
$numTokens++;
|
||||||
|
$j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($j >= $c || $tokens[$j]->id !== \T_STRING || \strtolower($tokens[$j]->text) !== 'void') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$text .= $tokens[$j]->text;
|
||||||
|
$numTokens++;
|
||||||
|
$k = $j + 1;
|
||||||
|
if ($k < $c && $tokens[$k]->id === \T_WHITESPACE && preg_match('/[ \t]+/', $tokens[$k]->text)) {
|
||||||
|
$text .= $tokens[$k]->text;
|
||||||
|
$numTokens++;
|
||||||
|
$k++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($k >= $c || $tokens[$k]->text !== ')') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$text .= ')';
|
||||||
|
$numTokens++;
|
||||||
|
array_splice($tokens, $i, $numTokens, [
|
||||||
|
new Token(\T_VOID_CAST, $text, $token->line, $token->pos),
|
||||||
|
]);
|
||||||
|
$c -= $numTokens - 1;
|
||||||
|
}
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseEmulate(string $code, array $tokens): array {
|
||||||
|
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||||
|
$token = $tokens[$i];
|
||||||
|
if ($token->id !== \T_VOID_CAST) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^\(([ \t]*)(void)([ \t]*)\)$/i', $token->text, $match)) {
|
||||||
|
throw new \LogicException('Unexpected T_VOID_CAST contents');
|
||||||
|
}
|
||||||
|
|
||||||
|
$newTokens = [];
|
||||||
|
$pos = $token->pos;
|
||||||
|
|
||||||
|
$newTokens[] = new Token(\ord('('), '(', $token->line, $pos);
|
||||||
|
$pos++;
|
||||||
|
|
||||||
|
if ($match[1] !== '') {
|
||||||
|
$newTokens[] = new Token(\T_WHITESPACE, $match[1], $token->line, $pos);
|
||||||
|
$pos += \strlen($match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newTokens[] = new Token(\T_STRING, $match[2], $token->line, $pos);
|
||||||
|
$pos += \strlen($match[2]);
|
||||||
|
|
||||||
|
if ($match[3] !== '') {
|
||||||
|
$newTokens[] = new Token(\T_WHITESPACE, $match[3], $token->line, $pos);
|
||||||
|
$pos += \strlen($match[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newTokens[] = new Token(\ord(')'), ')', $token->line, $pos);
|
||||||
|
|
||||||
|
array_splice($tokens, $i, 1, $newTokens);
|
||||||
|
$i += \count($newTokens) - 1;
|
||||||
|
$c += \count($newTokens) - 1;
|
||||||
|
}
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
}
|
15
lib/PhpParser/Node/Expr/BinaryOp/Pipe.php
Normal file
15
lib/PhpParser/Node/Expr/BinaryOp/Pipe.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Expr\BinaryOp;
|
||||||
|
|
||||||
|
use PhpParser\Node\Expr\BinaryOp;
|
||||||
|
|
||||||
|
class Pipe extends BinaryOp {
|
||||||
|
public function getOperatorSigil(): string {
|
||||||
|
return '|>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string {
|
||||||
|
return 'Expr_BinaryOp_Pipe';
|
||||||
|
}
|
||||||
|
}
|
@@ -32,4 +32,29 @@ abstract class CallLike extends Expr {
|
|||||||
assert(!$this->isFirstClassCallable());
|
assert(!$this->isFirstClassCallable());
|
||||||
return $this->getRawArgs();
|
return $this->getRawArgs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a specific argument from the raw arguments.
|
||||||
|
*
|
||||||
|
* Returns the named argument that matches the given `$name`, or the
|
||||||
|
* positional (unnamed) argument that exists at the given `$position`,
|
||||||
|
* otherwise, returns `null` for first-class callables or if no match is found.
|
||||||
|
*/
|
||||||
|
public function getArg(string $name, int $position): ?Arg {
|
||||||
|
if ($this->isFirstClassCallable()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
foreach ($this->getRawArgs() as $i => $arg) {
|
||||||
|
if ($arg->unpack) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
($arg->name !== null && $arg->name->toString() === $name)
|
||||||
|
|| ($arg->name === null && $i === $position)
|
||||||
|
) {
|
||||||
|
return $arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
11
lib/PhpParser/Node/Expr/Cast/Void_.php
Normal file
11
lib/PhpParser/Node/Expr/Cast/Void_.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Expr\Cast;
|
||||||
|
|
||||||
|
use PhpParser\Node\Expr\Cast;
|
||||||
|
|
||||||
|
class Void_ extends Cast {
|
||||||
|
public function getType(): string {
|
||||||
|
return 'Expr_Cast_Void';
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@ class PhpVersion {
|
|||||||
* if it is still under development.
|
* if it is still under development.
|
||||||
*/
|
*/
|
||||||
public static function getNewestSupported(): self {
|
public static function getNewestSupported(): self {
|
||||||
return self::fromComponents(8, 4);
|
return self::fromComponents(8, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -425,6 +425,10 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right, $precedence, $lhsPrecedence);
|
return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right, $precedence, $lhsPrecedence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function pExpr_BinaryOp_Pipe(BinaryOp\Pipe $node, int $precedence, int $lhsPrecedence): string {
|
||||||
|
return $this->pInfixOp(BinaryOp\Pipe::class, $node->left, ' |> ', $node->right, $precedence, $lhsPrecedence);
|
||||||
|
}
|
||||||
|
|
||||||
protected function pExpr_Instanceof(Expr\Instanceof_ $node, int $precedence, int $lhsPrecedence): string {
|
protected function pExpr_Instanceof(Expr\Instanceof_ $node, int $precedence, int $lhsPrecedence): string {
|
||||||
return $this->pPostfixOp(
|
return $this->pPostfixOp(
|
||||||
Expr\Instanceof_::class, $node->expr,
|
Expr\Instanceof_::class, $node->expr,
|
||||||
@@ -517,6 +521,10 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr, $precedence, $lhsPrecedence);
|
return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr, $precedence, $lhsPrecedence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function pExpr_Cast_Void(Cast\Void_ $node, int $precedence, int $lhsPrecedence): string {
|
||||||
|
return $this->pPrefixOp(Cast\Void_::class, '(void) ', $node->expr, $precedence, $lhsPrecedence);
|
||||||
|
}
|
||||||
|
|
||||||
// Function calls and similar constructs
|
// Function calls and similar constructs
|
||||||
|
|
||||||
protected function pExpr_FuncCall(Expr\FuncCall $node): string {
|
protected function pExpr_FuncCall(Expr\FuncCall $node): string {
|
||||||
|
@@ -59,9 +59,11 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
|||||||
BinaryOp\Mod::class => [ 40, 41, 40],
|
BinaryOp\Mod::class => [ 40, 41, 40],
|
||||||
BinaryOp\Plus::class => [ 50, 51, 50],
|
BinaryOp\Plus::class => [ 50, 51, 50],
|
||||||
BinaryOp\Minus::class => [ 50, 51, 50],
|
BinaryOp\Minus::class => [ 50, 51, 50],
|
||||||
|
// FIXME: This precedence is incorrect for PHP 8.
|
||||||
BinaryOp\Concat::class => [ 50, 51, 50],
|
BinaryOp\Concat::class => [ 50, 51, 50],
|
||||||
BinaryOp\ShiftLeft::class => [ 60, 61, 60],
|
BinaryOp\ShiftLeft::class => [ 60, 61, 60],
|
||||||
BinaryOp\ShiftRight::class => [ 60, 61, 60],
|
BinaryOp\ShiftRight::class => [ 60, 61, 60],
|
||||||
|
BinaryOp\Pipe::class => [ 65, 66, 65],
|
||||||
BinaryOp\Smaller::class => [ 70, 70, 70],
|
BinaryOp\Smaller::class => [ 70, 70, 70],
|
||||||
BinaryOp\SmallerOrEqual::class => [ 70, 70, 70],
|
BinaryOp\SmallerOrEqual::class => [ 70, 70, 70],
|
||||||
BinaryOp\Greater::class => [ 70, 70, 70],
|
BinaryOp\Greater::class => [ 70, 70, 70],
|
||||||
@@ -102,6 +104,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
|||||||
Expr\Include_::class => [220, -1, -1],
|
Expr\Include_::class => [220, -1, -1],
|
||||||
Expr\ArrowFunction::class => [230, -1, -1],
|
Expr\ArrowFunction::class => [230, -1, -1],
|
||||||
Expr\Throw_::class => [240, -1, -1],
|
Expr\Throw_::class => [240, -1, -1],
|
||||||
|
Expr\Cast\Void_::class => [250, -1, -1],
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @var int Current indentation level. */
|
/** @var int Current indentation level. */
|
||||||
@@ -1372,7 +1375,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
|||||||
BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
|
BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
|
||||||
BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
|
BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
|
||||||
BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
|
BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
|
||||||
BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class,
|
BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class, BinaryOp\Pipe::class,
|
||||||
];
|
];
|
||||||
foreach ($binaryOps as $binaryOp) {
|
foreach ($binaryOps as $binaryOp) {
|
||||||
$this->fixupMap[$binaryOp] = [
|
$this->fixupMap[$binaryOp] = [
|
||||||
|
@@ -22,6 +22,9 @@ if (!\function_exists('PhpParser\defineCompatibilityTokens')) {
|
|||||||
'T_PUBLIC_SET',
|
'T_PUBLIC_SET',
|
||||||
'T_PROTECTED_SET',
|
'T_PROTECTED_SET',
|
||||||
'T_PRIVATE_SET',
|
'T_PRIVATE_SET',
|
||||||
|
// PHP 8.5
|
||||||
|
'T_PIPE',
|
||||||
|
'T_VOID_CAST',
|
||||||
];
|
];
|
||||||
|
|
||||||
// PHP-Parser might be used together with another library that also emulates some or all
|
// PHP-Parser might be used together with another library that also emulates some or all
|
||||||
|
@@ -102,6 +102,18 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/Lexer/Emulative.php
|
path: lib/PhpParser/Lexer/Emulative.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^Constant T_PIPE not found\.$#'
|
||||||
|
identifier: constant.notFound
|
||||||
|
count: 2
|
||||||
|
path: lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^Constant T_VOID_CAST not found\.$#'
|
||||||
|
identifier: constant.notFound
|
||||||
|
count: 2
|
||||||
|
path: lib/PhpParser/Lexer/TokenEmulator/VoidCastEmulator.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: '#^If condition is always false\.$#'
|
message: '#^If condition is always false\.$#'
|
||||||
identifier: if.alwaysFalse
|
identifier: if.alwaysFalse
|
||||||
|
@@ -8,7 +8,7 @@ use PhpParser\Node\Scalar;
|
|||||||
class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
||||||
/** @dataProvider provideTestEvaluate */
|
/** @dataProvider provideTestEvaluate */
|
||||||
public function testEvaluate($exprString, $expected): void {
|
public function testEvaluate($exprString, $expected): void {
|
||||||
$parser = new Parser\Php7(new Lexer());
|
$parser = (new ParserFactory())->createForNewestSupportedVersion();
|
||||||
$expr = $parser->parse('<?php ' . $exprString . ';')[0]->expr;
|
$expr = $parser->parse('<?php ' . $exprString . ';')[0]->expr;
|
||||||
$evaluator = new ConstExprEvaluator();
|
$evaluator = new ConstExprEvaluator();
|
||||||
$this->assertSame($expected, $evaluator->evaluateDirectly($expr));
|
$this->assertSame($expected, $evaluator->evaluateDirectly($expr));
|
||||||
@@ -71,6 +71,7 @@ class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
|||||||
['true || (1/0)', true],
|
['true || (1/0)', true],
|
||||||
['true or (1/0)', true],
|
['true or (1/0)', true],
|
||||||
['true xor false', true],
|
['true xor false', true],
|
||||||
|
['"foo" |> "strlen"', 3],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -424,6 +424,27 @@ class EmulativeTest extends LexerTest {
|
|||||||
[\T_STRING, 'set'],
|
[\T_STRING, 'set'],
|
||||||
[\ord(')'), ')'],
|
[\ord(')'), ')'],
|
||||||
]],
|
]],
|
||||||
|
|
||||||
|
// PHP 8.5: Pipe operator
|
||||||
|
['|>', [
|
||||||
|
[\T_PIPE, '|>']
|
||||||
|
]],
|
||||||
|
|
||||||
|
// PHP 8.5: Void cast
|
||||||
|
['(void)', [
|
||||||
|
[\T_VOID_CAST, '(void)'],
|
||||||
|
]],
|
||||||
|
["( \tvoid \t)", [
|
||||||
|
[\T_VOID_CAST, "( \tvoid \t)"],
|
||||||
|
]],
|
||||||
|
['( vOiD)', [
|
||||||
|
[\T_VOID_CAST, '( vOiD)'],
|
||||||
|
]],
|
||||||
|
["(void\n)", [
|
||||||
|
[\ord('('), '('],
|
||||||
|
[\T_STRING, 'void'],
|
||||||
|
[\ord(')'), ')'],
|
||||||
|
]],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,6 +492,29 @@ class EmulativeTest extends LexerTest {
|
|||||||
[\T_STRING, 'set'],
|
[\T_STRING, 'set'],
|
||||||
[\ord(')'), ')']
|
[\ord(')'), ')']
|
||||||
]],
|
]],
|
||||||
|
['8.5', '|>', [
|
||||||
|
[\T_PIPE, '|>']
|
||||||
|
]],
|
||||||
|
['8.4', '|>', [
|
||||||
|
[\ord('|'), '|'],
|
||||||
|
[\ord('>'), '>'],
|
||||||
|
]],
|
||||||
|
['8.5', '(void)', [
|
||||||
|
[\T_VOID_CAST, '(void)'],
|
||||||
|
]],
|
||||||
|
['8.5', "( \tvoid \t)", [
|
||||||
|
[\T_VOID_CAST, "( \tvoid \t)"],
|
||||||
|
]],
|
||||||
|
['8.4', '(void)', [
|
||||||
|
[\ord('('), '('],
|
||||||
|
[\T_STRING, 'void'],
|
||||||
|
[\ord(')'), ')'],
|
||||||
|
]],
|
||||||
|
['8.4', "( \tVOID \t)", [
|
||||||
|
[\ord('('), '('],
|
||||||
|
[\T_STRING, 'VOID'],
|
||||||
|
[\ord(')'), ')'],
|
||||||
|
]],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace PhpParser\Node\Expr;
|
namespace PhpParser\Node\Expr;
|
||||||
|
|
||||||
use PhpParser\Node\Arg;
|
use PhpParser\Node\Arg;
|
||||||
|
use PhpParser\Node\Identifier;
|
||||||
use PhpParser\Node\Name;
|
use PhpParser\Node\Name;
|
||||||
use PhpParser\Node\Scalar\Int_;
|
use PhpParser\Node\Scalar\Int_;
|
||||||
use PhpParser\Node\VariadicPlaceholder;
|
use PhpParser\Node\VariadicPlaceholder;
|
||||||
@@ -18,6 +19,13 @@ class CallableLikeTest extends \PHPUnit\Framework\TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideTestGetArg
|
||||||
|
*/
|
||||||
|
public function testGetArg(CallLike $node, ?Arg $expected): void {
|
||||||
|
$this->assertSame($expected, $node->getArg('bar', 1));
|
||||||
|
}
|
||||||
|
|
||||||
public static function provideTestIsFirstClassCallable() {
|
public static function provideTestIsFirstClassCallable() {
|
||||||
$normalArgs = [new Arg(new Int_(1))];
|
$normalArgs = [new Arg(new Int_(1))];
|
||||||
$callableArgs = [new VariadicPlaceholder()];
|
$callableArgs = [new VariadicPlaceholder()];
|
||||||
@@ -35,4 +43,56 @@ class CallableLikeTest extends \PHPUnit\Framework\TestCase {
|
|||||||
[new NullsafeMethodCall(new Variable('this'), 'test', $callableArgs), true],
|
[new NullsafeMethodCall(new Variable('this'), 'test', $callableArgs), true],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function provideTestGetArg() {
|
||||||
|
$foo = new Arg(new Int_(1));
|
||||||
|
$namedFoo = new Arg(new Int_(1), false, false, [], new Identifier('foo'));
|
||||||
|
$bar = new Arg(new Int_(2));
|
||||||
|
$namedBar = new Arg(new Int_(2), false, false, [], new Identifier('bar'));
|
||||||
|
$unpack = new Arg(new Int_(3), false, true);
|
||||||
|
$callableArgs = [new VariadicPlaceholder()];
|
||||||
|
return [
|
||||||
|
[new FuncCall(new Name('test'), [$foo]), null],
|
||||||
|
[new FuncCall(new Name('test'), [$namedFoo]), null],
|
||||||
|
[new FuncCall(new Name('test'), [$foo, $bar]), $bar],
|
||||||
|
[new FuncCall(new Name('test'), [$namedBar]), $namedBar],
|
||||||
|
[new FuncCall(new Name('test'), [$namedFoo, $namedBar]), $namedBar],
|
||||||
|
[new FuncCall(new Name('test'), [$namedBar, $namedFoo]), $namedBar],
|
||||||
|
[new FuncCall(new Name('test'), [$namedFoo, $unpack]), null],
|
||||||
|
[new FuncCall(new Name('test'), $callableArgs), null],
|
||||||
|
[new MethodCall(new Variable('this'), 'test', [$foo]), null],
|
||||||
|
[new MethodCall(new Variable('this'), 'test', [$namedFoo]), null],
|
||||||
|
[new MethodCall(new Variable('this'), 'test', [$foo, $bar]), $bar],
|
||||||
|
[new MethodCall(new Variable('this'), 'test', [$namedBar]), $namedBar],
|
||||||
|
[new MethodCall(new Variable('this'), 'test', [$namedFoo, $namedBar]), $namedBar],
|
||||||
|
[new MethodCall(new Variable('this'), 'test', [$namedBar, $namedFoo]), $namedBar],
|
||||||
|
[new MethodCall(new Variable('this'), 'test', [$namedFoo, $unpack]), null],
|
||||||
|
[new MethodCall(new Variable('this'), 'test', $callableArgs), null],
|
||||||
|
[new StaticCall(new Name('Test'), 'test', [$foo]), null],
|
||||||
|
[new StaticCall(new Name('Test'), 'test', [$namedFoo]), null],
|
||||||
|
[new StaticCall(new Name('Test'), 'test', [$foo, $bar]), $bar],
|
||||||
|
[new StaticCall(new Name('Test'), 'test', [$namedBar]), $namedBar],
|
||||||
|
[new StaticCall(new Name('Test'), 'test', [$namedFoo, $namedBar]), $namedBar],
|
||||||
|
[new StaticCall(new Name('Test'), 'test', [$namedBar, $namedFoo]), $namedBar],
|
||||||
|
[new StaticCall(new Name('Test'), 'test', [$namedFoo, $unpack]), null],
|
||||||
|
[new StaticCall(new Name('Test'), 'test', $callableArgs), null],
|
||||||
|
[new New_(new Name('test'), [$foo]), null],
|
||||||
|
[new New_(new Name('test'), [$namedFoo]), null],
|
||||||
|
[new New_(new Name('test'), [$foo, $bar]), $bar],
|
||||||
|
[new New_(new Name('test'), [$namedBar]), $namedBar],
|
||||||
|
[new New_(new Name('test'), [$namedFoo, $namedBar]), $namedBar],
|
||||||
|
[new New_(new Name('test'), [$namedBar, $namedFoo]), $namedBar],
|
||||||
|
[new New_(new Name('test'), [$namedFoo, $unpack]), null],
|
||||||
|
[new NullsafeMethodCall(new Variable('this'), 'test', [$foo]), null],
|
||||||
|
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedFoo]), null],
|
||||||
|
[new NullsafeMethodCall(new Variable('this'), 'test', [$foo, $bar]), $bar],
|
||||||
|
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedBar]), $namedBar],
|
||||||
|
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedFoo, $namedBar]), $namedBar],
|
||||||
|
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedBar, $namedFoo]), $namedBar],
|
||||||
|
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedFoo, $unpack]), null],
|
||||||
|
// This is not legal code, but accepted by the parser.
|
||||||
|
[new New_(new Name('Test'), $callableArgs), null],
|
||||||
|
[new NullsafeMethodCall(new Variable('this'), 'test', $callableArgs), null],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
75
test/code/parser/expr/pipe.test
Normal file
75
test/code/parser/expr/pipe.test
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
Pipe operator
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
$a |> $b |> $c;
|
||||||
|
$a . $b |> $c . $d;
|
||||||
|
$a |> $b == $c;
|
||||||
|
$c == $a |> $b;
|
||||||
|
-----
|
||||||
|
array(
|
||||||
|
0: Stmt_Expression(
|
||||||
|
expr: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Stmt_Expression(
|
||||||
|
expr: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_BinaryOp_Concat(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
right: Expr_BinaryOp_Concat(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: d
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
2: Stmt_Expression(
|
||||||
|
expr: Expr_BinaryOp_Equal(
|
||||||
|
left: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
3: Stmt_Expression(
|
||||||
|
expr: Expr_BinaryOp_Equal(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
right: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
@@ -10,6 +10,7 @@ class Point {
|
|||||||
public readonly int $a = 0,
|
public readonly int $a = 0,
|
||||||
public $h { set => $value; },
|
public $h { set => $value; },
|
||||||
public $g = 1 { get => 2; },
|
public $g = 1 { get => 2; },
|
||||||
|
final $i,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
-----
|
-----
|
||||||
@@ -165,6 +166,20 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
6: Param(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: FINAL (32)
|
||||||
|
type: null
|
||||||
|
byRef: false
|
||||||
|
variadic: false
|
||||||
|
var: Expr_Variable(
|
||||||
|
name: i
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
hooks: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
returnType: null
|
returnType: null
|
||||||
stmts: array(
|
stmts: array(
|
||||||
|
448
test/code/parser/stmt/function/clone_function.test
Normal file
448
test/code/parser/stmt/function/clone_function.test
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
Declaring clone function stub
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function clone(object $object, array $withProperties = []): object {}
|
||||||
|
clone $x;
|
||||||
|
clone($x);
|
||||||
|
clone($x, );
|
||||||
|
clone($x, [ "foo" => $foo, "bar" => $bar ]);
|
||||||
|
clone($x, $array);
|
||||||
|
clone($x, $array, $extraParameter, $trailingComma, );
|
||||||
|
clone(object: $x, withProperties: [ "foo" => $foo, "bar" => $bar ]);
|
||||||
|
clone($x, withProperties: [ "foo" => $foo, "bar" => $bar ]);
|
||||||
|
clone(object: $x);
|
||||||
|
clone(object: $x, [ "foo" => $foo, "bar" => $bar ]);
|
||||||
|
clone(...["object" => $x, "withProperties" => [ "foo" => $foo, "bar" => $bar ]]);
|
||||||
|
clone(...);
|
||||||
|
-----
|
||||||
|
array(
|
||||||
|
0: Stmt_Function(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
name: Identifier(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
params: array(
|
||||||
|
0: Param(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: 0
|
||||||
|
type: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
variadic: false
|
||||||
|
var: Expr_Variable(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
hooks: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Param(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: 0
|
||||||
|
type: Identifier(
|
||||||
|
name: array
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
variadic: false
|
||||||
|
var: Expr_Variable(
|
||||||
|
name: withProperties
|
||||||
|
)
|
||||||
|
default: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
returnType: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
stmts: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Stmt_Expression(
|
||||||
|
expr: Expr_Clone(
|
||||||
|
expr: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
2: Stmt_Expression(
|
||||||
|
expr: Expr_Clone(
|
||||||
|
expr: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
3: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
4: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
5: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: array
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
6: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: array
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
2: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: extraParameter
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
3: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: trailingComma
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
7: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: withProperties
|
||||||
|
)
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
8: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: withProperties
|
||||||
|
)
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
9: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
10: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
11: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: object
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: withProperties
|
||||||
|
)
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
12: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: VariadicPlaceholder(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
95
test/code/parser/stmt/voidCast.test
Normal file
95
test/code/parser/stmt/voidCast.test
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
Void cast
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
(void)foo();
|
||||||
|
( VOID ) foo();
|
||||||
|
(void)$a or $b;
|
||||||
|
|
||||||
|
// This is explicitly allowed.
|
||||||
|
for ((void)a(); $b; (void)$c) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHP does not allow this, but the parser accepts it.
|
||||||
|
$x = (void) $y;
|
||||||
|
-----
|
||||||
|
array(
|
||||||
|
0: Stmt_Expression(
|
||||||
|
expr: Expr_Cast_Void(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Stmt_Expression(
|
||||||
|
expr: Expr_Cast_Void(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
2: Stmt_Expression(
|
||||||
|
expr: Expr_Cast_Void(
|
||||||
|
expr: Expr_BinaryOp_LogicalOr(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
3: Stmt_For(
|
||||||
|
init: array(
|
||||||
|
0: Expr_Cast_Void(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cond: array(
|
||||||
|
0: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
loop: array(
|
||||||
|
0: Expr_Cast_Void(
|
||||||
|
expr: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
stmts: array(
|
||||||
|
)
|
||||||
|
comments: array(
|
||||||
|
0: // This is explicitly allowed.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
4: Stmt_Expression(
|
||||||
|
expr: Expr_Assign(
|
||||||
|
var: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
expr: Expr_Cast_Void(
|
||||||
|
expr: Expr_Variable(
|
||||||
|
name: y
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
comments: array(
|
||||||
|
0: // PHP does not allow this, but the parser accepts it.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
16
test/code/prettyPrinter/expr/pipe.test
Normal file
16
test/code/prettyPrinter/expr/pipe.test
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Pipe operator
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
$a |> $b |> $c;
|
||||||
|
$a . $b |> $c . $d;
|
||||||
|
$a |> $b == $c;
|
||||||
|
$c == $a |> $b;
|
||||||
|
($a == $b) |> ($c == $d);
|
||||||
|
$a . ($b |> $c) . $d;
|
||||||
|
-----
|
||||||
|
$a |> $b |> $c;
|
||||||
|
$a . $b |> $c . $d;
|
||||||
|
$a |> $b == $c;
|
||||||
|
$c == $a |> $b;
|
||||||
|
($a == $b) |> ($c == $d);
|
||||||
|
$a . ($b |> $c) . $d;
|
@@ -9,13 +9,14 @@ class Point
|
|||||||
protected array $y = [],
|
protected array $y = [],
|
||||||
private string $z = 'hello',
|
private string $z = 'hello',
|
||||||
public readonly int $a = 0,
|
public readonly int $a = 0,
|
||||||
|
protected final bool $b = true,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-----
|
-----
|
||||||
class Point
|
class Point
|
||||||
{
|
{
|
||||||
public function __construct(public float $x = 0.0, protected array $y = [], private string $z = 'hello', public readonly int $a = 0)
|
public function __construct(public float $x = 0.0, protected array $y = [], private string $z = 'hello', public readonly int $a = 0, final protected bool $b = true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
test/code/prettyPrinter/stmt/voidCast.test
Normal file
6
test/code/prettyPrinter/stmt/voidCast.test
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Void cast
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
(void) foo();
|
||||||
|
-----
|
||||||
|
(void) foo();
|
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
error_reporting(E_ALL | E_STRICT);
|
error_reporting(E_ALL);
|
||||||
ini_set('short_open_tag', false);
|
ini_set('short_open_tag', false);
|
||||||
|
|
||||||
if ('cli' !== php_sapi_name()) {
|
if ('cli' !== php_sapi_name()) {
|
||||||
@@ -121,6 +121,8 @@ switch ($testType) {
|
|||||||
| Zend.tests.type_declarations.intersection_types.parsing_comment
|
| Zend.tests.type_declarations.intersection_types.parsing_comment
|
||||||
# comments in property fetch syntax, not emulated on old PHP versions
|
# comments in property fetch syntax, not emulated on old PHP versions
|
||||||
| Zend.tests.gh14961
|
| Zend.tests.gh14961
|
||||||
|
# harmless pretty print difference for clone($x, )
|
||||||
|
| Zend.tests.clone.ast
|
||||||
)\.phpt$~x', $file)) {
|
)\.phpt$~x', $file)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user