feat(compiler): 实现空合并运算符的编译支持

- 添加了对 ?? 运算符的完整编译支持
- 实现了变量存在性检查逻辑
- 添加了链式表达式解析功能
- 集成了测试用例验证功能
- 优化了错误处理机制
- 完善了代码生成流程
pull/1/head
韩天峰 5 months ago
parent cfcbfcdb2f
commit fdf7eb9765
  1. 44
      src/Php/CompilerBase.php
  2. 4
      src/Php/Generator/ClosureGenerator.php
  3. 66
      tests/aot/coalesce/001.phpt
  4. 23
      tests/aot/coalesce/002.phpt

@ -2920,9 +2920,21 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseBinaryOpCoalesce(Node\Expr\BinaryOp\Coalesce $expr): string
{
$left = $this->parseIdentifier($expr->left);
if ($this->isVarExpr($expr->left)) {
$this->checkVarMustExist($expr->left, $left);
}
$this->mustNoCall($expr->right);
$right = $this->parseIdentifier($expr->right);
$this->checkVarMustExist($expr->right, $right);
$isset = $this->parseChainedExpr($expr->left, self::OP_ISSET, true);
$chainOpResult = $expr->left->getAttribute('chainOpResult');
if ($chainOpResult) {
$left = $chainOpResult;
}
return $this->parseChainedExpr($expr->left, 'isset') . ' ? ' . $left . ' : ' . $right;
return $isset . ' ? ' . $left . ' : ' . $right;
}
protected function parseBinaryOpNotIdentical(Node\Expr\BinaryOp $expr): string
@ -3597,8 +3609,9 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
protected function parseChainedExpr(NodeAbstract $expr, string $op): string
protected function parseChainedExpr(NodeAbstract $node, string $op, bool $getValue = false): string
{
$expr = $node;
if ($this->isVarExpr($expr)) {
if ($op === 'isset') {
$var = $this->parseIdentifier($expr);
@ -3631,7 +3644,14 @@ class CompilerBase extends \PhpAot\Core\Translator
}
$list = array_reverse($list);
$fn = $op === 'isset' ? 'exists' : $op;
return 'php::' . $fn . '(' . $var . ', {' . implode(', ', $list) . '})';
if ($getValue) {
$result = $this->addTmpVar(self::TYPE_VAR);
$node->setAttribute('chainOpResult', $result);
return 'php::' . $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')';
} else {
return 'php::' . $fn . '(' . $var . ', {' . implode(', ', $list) . '})';
}
}
protected function parseCastArray(Node\Expr\Cast\Array_ $expr): string
@ -4295,6 +4315,24 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
protected function checkVarMustExist(NodeAbstract $node, string $name): void
{
if ($this->isVarExpr($node) and !$this->hasVar($name)) {
$this->errorUndefinedVariable($node);
}
}
protected function mustNoCall(NodeAbstract $node): void
{
$nodeFinder = new NodeFinder();
$r1 = $nodeFinder->findInstanceOf($node, Node\Expr\StaticCall::class);
$r2 = $nodeFinder->findInstanceOf($node, Node\Expr\MethodCall::class);
$r3 = $nodeFinder->findInstanceOf($node, Node\Expr\FuncCall::class);
if (count($r1) + count($r2) + count($r3) > 0) {
$this->fatalError($node, 'Calling function or method is not allowed');
}
}
protected function checkAccessible(ClassDef $classDef, int $flags): bool
{
// 在当前类中,允许调用所有方法

@ -78,9 +78,7 @@ trait ClosureGenerator
}
$useVars[] = $this->convertToRef($useItem->var);
} else {
if (!$this->hasVar($var)) {
$this->errorUndefinedVariable($useItem->var);
}
$this->checkVarMustExist($useItem->var, $var);
$useVars[] = $var;
}
}

@ -0,0 +1,66 @@
--TEST--
Test ?? operator
--FILE--
<?php
function foobar() {
echo "called\n";
return ['a'];
}
function main() {
$var = 7;
$var2 = NULL;
$obj = new StdClass;
$obj->boo = 7;
$arr = [
2 => 7,
"foo" => "bar",
"foobar" => NULL,
"qux" => $obj,
"bing" => [
"bang"
]
];
var_dump($var ?? 3);
var_dump($var2 ?? 3);
echo PHP_EOL;
var_dump($obj->boo ?? 3);
var_dump($obj->bing ?? 3);
var_dump($arr["qux"]->boo ?? 3);
var_dump($arr["qux"]->bing ?? 3);
echo PHP_EOL;
var_dump($arr[2] ?? 3);
var_dump($arr["foo"] ?? 3);
var_dump($arr["foobar"] ?? 3);
var_dump($arr["qux"] ?? 3);
var_dump($arr["bing"][0] ?? 3);
var_dump($arr["bing"][1] ?? 3);
echo PHP_EOL;
var_dump(foobar()[0] ?? false);
}
?>
--EXPECTF--
int(7)
int(3)
int(7)
int(3)
int(7)
int(3)
int(7)
string(3) "bar"
int(3)
object(stdClass)#%d (%d) {
["boo"]=>
int(7)
}
string(4) "bang"
int(3)
called
string(1) "a"

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