diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 561675f5..3e08ec31 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -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 { // 在当前类中,允许调用所有方法 diff --git a/src/Php/Generator/ClosureGenerator.php b/src/Php/Generator/ClosureGenerator.php index 811684b3..c9c69628 100644 --- a/src/Php/Generator/ClosureGenerator.php +++ b/src/Php/Generator/ClosureGenerator.php @@ -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; } } diff --git a/tests/aot/coalesce/001.phpt b/tests/aot/coalesce/001.phpt new file mode 100644 index 00000000..03e40b6f --- /dev/null +++ b/tests/aot/coalesce/001.phpt @@ -0,0 +1,66 @@ +--TEST-- +Test ?? operator +--FILE-- +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" \ No newline at end of file diff --git a/tests/aot/coalesce/002.phpt b/tests/aot/coalesce/002.phpt new file mode 100644 index 00000000..28ac8a0e --- /dev/null +++ b/tests/aot/coalesce/002.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test ?? operator +--FILE-- + +--EXPECTF-- +f(1) +f(2) +f(0) +int(1)