mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2024-11-26 20:14:46 +01:00
Add support for ??= operator
Introduced in PHP 5.4, represented using an AssignOp\Coalesce node.
This commit is contained in:
parent
b7e6361536
commit
9de96821f7
@ -1,7 +1,9 @@
|
||||
Version 4.2.1-dev
|
||||
-----------------
|
||||
|
||||
Nothing yet.
|
||||
### Added
|
||||
|
||||
* [PHP 7.4] Add support for `??=` operator through a new `AssignOp\Coalesce` node. (#575)
|
||||
|
||||
Version 4.2.0 (2019-01-12)
|
||||
--------------------------
|
||||
|
@ -579,6 +579,7 @@ expr:
|
||||
| variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
|
||||
| variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
|
||||
| variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; }
|
||||
| variable T_COALESCE_EQUAL expr { $$ = Expr\AssignOp\Coalesce [$1, $3]; }
|
||||
| variable T_INC { $$ = Expr\PostInc[$1]; }
|
||||
| T_INC variable { $$ = Expr\PreInc [$2]; }
|
||||
| variable T_DEC { $$ = Expr\PostDec[$1]; }
|
||||
|
@ -660,6 +660,7 @@ expr:
|
||||
| variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
|
||||
| variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
|
||||
| variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; }
|
||||
| variable T_COALESCE_EQUAL expr { $$ = Expr\AssignOp\Coalesce [$1, $3]; }
|
||||
| variable T_INC { $$ = Expr\PostInc[$1]; }
|
||||
| T_INC variable { $$ = Expr\PreInc [$2]; }
|
||||
| variable T_DEC { $$ = Expr\PostDec[$1]; }
|
||||
|
@ -10,7 +10,7 @@
|
||||
%right T_YIELD
|
||||
%right T_DOUBLE_ARROW
|
||||
%right T_YIELD_FROM
|
||||
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL
|
||||
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL T_COALESCE_EQUAL
|
||||
%left '?' ':'
|
||||
%right T_COALESCE
|
||||
%left T_BOOLEAN_OR
|
||||
|
@ -4,27 +4,54 @@ namespace PhpParser\Lexer;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\Parser;
|
||||
|
||||
class Emulative extends \PhpParser\Lexer
|
||||
{
|
||||
const PHP_7_3 = '7.3.0dev';
|
||||
const PHP_7_4 = '7.4.0dev';
|
||||
|
||||
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 array Patches used to reverse changes introduced in the code
|
||||
* @var mixed[] Patches used to reverse changes introduced in the code
|
||||
*/
|
||||
private $patches;
|
||||
private $patches = [];
|
||||
|
||||
/**
|
||||
* @param mixed[] $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
// add emulated tokens here
|
||||
$this->tokenMap[self::T_COALESCE_EQUAL] = Parser\Tokens::T_COALESCE_EQUAL;
|
||||
}
|
||||
|
||||
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
|
||||
$this->patches = [];
|
||||
$preparedCode = $this->prepareCode($code);
|
||||
if (null === $preparedCode) {
|
||||
|
||||
if ($this->isEmulationNeeded($code) === false) {
|
||||
// Nothing to emulate, yay
|
||||
parent::startLexing($code, $errorHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
$collector = new ErrorHandler\Collecting();
|
||||
|
||||
// 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();
|
||||
@ -36,30 +63,60 @@ class Emulative extends \PhpParser\Lexer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares code for emulation. If nothing has to be emulated null is returned.
|
||||
*
|
||||
* @param string $code
|
||||
* @return null|string
|
||||
*/
|
||||
private function prepareCode(string $code) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isHeredocNowdocEmulationNeeded(string $code): bool
|
||||
{
|
||||
// skip version where this works without emulation
|
||||
if (version_compare(\PHP_VERSION, self::PHP_7_3, '>=')) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($code, '<<<') === false) {
|
||||
// Definitely doesn't contain heredoc/nowdoc
|
||||
return null;
|
||||
return strpos($code, '<<<') !== false;
|
||||
}
|
||||
|
||||
private function processHeredocNowdoc(string $code): string
|
||||
{
|
||||
if ($this->isHeredocNowdocEmulationNeeded($code) === false) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
$flexibleDocStringRegex = <<<'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;
|
||||
if (!preg_match_all($flexibleDocStringRegex, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
|
||||
if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
|
||||
// No heredoc/nowdoc found
|
||||
return null;
|
||||
return $code;
|
||||
}
|
||||
|
||||
// Keep track of how much we need to adjust string offsets due to the modifications we
|
||||
@ -93,19 +150,31 @@ REGEX;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->patches)) {
|
||||
// We did not end up emulating anything
|
||||
return null;
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
private function fixupTokens() {
|
||||
assert(count($this->patches) > 0);
|
||||
private function isEmulationNeeded(string $code): bool
|
||||
{
|
||||
if ($this->isHeredocNowdocEmulationNeeded($code)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isCoalesceEqualEmulationNeeded($code)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function fixupTokens()
|
||||
{
|
||||
if (\count($this->patches) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load first patch
|
||||
$patchIdx = 0;
|
||||
|
||||
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
|
||||
|
||||
// We use a manual loop over the tokens, because we modify the array on the fly
|
||||
@ -200,4 +269,4 @@ REGEX;
|
||||
$error->setAttributes($attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
lib/PhpParser/Node/Expr/AssignOp/Coalesce.php
Normal file
12
lib/PhpParser/Node/Expr/AssignOp/Coalesce.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Expr\AssignOp;
|
||||
|
||||
use PhpParser\Node\Expr\AssignOp;
|
||||
|
||||
class Coalesce extends AssignOp
|
||||
{
|
||||
public function getType() : string {
|
||||
return 'Expr_AssignOp_Coalesce';
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -30,115 +30,116 @@ final class Tokens
|
||||
const T_SL_EQUAL = 278;
|
||||
const T_SR_EQUAL = 279;
|
||||
const T_POW_EQUAL = 280;
|
||||
const T_COALESCE = 281;
|
||||
const T_BOOLEAN_OR = 282;
|
||||
const T_BOOLEAN_AND = 283;
|
||||
const T_IS_EQUAL = 284;
|
||||
const T_IS_NOT_EQUAL = 285;
|
||||
const T_IS_IDENTICAL = 286;
|
||||
const T_IS_NOT_IDENTICAL = 287;
|
||||
const T_SPACESHIP = 288;
|
||||
const T_IS_SMALLER_OR_EQUAL = 289;
|
||||
const T_IS_GREATER_OR_EQUAL = 290;
|
||||
const T_SL = 291;
|
||||
const T_SR = 292;
|
||||
const T_INSTANCEOF = 293;
|
||||
const T_INC = 294;
|
||||
const T_DEC = 295;
|
||||
const T_INT_CAST = 296;
|
||||
const T_DOUBLE_CAST = 297;
|
||||
const T_STRING_CAST = 298;
|
||||
const T_ARRAY_CAST = 299;
|
||||
const T_OBJECT_CAST = 300;
|
||||
const T_BOOL_CAST = 301;
|
||||
const T_UNSET_CAST = 302;
|
||||
const T_POW = 303;
|
||||
const T_NEW = 304;
|
||||
const T_CLONE = 305;
|
||||
const T_EXIT = 306;
|
||||
const T_IF = 307;
|
||||
const T_ELSEIF = 308;
|
||||
const T_ELSE = 309;
|
||||
const T_ENDIF = 310;
|
||||
const T_LNUMBER = 311;
|
||||
const T_DNUMBER = 312;
|
||||
const T_STRING = 313;
|
||||
const T_STRING_VARNAME = 314;
|
||||
const T_VARIABLE = 315;
|
||||
const T_NUM_STRING = 316;
|
||||
const T_INLINE_HTML = 317;
|
||||
const T_CHARACTER = 318;
|
||||
const T_BAD_CHARACTER = 319;
|
||||
const T_ENCAPSED_AND_WHITESPACE = 320;
|
||||
const T_CONSTANT_ENCAPSED_STRING = 321;
|
||||
const T_ECHO = 322;
|
||||
const T_DO = 323;
|
||||
const T_WHILE = 324;
|
||||
const T_ENDWHILE = 325;
|
||||
const T_FOR = 326;
|
||||
const T_ENDFOR = 327;
|
||||
const T_FOREACH = 328;
|
||||
const T_ENDFOREACH = 329;
|
||||
const T_DECLARE = 330;
|
||||
const T_ENDDECLARE = 331;
|
||||
const T_AS = 332;
|
||||
const T_SWITCH = 333;
|
||||
const T_ENDSWITCH = 334;
|
||||
const T_CASE = 335;
|
||||
const T_DEFAULT = 336;
|
||||
const T_BREAK = 337;
|
||||
const T_CONTINUE = 338;
|
||||
const T_GOTO = 339;
|
||||
const T_FUNCTION = 340;
|
||||
const T_CONST = 341;
|
||||
const T_RETURN = 342;
|
||||
const T_TRY = 343;
|
||||
const T_CATCH = 344;
|
||||
const T_FINALLY = 345;
|
||||
const T_THROW = 346;
|
||||
const T_USE = 347;
|
||||
const T_INSTEADOF = 348;
|
||||
const T_GLOBAL = 349;
|
||||
const T_STATIC = 350;
|
||||
const T_ABSTRACT = 351;
|
||||
const T_FINAL = 352;
|
||||
const T_PRIVATE = 353;
|
||||
const T_PROTECTED = 354;
|
||||
const T_PUBLIC = 355;
|
||||
const T_VAR = 356;
|
||||
const T_UNSET = 357;
|
||||
const T_ISSET = 358;
|
||||
const T_EMPTY = 359;
|
||||
const T_HALT_COMPILER = 360;
|
||||
const T_CLASS = 361;
|
||||
const T_TRAIT = 362;
|
||||
const T_INTERFACE = 363;
|
||||
const T_EXTENDS = 364;
|
||||
const T_IMPLEMENTS = 365;
|
||||
const T_OBJECT_OPERATOR = 366;
|
||||
const T_LIST = 367;
|
||||
const T_ARRAY = 368;
|
||||
const T_CALLABLE = 369;
|
||||
const T_CLASS_C = 370;
|
||||
const T_TRAIT_C = 371;
|
||||
const T_METHOD_C = 372;
|
||||
const T_FUNC_C = 373;
|
||||
const T_LINE = 374;
|
||||
const T_FILE = 375;
|
||||
const T_COMMENT = 376;
|
||||
const T_DOC_COMMENT = 377;
|
||||
const T_OPEN_TAG = 378;
|
||||
const T_OPEN_TAG_WITH_ECHO = 379;
|
||||
const T_CLOSE_TAG = 380;
|
||||
const T_WHITESPACE = 381;
|
||||
const T_START_HEREDOC = 382;
|
||||
const T_END_HEREDOC = 383;
|
||||
const T_DOLLAR_OPEN_CURLY_BRACES = 384;
|
||||
const T_CURLY_OPEN = 385;
|
||||
const T_PAAMAYIM_NEKUDOTAYIM = 386;
|
||||
const T_NAMESPACE = 387;
|
||||
const T_NS_C = 388;
|
||||
const T_DIR = 389;
|
||||
const T_NS_SEPARATOR = 390;
|
||||
const T_ELLIPSIS = 391;
|
||||
const T_COALESCE_EQUAL = 281;
|
||||
const T_COALESCE = 282;
|
||||
const T_BOOLEAN_OR = 283;
|
||||
const T_BOOLEAN_AND = 284;
|
||||
const T_IS_EQUAL = 285;
|
||||
const T_IS_NOT_EQUAL = 286;
|
||||
const T_IS_IDENTICAL = 287;
|
||||
const T_IS_NOT_IDENTICAL = 288;
|
||||
const T_SPACESHIP = 289;
|
||||
const T_IS_SMALLER_OR_EQUAL = 290;
|
||||
const T_IS_GREATER_OR_EQUAL = 291;
|
||||
const T_SL = 292;
|
||||
const T_SR = 293;
|
||||
const T_INSTANCEOF = 294;
|
||||
const T_INC = 295;
|
||||
const T_DEC = 296;
|
||||
const T_INT_CAST = 297;
|
||||
const T_DOUBLE_CAST = 298;
|
||||
const T_STRING_CAST = 299;
|
||||
const T_ARRAY_CAST = 300;
|
||||
const T_OBJECT_CAST = 301;
|
||||
const T_BOOL_CAST = 302;
|
||||
const T_UNSET_CAST = 303;
|
||||
const T_POW = 304;
|
||||
const T_NEW = 305;
|
||||
const T_CLONE = 306;
|
||||
const T_EXIT = 307;
|
||||
const T_IF = 308;
|
||||
const T_ELSEIF = 309;
|
||||
const T_ELSE = 310;
|
||||
const T_ENDIF = 311;
|
||||
const T_LNUMBER = 312;
|
||||
const T_DNUMBER = 313;
|
||||
const T_STRING = 314;
|
||||
const T_STRING_VARNAME = 315;
|
||||
const T_VARIABLE = 316;
|
||||
const T_NUM_STRING = 317;
|
||||
const T_INLINE_HTML = 318;
|
||||
const T_CHARACTER = 319;
|
||||
const T_BAD_CHARACTER = 320;
|
||||
const T_ENCAPSED_AND_WHITESPACE = 321;
|
||||
const T_CONSTANT_ENCAPSED_STRING = 322;
|
||||
const T_ECHO = 323;
|
||||
const T_DO = 324;
|
||||
const T_WHILE = 325;
|
||||
const T_ENDWHILE = 326;
|
||||
const T_FOR = 327;
|
||||
const T_ENDFOR = 328;
|
||||
const T_FOREACH = 329;
|
||||
const T_ENDFOREACH = 330;
|
||||
const T_DECLARE = 331;
|
||||
const T_ENDDECLARE = 332;
|
||||
const T_AS = 333;
|
||||
const T_SWITCH = 334;
|
||||
const T_ENDSWITCH = 335;
|
||||
const T_CASE = 336;
|
||||
const T_DEFAULT = 337;
|
||||
const T_BREAK = 338;
|
||||
const T_CONTINUE = 339;
|
||||
const T_GOTO = 340;
|
||||
const T_FUNCTION = 341;
|
||||
const T_CONST = 342;
|
||||
const T_RETURN = 343;
|
||||
const T_TRY = 344;
|
||||
const T_CATCH = 345;
|
||||
const T_FINALLY = 346;
|
||||
const T_THROW = 347;
|
||||
const T_USE = 348;
|
||||
const T_INSTEADOF = 349;
|
||||
const T_GLOBAL = 350;
|
||||
const T_STATIC = 351;
|
||||
const T_ABSTRACT = 352;
|
||||
const T_FINAL = 353;
|
||||
const T_PRIVATE = 354;
|
||||
const T_PROTECTED = 355;
|
||||
const T_PUBLIC = 356;
|
||||
const T_VAR = 357;
|
||||
const T_UNSET = 358;
|
||||
const T_ISSET = 359;
|
||||
const T_EMPTY = 360;
|
||||
const T_HALT_COMPILER = 361;
|
||||
const T_CLASS = 362;
|
||||
const T_TRAIT = 363;
|
||||
const T_INTERFACE = 364;
|
||||
const T_EXTENDS = 365;
|
||||
const T_IMPLEMENTS = 366;
|
||||
const T_OBJECT_OPERATOR = 367;
|
||||
const T_LIST = 368;
|
||||
const T_ARRAY = 369;
|
||||
const T_CALLABLE = 370;
|
||||
const T_CLASS_C = 371;
|
||||
const T_TRAIT_C = 372;
|
||||
const T_METHOD_C = 373;
|
||||
const T_FUNC_C = 374;
|
||||
const T_LINE = 375;
|
||||
const T_FILE = 376;
|
||||
const T_COMMENT = 377;
|
||||
const T_DOC_COMMENT = 378;
|
||||
const T_OPEN_TAG = 379;
|
||||
const T_OPEN_TAG_WITH_ECHO = 380;
|
||||
const T_CLOSE_TAG = 381;
|
||||
const T_WHITESPACE = 382;
|
||||
const T_START_HEREDOC = 383;
|
||||
const T_END_HEREDOC = 384;
|
||||
const T_DOLLAR_OPEN_CURLY_BRACES = 385;
|
||||
const T_CURLY_OPEN = 386;
|
||||
const T_PAAMAYIM_NEKUDOTAYIM = 387;
|
||||
const T_NAMESPACE = 388;
|
||||
const T_NS_C = 389;
|
||||
const T_DIR = 390;
|
||||
const T_NS_SEPARATOR = 391;
|
||||
const T_ELLIPSIS = 392;
|
||||
}
|
||||
|
@ -260,6 +260,10 @@ class Standard extends PrettyPrinterAbstract
|
||||
return $this->pInfixOp(AssignOp\Pow::class, $node->var, ' **= ', $node->expr);
|
||||
}
|
||||
|
||||
protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node) {
|
||||
return $this->pInfixOp(AssignOp\Coalesce::class, $node->var, ' ??= ', $node->expr);
|
||||
}
|
||||
|
||||
// Binary expressions
|
||||
|
||||
protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) {
|
||||
|
@ -82,6 +82,7 @@ abstract class PrettyPrinterAbstract
|
||||
AssignOp\ShiftLeft::class => [160, 1],
|
||||
AssignOp\ShiftRight::class => [160, 1],
|
||||
AssignOp\Pow::class => [160, 1],
|
||||
AssignOp\Coalesce::class => [160, 1],
|
||||
Expr\YieldFrom::class => [165, 1],
|
||||
Expr\Print_::class => [168, 1],
|
||||
BinaryOp\LogicalAnd::class => [170, -1],
|
||||
@ -1156,7 +1157,7 @@ abstract class PrettyPrinterAbstract
|
||||
Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class,
|
||||
AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class,
|
||||
AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class,
|
||||
AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class,
|
||||
AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class
|
||||
];
|
||||
foreach ($assignOps as $assignOp) {
|
||||
$this->fixupMap[$assignOp] = [
|
||||
|
@ -108,6 +108,10 @@ class EmulativeTest extends LexerTest
|
||||
|
||||
public function provideTestLexNewFeatures() {
|
||||
return [
|
||||
// PHP 7.4
|
||||
['??=', [
|
||||
[Tokens::T_COALESCE_EQUAL, '??='],
|
||||
]],
|
||||
['yield from', [
|
||||
[Tokens::T_YIELD_FROM, 'yield from'],
|
||||
]],
|
||||
|
@ -17,6 +17,7 @@ $a += $b;
|
||||
$a <<= $b;
|
||||
$a >>= $b;
|
||||
$a **= $b;
|
||||
$a ??= $b;
|
||||
|
||||
// chained assign
|
||||
$a = $b *= $c **= $d;
|
||||
@ -185,6 +186,16 @@ array(
|
||||
)
|
||||
)
|
||||
13: Stmt_Expression(
|
||||
expr: Expr_AssignOp_Coalesce(
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
expr: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
)
|
||||
)
|
||||
14: Stmt_Expression(
|
||||
expr: Expr_Assign(
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
@ -213,7 +224,7 @@ array(
|
||||
0: // chained assign
|
||||
)
|
||||
)
|
||||
14: Stmt_Expression(
|
||||
15: Stmt_Expression(
|
||||
expr: Expr_AssignRef(
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
@ -232,7 +243,7 @@ array(
|
||||
0: // by ref assign
|
||||
)
|
||||
)
|
||||
15: Stmt_Expression(
|
||||
16: Stmt_Expression(
|
||||
expr: Expr_Assign(
|
||||
var: Expr_List(
|
||||
items: array(
|
||||
@ -259,7 +270,7 @@ array(
|
||||
0: // list() assign
|
||||
)
|
||||
)
|
||||
16: Stmt_Expression(
|
||||
17: Stmt_Expression(
|
||||
expr: Expr_Assign(
|
||||
var: Expr_List(
|
||||
items: array(
|
||||
@ -285,7 +296,7 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
17: Stmt_Expression(
|
||||
18: Stmt_Expression(
|
||||
expr: Expr_Assign(
|
||||
var: Expr_List(
|
||||
items: array(
|
||||
@ -326,7 +337,7 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
18: Stmt_Expression(
|
||||
19: Stmt_Expression(
|
||||
expr: Expr_PreInc(
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
@ -339,21 +350,21 @@ array(
|
||||
0: // inc/dec
|
||||
)
|
||||
)
|
||||
19: Stmt_Expression(
|
||||
20: Stmt_Expression(
|
||||
expr: Expr_PostInc(
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
)
|
||||
)
|
||||
20: Stmt_Expression(
|
||||
21: Stmt_Expression(
|
||||
expr: Expr_PreDec(
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
)
|
||||
)
|
||||
21: Stmt_Expression(
|
||||
22: Stmt_Expression(
|
||||
expr: Expr_PostDec(
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
|
@ -56,9 +56,9 @@ $a || $b;
|
||||
$a ? $b : $c;
|
||||
$a ?: $c;
|
||||
$a ?? $c;
|
||||
|
||||
$a = $b;
|
||||
$a **= $b;
|
||||
$a ??= $c;
|
||||
$a *= $b;
|
||||
$a /= $b;
|
||||
$a %= $b;
|
||||
@ -131,6 +131,7 @@ $a ?: $c;
|
||||
$a ?? $c;
|
||||
$a = $b;
|
||||
$a **= $b;
|
||||
$a ??= $c;
|
||||
$a *= $b;
|
||||
$a /= $b;
|
||||
$a %= $b;
|
||||
|
Loading…
Reference in New Issue
Block a user