refactor(compiler): 重构编译器基础类以改善表达式解析

- 统一使用 Expr 命名空间替代 Node\Expr 提高代码一致性
- 添加 OP_NOT_EMPTY 常量用于非空检查操作
- 实现值选择操作的统一处理方法 parseValueSelection
- 优化三元运算符解析逻辑,支持 ?: 操作符的正确处理
- 更新测试用例验证 ?: 操作符功能
- 添加示例文件演示赋值比较功能
- 修复多个表达式解析方法的类型声明一致性问题
- 重构链式操作解析逻辑,改进 isset 和 empty 处理
- 优化数组维度获取、函数调用等表达式的解析实现
pull/1/head
韩天峰 5 months ago
parent 59698b5319
commit 8eb944312a
  1. 38
      examples/assign_cmp.php
  2. 259
      src/Php/CompilerBase.php
  3. 4
      src/Php/CompilerTest.php
  4. 25
      tests/aot/coalesce/005.phpt

@ -0,0 +1,38 @@
<?php
class std
{
static function int(int $value): int
{
return $value;
}
static function float(float $value): float
{
return $value;
}
static function bool(bool $value): bool
{
return $value;
}
static function string(string $str): resource
{
return fopen('php://stdin', 'r');
}
}
function main()
{
$a = std::int(100);
var_dump($a);
$b = std::float(100.0);
var_dump($b);
$c = std::bool(true);
var_dump($c);
// $b = true;
// var_dump($b && $a = 99);
}

@ -27,6 +27,7 @@ use PhpAot\Php\Generator\PropertyPromotion;
use PhpAot\Php\Generator\Utils; use PhpAot\Php\Generator\Utils;
use PhpParser\Modifiers; use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\CallLike;
use PhpParser\Node\Expr\Variable; use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike; use PhpParser\Node\FunctionLike;
@ -88,6 +89,7 @@ class CompilerBase extends \PhpAot\Core\Translator
public const string STATIC_VAR = '_static_var_'; public const string STATIC_VAR = '_static_var_';
public const string OP_ISSET = 'isset'; public const string OP_ISSET = 'isset';
public const string OP_EMPTY = 'empty'; public const string OP_EMPTY = 'empty';
public const string OP_NOT_EMPTY = 'notEmpty';
public const string OP_REFVAL = 'toReference'; public const string OP_REFVAL = 'toReference';
public const string OP_NOP = "if (0) {}\n"; public const string OP_NOP = "if (0) {}\n";
protected string $phpxDir = '~/workspace/projects/phpx'; protected string $phpxDir = '~/workspace/projects/phpx';
@ -286,7 +288,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->formatCppCode($file); $this->formatCppCode($file);
} }
public function isScalarInt(Node\Expr $position): bool public function isScalarInt(Expr $position): bool
{ {
return $position instanceof Node\Scalar\LNumber; return $position instanceof Node\Scalar\LNumber;
} }
@ -997,7 +999,7 @@ class CompilerBase extends \PhpAot\Core\Translator
/* /*
* 函数参数默认值只能为字面量,无法使用表达式获取值 * 函数参数默认值只能为字面量,无法使用表达式获取值
*/ */
if ($default instanceof Node\Expr\ConstFetch) { if ($default instanceof Expr\ConstFetch) {
return $this->parseConstFetch($default, true); return $this->parseConstFetch($default, true);
} else { } else {
return $this->parseIdentifier($default); return $this->parseIdentifier($default);
@ -1085,7 +1087,7 @@ class CompilerBase extends \PhpAot\Core\Translator
// 变长参数可以视为空数组默认值 // 变长参数可以视为空数组默认值
$defaultValueCount++; $defaultValueCount++;
$argInfo->default = '{}'; $argInfo->default = '{}';
$argInfo->defaultValue = new Node\Expr\Array_(); $argInfo->defaultValue = new Expr\Array_();
} }
$functionDef->argInfoList[] = $argInfo; $functionDef->argInfoList[] = $argInfo;
} }
@ -1257,7 +1259,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return "{$array}.setProperty({$propName}, " . $this->trimBrackets($this->parseExpr($right)) . ')'; return "{$array}.setProperty({$propName}, " . $this->trimBrackets($this->parseExpr($right)) . ')';
} }
protected function parseRightAssociativeAssign(NodeAbstract $left, Node\Expr\Assign $right): string protected function parseRightAssociativeAssign(NodeAbstract $left, Expr\Assign $right): string
{ {
$chain[] = $left; $chain[] = $left;
$next = $right; $next = $right;
@ -1294,7 +1296,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return "php::setStaticProperty({$class}, {$propName}, {$value})"; return "php::setStaticProperty({$class}, {$propName}, {$value})";
} }
protected function parseAssign(Node\Expr\Assign $v): string protected function parseAssign(Expr\Assign $v): string
{ {
$left = $v->var; $left = $v->var;
$right = $v->expr; $right = $v->expr;
@ -1307,7 +1309,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseAssignFinally($left, $right): string protected function parseAssignFinally($left, $right): string
{ {
if ($left instanceof Node\Expr\List_) { if ($left instanceof Expr\List_) {
$items = $left->items; $items = $left->items;
$code = '{'; $code = '{';
$this->indentLevel++; $this->indentLevel++;
@ -1318,7 +1320,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if (!$item) { if (!$item) {
continue; continue;
} }
if ($item instanceof Node\Expr\ArrayItem) { if ($item instanceof Expr\ArrayItem) {
$oriInAssignExpr = $this->context->inAssignExpr; $oriInAssignExpr = $this->context->inAssignExpr;
$this->context->inAssignExpr = true; $this->context->inAssignExpr = true;
$var = $this->parseIdentifier($item->value); $var = $this->parseIdentifier($item->value);
@ -1433,7 +1435,7 @@ class CompilerBase extends \PhpAot\Core\Translator
{ {
$lines = []; $lines = [];
foreach ($v->exprs as $expr) { foreach ($v->exprs as $expr) {
if ($expr instanceof Node\Expr\Assign) { if ($expr instanceof Expr\Assign) {
$this->fatalError($expr, 'Cannot echo assign expression'); $this->fatalError($expr, 'Cannot echo assign expression');
} else { } else {
$lines[] = 'php::echo(' . $this->parseExpr($expr) . ');'; $lines[] = 'php::echo(' . $this->parseExpr($expr) . ');';
@ -1534,7 +1536,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return '((' . $leftExpr . ') ' . $op . ' (' . $rightExpr . '))'; return '((' . $leftExpr . ') ' . $op . ' (' . $rightExpr . '))';
} }
protected function parseBinaryOpPlus(Node\Expr\BinaryOp\Plus $expr): string protected function parseBinaryOpPlus(Expr\BinaryOp\Plus $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '+'); return $this->parseBinaryOp($expr->left, $expr->right, '+');
} }
@ -1583,7 +1585,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code; return $code;
} }
protected function parseBinaryOpMul(Node\Expr\BinaryOp\Mul $expr): string protected function parseBinaryOpMul(Expr\BinaryOp\Mul $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '*'); return $this->parseBinaryOp($expr->left, $expr->right, '*');
} }
@ -1881,7 +1883,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return self::TYPE_VAR; return self::TYPE_VAR;
} }
protected function parseArray(Node\Expr\Array_ $node): string protected function parseArray(Expr\Array_ $node): string
{ {
$items = $node->items; $items = $node->items;
// 优化代码风格,空数组直接返回{},否则会产生一些空洞内容 // 优化代码风格,空数组直接返回{},否则会产生一些空洞内容
@ -2041,7 +2043,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
protected function parseBinaryOpConcat(Node\Expr\BinaryOp\Concat $expr): string protected function parseBinaryOpConcat(Expr\BinaryOp\Concat $expr): string
{ {
$left = $this->parseIdentifier($expr->left); $left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right); $right = $this->parseIdentifier($expr->right);
@ -2090,12 +2092,12 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code; return $code;
} }
protected function parseBinaryOpSmaller(Node\Expr\BinaryOp\Smaller $expr): string protected function parseBinaryOpSmaller(Expr\BinaryOp\Smaller $expr): string
{ {
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '<')); return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '<'));
} }
protected function parsePreInc(Node\Expr\PreInc $expr): string protected function parsePreInc(Expr\PreInc $expr): string
{ {
return '++' . $this->parseIdentifier($expr->var); return '++' . $this->parseIdentifier($expr->var);
} }
@ -2105,7 +2107,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return str_replace('=', '', $op); return str_replace('=', '', $op);
} }
protected function parseAssignOp(Node\Expr\AssignOp $node, string $op): string protected function parseAssignOp(Expr\AssignOp $node, string $op): string
{ {
$var = $this->parseIdentifier($node->var); $var = $this->parseIdentifier($node->var);
$expr = $this->parseIdentifier($node->expr); $expr = $this->parseIdentifier($node->expr);
@ -2164,42 +2166,42 @@ class CompilerBase extends \PhpAot\Core\Translator
return $var . ' ' . $op . ' (' . $expr . ')'; return $var . ' ' . $op . ' (' . $expr . ')';
} }
protected function parseAssignOpConcat(Node\Expr\AssignOp\Concat $expr): string protected function parseAssignOpConcat(Expr\AssignOp\Concat $expr): string
{ {
return $this->parseAssignOp($expr, '.='); return $this->parseAssignOp($expr, '.=');
} }
protected function parseAssignOpPlus(Node\Expr\AssignOp\Plus $expr): string protected function parseAssignOpPlus(Expr\AssignOp\Plus $expr): string
{ {
return $this->parseAssignOp($expr, '+='); return $this->parseAssignOp($expr, '+=');
} }
protected function parseAssignOpMinus(Node\Expr\AssignOp\Minus $expr): string protected function parseAssignOpMinus(Expr\AssignOp\Minus $expr): string
{ {
return $this->parseAssignOp($expr, '-='); return $this->parseAssignOp($expr, '-=');
} }
protected function parseAssignOpMod(Node\Expr\AssignOp\Mod $expr): string protected function parseAssignOpMod(Expr\AssignOp\Mod $expr): string
{ {
return $this->parseAssignOp($expr, '%='); return $this->parseAssignOp($expr, '%=');
} }
protected function parseAssignOpMul(Node\Expr\AssignOp\Mul $expr): string protected function parseAssignOpMul(Expr\AssignOp\Mul $expr): string
{ {
return $this->parseAssignOp($expr, '*='); return $this->parseAssignOp($expr, '*=');
} }
protected function parseAssignOpDiv(Node\Expr\AssignOp\Div $expr): string protected function parseAssignOpDiv(Expr\AssignOp\Div $expr): string
{ {
return $this->parseAssignOp($expr, '/='); return $this->parseAssignOp($expr, '/=');
} }
protected function parseAssignOpBitwiseAnd(Node\Expr\AssignOp\BitwiseAnd $expr): string protected function parseAssignOpBitwiseAnd(Expr\AssignOp\BitwiseAnd $expr): string
{ {
return $this->parseAssignOp($expr, '&='); return $this->parseAssignOp($expr, '&=');
} }
protected function parseAssignOpPow(Node\Expr\AssignOp\Pow $expr): string protected function parseAssignOpPow(Expr\AssignOp\Pow $expr): string
{ {
return $this->parseAssignOp($expr, '**='); return $this->parseAssignOp($expr, '**=');
} }
@ -2242,7 +2244,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
protected function parseArrayDimFetch(Node\Expr\ArrayDimFetch $node, bool $write): string protected function parseArrayDimFetch(Expr\ArrayDimFetch $node, bool $write): string
{ {
$var = $this->parseIdentifier($node->var); $var = $this->parseIdentifier($node->var);
if ($this->isVarExpr($node->var)) { if ($this->isVarExpr($node->var)) {
@ -2296,17 +2298,17 @@ class CompilerBase extends \PhpAot\Core\Translator
return $id . '.offsetSet(' . $this->trimBrackets($dim) . ', ' . $this->trimBrackets($var) . ')'; return $id . '.offsetSet(' . $this->trimBrackets($dim) . ', ' . $this->trimBrackets($var) . ')';
} }
protected function parseBinaryOpShiftLeft(Node\Expr\BinaryOp\ShiftLeft $expr): string protected function parseBinaryOpShiftLeft(Expr\BinaryOp\ShiftLeft $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '<<'); return $this->parseBinaryOp($expr->left, $expr->right, '<<');
} }
protected function parseBinaryOpShiftRight(Node\Expr\BinaryOp\ShiftRight $expr): string protected function parseBinaryOpShiftRight(Expr\BinaryOp\ShiftRight $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '>>'); return $this->parseBinaryOp($expr->left, $expr->right, '>>');
} }
protected function parseBinaryOpMod(Node\Expr\BinaryOp\Mod $expr): string protected function parseBinaryOpMod(Expr\BinaryOp\Mod $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '%'); return $this->parseBinaryOp($expr->left, $expr->right, '%');
} }
@ -2363,7 +2365,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($node, 'All execution code must be within a function, found stray code'); $this->fatalError($node, 'All execution code must be within a function, found stray code');
} }
protected function parseFuncCall(Node\Expr\FuncCall $expr): string protected function parseFuncCall(Expr\FuncCall $expr): string
{ {
if ($this->isVarExpr($expr->name)) { if ($this->isVarExpr($expr->name)) {
$fn = $this->parseIdentifier($expr->name); $fn = $this->parseIdentifier($expr->name);
@ -2613,7 +2615,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->parseExpr($value); return $this->parseExpr($value);
} }
protected function parsePostOp(Node\Expr\PostDec|Node\Expr\PostInc $expr, string $op): string protected function parsePostOp(Expr\PostDec|Expr\PostInc $expr, string $op): string
{ {
if ($this->isVarExpr($expr->var) or $this->isPropertyFetch($expr->var) or $this->isArrayDimFetch($expr->var)) { if ($this->isVarExpr($expr->var) or $this->isPropertyFetch($expr->var) or $this->isArrayDimFetch($expr->var)) {
$var = $this->parseIdentifier($expr->var); $var = $this->parseIdentifier($expr->var);
@ -2640,30 +2642,25 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($expr, 'Post-increment operator is not supported for non-variable expressions'); $this->fatalError($expr, 'Post-increment operator is not supported for non-variable expressions');
} }
protected function parsePostDec(Node\Expr\PostDec $expr): string protected function parsePostDec(Expr\PostDec $expr): string
{ {
return $this->parsePostOp($expr, '-'); return $this->parsePostOp($expr, '-');
} }
protected function parsePostInc(Node\Expr\PostInc $expr): string protected function parsePostInc(Expr\PostInc $expr): string
{ {
return $this->parsePostOp($expr, '+'); return $this->parsePostOp($expr, '+');
} }
protected function parseTernary(Node\Expr\Ternary $expr): string protected function parseTernary(Expr\Ternary $expr): string
{ {
$cond = $expr->cond; if ($expr->if === null) {
$if = $expr->if; return $this->parseValueSelection($expr, $expr->cond, $expr->else, self::OP_NOT_EMPTY);
$else = $expr->else;
if ($if === null) {
$cond = $this->parseExpr($cond);
return '(' . $cond . ') ? (' . $cond . ') : (' . $this->parseExpr($else) . ')';
} }
return '(' . $this->parseExpr($cond) . ') ? (' . $this->parseExpr($if) . ') : (' . $this->parseExpr($else) . ')'; return '(' . $this->parseExpr($expr->cond) . ') ? (' . $this->parseExpr($expr->if) . ') : (' . $this->parseExpr($expr->else) . ')';
} }
protected function parseMatch(Node\Expr\Match_ $expr): string protected function parseMatch(Expr\Match_ $expr): string
{ {
$var = $this->parseIdentifier($expr->cond); $var = $this->parseIdentifier($expr->cond);
if ($this->isVarExpr($expr->cond)) { if ($this->isVarExpr($expr->cond)) {
@ -2707,12 +2704,12 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code; return $code;
} }
protected function parseBinaryOpGreater(Node\Expr\BinaryOp\Greater $expr): string protected function parseBinaryOpGreater(Expr\BinaryOp\Greater $expr): string
{ {
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '>')); return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '>'));
} }
protected function parseBinaryOpPow(Node\Expr\BinaryOp\Pow $expr): string protected function parseBinaryOpPow(Expr\BinaryOp\Pow $expr): string
{ {
$left = $this->parseIdentifier($expr->left); $left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right); $right = $this->parseIdentifier($expr->right);
@ -2720,27 +2717,27 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::math::pow(' . $left . ', ' . $right . ')'; return 'php::math::pow(' . $left . ', ' . $right . ')';
} }
protected function parsePreDec(Node\Expr\PreDec $expr): string protected function parsePreDec(Expr\PreDec $expr): string
{ {
return '--' . $this->parseIdentifier($expr->var); return '--' . $this->parseIdentifier($expr->var);
} }
protected function parseBinaryOpBitwiseAnd(Node\Expr\BinaryOp\BitwiseAnd $expr): string protected function parseBinaryOpBitwiseAnd(Expr\BinaryOp\BitwiseAnd $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '&'); return $this->parseBinaryOp($expr->left, $expr->right, '&');
} }
protected function parseBinaryOpBitwiseOr(Node\Expr\BinaryOp\BitwiseOr $expr): string protected function parseBinaryOpBitwiseOr(Expr\BinaryOp\BitwiseOr $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '|'); return $this->parseBinaryOp($expr->left, $expr->right, '|');
} }
protected function parseBinaryOpBitwiseXor(Node\Expr\BinaryOp\BitwiseXor $expr): string protected function parseBinaryOpBitwiseXor(Expr\BinaryOp\BitwiseXor $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '^'); return $this->parseBinaryOp($expr->left, $expr->right, '^');
} }
protected function parseBitwiseNot(Node\Expr\BitwiseNot $expr): string protected function parseBitwiseNot(Expr\BitwiseNot $expr): string
{ {
$var = $this->parseIdentifier($expr->expr); $var = $this->parseIdentifier($expr->expr);
@ -2780,12 +2777,12 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code . PHP_EOL; return $code . PHP_EOL;
} }
protected function parseBinaryOpEqual(Node\Expr\BinaryOp\Equal $expr): string protected function parseBinaryOpEqual(Expr\BinaryOp\Equal $expr): string
{ {
return 'php::equals(' . $this->parseExpr($expr->left) . ', ' . $this->parseExpr($expr->right) . ')'; return 'php::equals(' . $this->parseExpr($expr->left) . ', ' . $this->parseExpr($expr->right) . ')';
} }
protected function parseBinaryOpNotEqual(Node\Expr\BinaryOp\NotEqual $expr): string protected function parseBinaryOpNotEqual(Expr\BinaryOp\NotEqual $expr): string
{ {
return '!php::equals(' . $this->parseExpr($expr->left) . ', ' . $this->parseExpr($expr->right) . ')'; return '!php::equals(' . $this->parseExpr($expr->left) . ', ' . $this->parseExpr($expr->right) . ')';
} }
@ -2793,22 +2790,22 @@ class CompilerBase extends \PhpAot\Core\Translator
/** /**
* 逻辑比较的运算,必须返回 bool 类型. * 逻辑比较的运算,必须返回 bool 类型.
*/ */
protected function parseBinaryOpLogicalAnd(Node\Expr\BinaryOp\LogicalAnd|Node\Expr\BinaryOp\BooleanAnd $expr): string protected function parseBinaryOpLogicalAnd(Expr\BinaryOp\LogicalAnd|Expr\BinaryOp\BooleanAnd $expr): string
{ {
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '&&')); return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '&&'));
} }
protected function parseBinaryOpLogicalOr(Node\Expr\BinaryOp\LogicalOr|Node\Expr\BinaryOp\BooleanOr $expr): string protected function parseBinaryOpLogicalOr(Expr\BinaryOp\LogicalOr|Expr\BinaryOp\BooleanOr $expr): string
{ {
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '||')); return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '||'));
} }
protected function parseBinaryOpLogicalXor(Node\Expr\BinaryOp\LogicalXor $expr): string protected function parseBinaryOpLogicalXor(Expr\BinaryOp\LogicalXor $expr): string
{ {
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '^')); return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '^'));
} }
protected function parseBooleanNot(Node\Expr\BooleanNot $expr): string protected function parseBooleanNot(Expr\BooleanNot $expr): string
{ {
$expr = $this->parseExpr($expr->expr); $expr = $this->parseExpr($expr->expr);
@ -2887,17 +2884,17 @@ class CompilerBase extends \PhpAot\Core\Translator
return $expr; return $expr;
} }
protected function parseBinaryOpSmallerOrEqual(Node\Expr\BinaryOp\SmallerOrEqual $expr): string protected function parseBinaryOpSmallerOrEqual(Expr\BinaryOp\SmallerOrEqual $expr): string
{ {
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '<=')); return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '<='));
} }
protected function parseBinaryOpGreaterOrEqual(Node\Expr\BinaryOp\GreaterOrEqual $expr): string protected function parseBinaryOpGreaterOrEqual(Expr\BinaryOp\GreaterOrEqual $expr): string
{ {
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '>=')); return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '>='));
} }
protected function parsePrint(Node\Expr\Print_ $expr): string protected function parsePrint(Expr\Print_ $expr): string
{ {
return 'php::echo(' . $this->parseExpr($expr->expr) . ')'; return 'php::echo(' . $this->parseExpr($expr->expr) . ')';
} }
@ -2916,7 +2913,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code; return $code;
} }
protected function parseBinaryOpIdentical(Node\Expr\BinaryOp $expr): string protected function parseBinaryOpIdentical(Expr\BinaryOp $expr): string
{ {
$left = $this->parseIdentifier($expr->left); $left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right); $right = $this->parseIdentifier($expr->right);
@ -2928,7 +2925,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::same(' . $left . ', ' . $right . ')'; return 'php::same(' . $left . ', ' . $right . ')';
} }
protected function parseBinaryOpSpaceship(Node\Expr\BinaryOp\Spaceship $expr): string protected function parseBinaryOpSpaceship(Expr\BinaryOp\Spaceship $expr): string
{ {
$left = $this->parseIdentifier($expr->left); $left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right); $right = $this->parseIdentifier($expr->right);
@ -2936,31 +2933,39 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::compare(' . $left . ', ' . $right . ')'; return 'php::compare(' . $left . ', ' . $right . ')';
} }
protected function parseBinaryOpCoalesce(Node\Expr\BinaryOp\Coalesce $expr): string /**
* 值选择,如 ?: 或者 ??
*/
protected function parseValueSelection(NodeAbstract $expr, Expr $left, Expr $right, string $op): string
{ {
$left = $this->parseIdentifier($expr->left); $leftExpr = $this->parseIdentifier($left);
if ($this->isVarExpr($expr->left)) { if ($this->isVarExpr($left)) {
$this->checkVarMustExist($expr->left, $left); $this->checkVarMustExist($left, $leftExpr);
} }
$isset = $this->parseChainedExpr($expr->left, self::OP_ISSET, true); $condExpr = $this->parseChainedExpr($left, $op, true);
$chainOpResult = $expr->left->getAttribute('chainOpResult'); $chainOpResult = $left->getAttribute('chainOpResult');
if ($chainOpResult) { if ($chainOpResult) {
$left = $chainOpResult; $leftExpr = $chainOpResult;
} }
$right = $this->parseIdentifier($expr->right); $rightExpr = $this->parseIdentifier($right);
$this->checkVarMustExist($expr->right, $right); $this->checkVarMustExist($right, $rightExpr);
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(self::TYPE_VAR);
$this->context->beforeStmtLines[] = '// Coalesce: ' . $this->printer->prettyPrintExpr($expr) . PHP_EOL . $this->context->beforeStmtLines[] = '// Expr: ' . $this->printer->prettyPrintExpr($expr) . PHP_EOL .
$tmpVar . ' = ' . $isset . ' ? ' . $left . ' : ' . $right . ';'; $tmpVar . ' = ' . $condExpr . ' ? ' . $leftExpr . ' : ' . $rightExpr . ';';
$expr->setAttribute('replace', $tmpVar); $expr->setAttribute('replace', $tmpVar);
return $tmpVar; return $tmpVar;
} }
protected function parseBinaryOpNotIdentical(Node\Expr\BinaryOp $expr): string protected function parseBinaryOpCoalesce(Expr\BinaryOp\Coalesce $expr): string
{
return $this->parseValueSelection($expr, $expr->left, $expr->right, self::OP_ISSET);
}
protected function parseBinaryOpNotIdentical(Expr\BinaryOp $expr): string
{ {
return '!(' . $this->parseBinaryOpIdentical($expr) . ')'; return '!(' . $this->parseBinaryOpIdentical($expr) . ')';
} }
@ -2983,7 +2988,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->constData[$name] = $this->packData($bytes); $this->constData[$name] = $this->packData($bytes);
} }
protected function parseNew(Node\Expr\New_ $expr): string protected function parseNew(Expr\New_ $expr): string
{ {
// 匿名类 // 匿名类
if ($expr->class instanceof Node\Stmt\Class_) { if ($expr->class instanceof Node\Stmt\Class_) {
@ -3028,12 +3033,12 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::newObject(' . $cePtr . ', ' . $this->parseCallArgs($args) . ')'; return 'php::newObject(' . $cePtr . ', ' . $this->parseCallArgs($args) . ')';
} }
protected function parseClone(Node\Expr\Clone_ $expr): string protected function parseClone(Expr\Clone_ $expr): string
{ {
return 'php::clone(' . $this->parseExpr($expr->expr) . ')'; return 'php::clone(' . $this->parseExpr($expr->expr) . ')';
} }
protected function parseInstanceof(Node\Expr\Instanceof_ $expr): string protected function parseInstanceof(Expr\Instanceof_ $expr): string
{ {
if ($this->isNameExpr($expr->class)) { if ($this->isNameExpr($expr->class)) {
$className = $this->getNamespacedClassName($this->parseIdentifier($expr->class)); $className = $this->getNamespacedClassName($this->parseIdentifier($expr->class));
@ -3044,27 +3049,27 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
protected function parseCastInt(Node\Expr\Cast\Int_ $node): string protected function parseCastInt(Expr\Cast\Int_ $node): string
{ {
return $this->convertIntExpr($this->parseExpr($node->expr)); return $this->convertIntExpr($this->parseExpr($node->expr));
} }
protected function parseCastString(Node\Expr\Cast\String_ $node): string protected function parseCastString(Expr\Cast\String_ $node): string
{ {
return $this->convertStringExpr($this->parseExpr($node->expr)); return $this->convertStringExpr($this->parseExpr($node->expr));
} }
protected function parseCastBool(Node\Expr\Cast\Bool_ $node): string protected function parseCastBool(Expr\Cast\Bool_ $node): string
{ {
return $this->convertBoolExpr($this->parseExpr($node->expr)); return $this->convertBoolExpr($this->parseExpr($node->expr));
} }
protected function parseCastObject(Node\Expr\Cast\Object_ $node): string protected function parseCastObject(Expr\Cast\Object_ $node): string
{ {
return $this->convertObjectExpr($this->parseExpr($node->expr)); return $this->convertObjectExpr($this->parseExpr($node->expr));
} }
protected function parseConstFetch(Node\Expr\ConstFetch $expr, bool $scalar = false): string protected function parseConstFetch(Expr\ConstFetch $expr, bool $scalar = false): string
{ {
if ($expr->name->getType() != 'Name' and !($expr->name instanceof Node\Name\FullyQualified)) { if ($expr->name->getType() != 'Name' and !($expr->name instanceof Node\Name\FullyQualified)) {
abort($expr); abort($expr);
@ -3100,19 +3105,19 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::constant("' . $this->escapeString($name) . '")'; return 'php::constant("' . $this->escapeString($name) . '")';
} }
protected function parseUnaryMinus(Node\Expr\UnaryMinus $expr): string protected function parseUnaryMinus(Expr\UnaryMinus $expr): string
{ {
$code = $this->parseExpr($expr->expr); $code = $this->parseExpr($expr->expr);
return '-' . $code; return '-' . $code;
} }
protected function parseUnaryPlus(Node\Expr\UnaryPlus $expr) protected function parseUnaryPlus(Expr\UnaryPlus $expr)
{ {
return $this->parseExpr($expr->expr); return $this->parseExpr($expr->expr);
} }
protected function parseBinaryOpDiv(Node\Expr\BinaryOp\Div $expr): string protected function parseBinaryOpDiv(Expr\BinaryOp\Div $expr): string
{ {
$left = $this->parseIdentifier($expr->left); $left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right); $right = $this->parseIdentifier($expr->right);
@ -3120,7 +3125,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $left . ' / (' . $right . ')'; return $left . ' / (' . $right . ')';
} }
protected function parseBinaryOpMinus(Node\Expr\BinaryOp\Minus $expr): string protected function parseBinaryOpMinus(Expr\BinaryOp\Minus $expr): string
{ {
return $this->parseBinaryOp($expr->left, $expr->right, '-'); return $this->parseBinaryOp($expr->left, $expr->right, '-');
} }
@ -3202,7 +3207,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $expr; return $expr;
} }
protected function parseExit(Node\Expr\Exit_ $node): string protected function parseExit(Expr\Exit_ $node): string
{ {
if (!$node->expr) { if (!$node->expr) {
return 'php::exit(0)'; return 'php::exit(0)';
@ -3238,7 +3243,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return implode(PHP_EOL . $this->getIndent(), $lines); return implode(PHP_EOL . $this->getIndent(), $lines);
} }
protected function getPropertyIdentifier(Node\Expr\PropertyFetch $expr, NodeAbstract $object, NodeAbstract $property): ?string protected function getPropertyIdentifier(Expr\PropertyFetch $expr, NodeAbstract $object, NodeAbstract $property): ?string
{ {
if ($this->isVarExpr($object) and $this->isIdExpr($property)) { if ($this->isVarExpr($object) and $this->isIdExpr($property)) {
$objectName = $this->parseIdentifier($object); $objectName = $this->parseIdentifier($object);
@ -3258,7 +3263,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->identifierToStr($property, literal: true); return $this->identifierToStr($property, literal: true);
} }
protected function parsePropertyFetch(Node\Expr\PropertyFetch $expr, bool $update = false): string protected function parsePropertyFetch(Expr\PropertyFetch $expr, bool $update = false): string
{ {
$object = $expr->var; $object = $expr->var;
$property = $expr->name; $property = $expr->name;
@ -3266,12 +3271,12 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->parseIdentifier($object) . '.attr(' . $id . ', ' . $this->escapeBool($update) . ')'; return $this->parseIdentifier($object) . '.attr(' . $id . ', ' . $this->escapeBool($update) . ')';
} }
protected function parseAssignOpShiftRight(Node\Expr\AssignOp\ShiftRight $node): string protected function parseAssignOpShiftRight(Expr\AssignOp\ShiftRight $node): string
{ {
return $this->parseAssignOp($node, '>>='); return $this->parseAssignOp($node, '>>=');
} }
protected function parseAssignOpBitwiseXor(Node\Expr\AssignOp\BitwiseXor $node): string protected function parseAssignOpBitwiseXor(Expr\AssignOp\BitwiseXor $node): string
{ {
return $this->parseAssignOp($node, '^='); return $this->parseAssignOp($node, '^=');
} }
@ -3534,24 +3539,24 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::eval("' . $this->escapeString($this->genEmbeddedCode($v)) . '");'; return 'php::eval("' . $this->escapeString($this->genEmbeddedCode($v)) . '");';
} }
protected function parseEval(Node\Expr\Eval_ $expr): string protected function parseEval(Expr\Eval_ $expr): string
{ {
return 'php::eval(' . $this->parseIdentifier($expr->expr) . ')'; return 'php::eval(' . $this->parseIdentifier($expr->expr) . ')';
} }
protected function parseInclude(Node\Expr\Include_ $expr): string protected function parseInclude(Expr\Include_ $expr): string
{ {
switch ($expr->type) { switch ($expr->type) {
case Node\Expr\Include_::TYPE_INCLUDE: case Expr\Include_::TYPE_INCLUDE:
$type = 'php::INCLUDE'; $type = 'php::INCLUDE';
break; break;
case Node\Expr\Include_::TYPE_INCLUDE_ONCE: case Expr\Include_::TYPE_INCLUDE_ONCE:
$type = 'php::INCLUDE_ONCE'; $type = 'php::INCLUDE_ONCE';
break; break;
case Node\Expr\Include_::TYPE_REQUIRE: case Expr\Include_::TYPE_REQUIRE:
$type = 'php::REQUIRE'; $type = 'php::REQUIRE';
break; break;
case Node\Expr\Include_::TYPE_REQUIRE_ONCE: case Expr\Include_::TYPE_REQUIRE_ONCE:
$type = 'php::REQUIRE_ONCE'; $type = 'php::REQUIRE_ONCE';
break; break;
default: default:
@ -3604,7 +3609,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return sprintf('%.' . $this->floatPrecision . 'g', $value); return sprintf('%.' . $this->floatPrecision . 'g', $value);
} }
protected function parseIsset(Node\Expr\Isset_ $expr): string protected function parseIsset(Expr\Isset_ $expr): string
{ {
$vars = $expr->vars; $vars = $expr->vars;
if (count($vars) > 1) { if (count($vars) > 1) {
@ -3617,7 +3622,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->parseChainedExpr($vars[0], self::OP_ISSET); return $this->parseChainedExpr($vars[0], self::OP_ISSET);
} }
protected function parseEmpty(Node\Expr\Empty_ $expr): string protected function parseEmpty(Expr\Empty_ $expr): string
{ {
return $this->parseChainedExpr($expr->expr, self::OP_EMPTY); return $this->parseChainedExpr($expr->expr, self::OP_EMPTY);
} }
@ -3636,11 +3641,14 @@ class CompilerBase extends \PhpAot\Core\Translator
{ {
$expr = $node; $expr = $node;
if ($this->isVarExpr($expr)) { if ($this->isVarExpr($expr)) {
if ($op === 'isset') { if ($op === self::OP_ISSET) {
$var = $this->parseIdentifier($expr); $var = $this->parseIdentifier($expr);
return $this->hasVar($var) ? 'php::exists(' . $var . ')' : 'false'; return $this->hasVar($var) ? 'php::exists(' . $var . ')' : 'false';
} elseif ($op === self::OP_NOT_EMPTY) {
return '!php::empty(' . $this->parseExpr($expr) . ')';
} else {
return 'php::empty(' . $this->parseExpr($expr) . ')';
} }
return 'php::' . $op . '(' . $this->parseExpr($expr) . ')';
} }
$list = []; $list = [];
@ -3665,19 +3673,24 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
$expr = $expr->var; $expr = $expr->var;
} }
$list = array_reverse($list); $list = array_reverse($list);
$fn = $op === 'isset' ? 'exists' : $op; $fn = match ($op) {
self::OP_ISSET => 'php::exists',
self::OP_NOT_EMPTY => '!php::empty',
default => 'php::empty',
};
if ($getValue) { if ($getValue) {
$result = $this->addTmpVar(self::TYPE_VAR); $result = $this->addTmpVar(self::TYPE_VAR);
$node->setAttribute('chainOpResult', $result); $node->setAttribute('chainOpResult', $result);
return 'php::' . $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')'; return $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')';
} else { } else {
return 'php::' . $fn . '(' . $var . ', {' . implode(', ', $list) . '})'; return $fn . '(' . $var . ', {' . implode(', ', $list) . '})';
} }
} }
protected function parseCastArray(Node\Expr\Cast\Array_ $expr): string protected function parseCastArray(Expr\Cast\Array_ $expr): string
{ {
return $this->convertArrayExpr($this->parseIdentifier($expr->expr)); return $this->convertArrayExpr($this->parseIdentifier($expr->expr));
} }
@ -3753,7 +3766,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->parseIdentifier($expr) . '.toReference()'; return $this->parseIdentifier($expr) . '.toReference()';
} }
protected function parseAssignRef(Node\Expr\AssignRef $expr): string protected function parseAssignRef(Expr\AssignRef $expr): string
{ {
if (!$this->isVarExpr($expr->var)) { if (!$this->isVarExpr($expr->var)) {
$this->fatalError($expr, 'Cannot assign reference to non-variable'); $this->fatalError($expr, 'Cannot assign reference to non-variable');
@ -3789,7 +3802,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $left . ' = ' . $tmpVar . '.toReference()'; return $left . ' = ' . $tmpVar . '.toReference()';
} }
protected function parseMethodCall(Node\Expr\MethodCall $expr): string protected function parseMethodCall(Expr\MethodCall $expr): string
{ {
$class = ''; $class = '';
$object = $this->parseIdentifier($expr->var); $object = $this->parseIdentifier($expr->var);
@ -3876,7 +3889,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
protected function parseStaticCall(Node\Expr\StaticCall $expr): string protected function parseStaticCall(Expr\StaticCall $expr): string
{ {
$self = false; $self = false;
$callScope = []; $callScope = [];
@ -3969,7 +3982,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
protected function findNativeStaticProperty(Node\Expr\StaticPropertyFetch $expr, ?string &$class): ?PropertyDef protected function findNativeStaticProperty(Expr\StaticPropertyFetch $expr, ?string &$class): ?PropertyDef
{ {
if ($this->isNameExpr($expr->class) and $this->isIdExpr($expr->name)) { if ($this->isNameExpr($expr->class) and $this->isIdExpr($expr->name)) {
$class = $this->parseIdentifier($expr->class); $class = $this->parseIdentifier($expr->class);
@ -4036,7 +4049,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return null; return null;
} }
protected function parseNativeStaticPropertyFetch(Node\Expr\StaticPropertyFetch $expr): string|bool protected function parseNativeStaticPropertyFetch(Expr\StaticPropertyFetch $expr): string|bool
{ {
$nativeProp = $this->findNativeStaticProperty($expr, $class); $nativeProp = $this->findNativeStaticProperty($expr, $class);
if ($nativeProp) { if ($nativeProp) {
@ -4047,7 +4060,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return false; return false;
} }
protected function parseStaticPropertyFetch(Node\Expr\StaticPropertyFetch $expr): string protected function parseStaticPropertyFetch(Expr\StaticPropertyFetch $expr): string
{ {
$native = $this->parseNativeStaticPropertyFetch($expr); $native = $this->parseNativeStaticPropertyFetch($expr);
if ($native) { if ($native) {
@ -4056,7 +4069,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::getStaticProperty(' . $this->identifierToStr($expr->class) . ', ' . $this->identifierToStr($expr->name) . ')'; return 'php::getStaticProperty(' . $this->identifierToStr($expr->class) . ', ' . $this->identifierToStr($expr->name) . ')';
} }
protected function parseClassConstFetch(Node\Expr\ClassConstFetch $expr): string protected function parseClassConstFetch(Expr\ClassConstFetch $expr): string
{ {
$class = $this->parseIdentifier($expr->class); $class = $this->parseIdentifier($expr->class);
$self = false; $self = false;
@ -4326,7 +4339,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code; return $code;
} }
protected function parseErrorSuppress(Node\Expr\ErrorSuppress $expr): string protected function parseErrorSuppress(Expr\ErrorSuppress $expr): string
{ {
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->context->beforeStmtLines[] = 'auto ' . $tmpVar . ' = EG(error_reporting);'; $this->context->beforeStmtLines[] = 'auto ' . $tmpVar . ' = EG(error_reporting);';
@ -4357,9 +4370,9 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function mustNoCall(NodeAbstract $node): void protected function mustNoCall(NodeAbstract $node): void
{ {
$nodeFinder = new NodeFinder(); $nodeFinder = new NodeFinder();
$r1 = $nodeFinder->findInstanceOf($node, Node\Expr\StaticCall::class); $r1 = $nodeFinder->findInstanceOf($node, Expr\StaticCall::class);
$r2 = $nodeFinder->findInstanceOf($node, Node\Expr\MethodCall::class); $r2 = $nodeFinder->findInstanceOf($node, Expr\MethodCall::class);
$r3 = $nodeFinder->findInstanceOf($node, Node\Expr\FuncCall::class); $r3 = $nodeFinder->findInstanceOf($node, Expr\FuncCall::class);
if (count($r1) + count($r2) + count($r3) > 0) { if (count($r1) + count($r2) + count($r3) > 0) {
$this->fatalError($node, 'Calling function or method is not allowed'); $this->fatalError($node, 'Calling function or method is not allowed');
} }
@ -4443,7 +4456,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code . "{$obj}.updateArrayProperty({$propName}, {$dim}, {$value})"; return $code . "{$obj}.updateArrayProperty({$propName}, {$dim}, {$value})";
} }
protected function parseParentMethodCall(Node\Expr\StaticCall $expr): string protected function parseParentMethodCall(Expr\StaticCall $expr): string
{ {
if (!$this->classDef->extends) { if (!$this->classDef->extends) {
$this->fatalError($expr, 'Cannot call parent method `' . $this->classDef->name . '::' . $this->parseIdentifier($expr->name) . '()` because class `' . $this->classDef->name . '` does not extend any class'); $this->fatalError($expr, 'Cannot call parent method `' . $this->classDef->name . '::' . $this->parseIdentifier($expr->name) . '()` because class `' . $this->classDef->name . '` does not extend any class');
@ -4503,7 +4516,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->printer->prettyPrint([$stmt]); return $this->printer->prettyPrint([$stmt]);
} }
protected function parseArrowFunction(Node\Expr\ArrowFunction $expr): string protected function parseArrowFunction(Expr\ArrowFunction $expr): string
{ {
$nodeFinder = new NodeFinder(); $nodeFinder = new NodeFinder();
$vars = $nodeFinder->findInstanceOf($expr->expr, Variable::class); $vars = $nodeFinder->findInstanceOf($expr->expr, Variable::class);
@ -4549,7 +4562,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->genClosure($expr, $expr->params, $cb, $uses); return $this->genClosure($expr, $expr->params, $cb, $uses);
} }
protected function parseClosure(Node\Expr\Closure $expr): string protected function parseClosure(Expr\Closure $expr): string
{ {
$cb = function () use ($expr) { $cb = function () use ($expr) {
$fnCode = $this->parseStmts($expr->stmts); $fnCode = $this->parseStmts($expr->stmts);
@ -4561,7 +4574,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->genClosure($expr, $expr->params, $cb, $expr->uses); return $this->genClosure($expr, $expr->params, $cb, $expr->uses);
} }
protected function parseAssignOpCoalesce(Node\Expr\AssignOp\Coalesce $expr): string protected function parseAssignOpCoalesce(Expr\AssignOp\Coalesce $expr): string
{ {
$this->checkLeftValue($expr->var); $this->checkLeftValue($expr->var);
$isset = $this->parseChainedExpr($expr->var, self::OP_ISSET); $isset = $this->parseChainedExpr($expr->var, self::OP_ISSET);
@ -4589,26 +4602,26 @@ class CompilerBase extends \PhpAot\Core\Translator
return $stmts[array_key_last($stmts)] instanceof Node\Stmt\Return_; return $stmts[array_key_last($stmts)] instanceof Node\Stmt\Return_;
} }
protected function parseNullsafePropertyFetch(Node\Expr\NullsafePropertyFetch $expr): string protected function parseNullsafePropertyFetch(Expr\NullsafePropertyFetch $expr): string
{ {
return $this->parseNullsafeExpr($expr); return $this->parseNullsafeExpr($expr);
} }
protected function parseNullsafeMethodCall(Node\Expr\NullsafeMethodCall $expr): string protected function parseNullsafeMethodCall(Expr\NullsafeMethodCall $expr): string
{ {
return $this->parseNullsafeExpr($expr); return $this->parseNullsafeExpr($expr);
} }
protected function parseNullsafeExpr(Node\Expr\NullsafePropertyFetch|Node\Expr\NullsafeMethodCall $expr): string protected function parseNullsafeExpr(Expr\NullsafePropertyFetch|Expr\NullsafeMethodCall $expr): string
{ {
$list = []; $list = [];
$comment = '// Nullsafe Operator: ' . $this->printer->prettyPrint([$expr]); $comment = '// Nullsafe Operator: ' . $this->printer->prettyPrint([$expr]);
while (1) { while (1) {
if ($expr instanceof Node\Expr\NullsafePropertyFetch) { if ($expr instanceof Expr\NullsafePropertyFetch) {
$list[] = ['property', $this->identifierToStr($expr->name, literal: true)]; $list[] = ['property', $this->identifierToStr($expr->name, literal: true)];
$expr = $expr->var; $expr = $expr->var;
} elseif ($expr instanceof Node\Expr\NullsafeMethodCall) { } elseif ($expr instanceof Expr\NullsafeMethodCall) {
$list[] = ['method', $this->identifierToStr($expr->name, literal: true), $expr->args]; $list[] = ['method', $this->identifierToStr($expr->name, literal: true), $expr->args];
$expr = $expr->var; $expr = $expr->var;
} else { } else {
@ -4659,7 +4672,7 @@ class CompilerBase extends \PhpAot\Core\Translator
/** /**
* 混杂数组赋值,需要拆分为多行插入 * 混杂数组赋值,需要拆分为多行插入
*/ */
private function parseArrayMixed(Node\Expr\Array_ $node): string private function parseArrayMixed(Expr\Array_ $node): string
{ {
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_ARRAY); $this->addLocalVar($tmpVar, self::TYPE_ARRAY);

@ -8,6 +8,10 @@
namespace PhpAot\Php; namespace PhpAot\Php;
/**
* @internal
* @coversNothing
*/
class CompilerTest extends Translator class CompilerTest extends Translator
{ {
public static function create(string $rootPath = ''): CompilerTest public static function create(string $rootPath = ''): CompilerTest

@ -0,0 +1,25 @@
--TEST--
Test ?: operator
--FILE--
<?php
function f($x)
{
printf("%s(%d)\n", __FUNCTION__, $x);
return $x;
}
function main() {
$a = f(0) ?: f(2);
var_dump($a);
$a = f(1) ?: f(2);
var_dump($a);
}
?>
--EXPECT--
f(0)
f(2)
int(2)
f(1)
int(1)
Loading…
Cancel
Save