fix: 修复空安全链中普通方法调用误判为空安全的问题

pull/14/head
韩天峰 2 months ago
parent b1dcaaa37e
commit ba354c62c8
  1. 41
      src/Php/CompilerBase.php
  2. 35
      tests/aot/nullsafe/nullsafe-normal-method-after-null-return.phpt

@ -5161,6 +5161,10 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
protected function parsePropertyFetch(Expr\PropertyFetch $expr): string
{
if ($this->containsNullsafeChain($expr->var)) {
return $this->parseNullsafeExpr($expr);
}
$update = $this->isPropertyFetchUpdate($expr);
$object = $expr->var;
$property = $expr->name;
@ -6101,6 +6105,10 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
protected function parseMethodCall(Expr\MethodCall $expr): string
{
if ($this->containsNullsafeChain($expr->var)) {
return $this->parseNullsafeExpr($expr);
}
$class = '';
$object = $this->parseIdentifier($expr->var);
if ($this->isVarExpr($expr->var)) {
@ -7535,17 +7543,25 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
return $this->parseNullsafeExpr($expr);
}
protected function parseNullsafeExpr(Expr\NullsafePropertyFetch|Expr\NullsafeMethodCall $expr): string
protected function parseNullsafeExpr(
Expr\PropertyFetch|Expr\MethodCall|Expr\NullsafePropertyFetch|Expr\NullsafeMethodCall $expr
): string
{
$list = [];
$comment = '// Nullsafe Operator: ' . $this->printer->prettyPrint([$expr]);
while (1) {
if ($expr instanceof Expr\NullsafePropertyFetch) {
$list[] = ['property', $this->identifierToStr($expr->name, literal: true), $expr];
$list[] = ['property', $this->identifierToStr($expr->name, literal: true), $expr, true];
$expr = $expr->var;
} 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, true];
$expr = $expr->var;
} elseif ($expr instanceof Expr\PropertyFetch) {
$list[] = ['property', $this->identifierToStr($expr->name, literal: true), $expr, false];
$expr = $expr->var;
} elseif ($expr instanceof Expr\MethodCall) {
$list[] = ['method', $this->identifierToStr($expr->name, literal: true), $expr->args, false];
$expr = $expr->var;
} else {
if ($this->isVarExpr($expr)) {
@ -7573,7 +7589,9 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
foreach ($list as $key => $item) {
$tmpVar = $this->addTmpVar($key !== $last ? self::TYPE_OBJECT : self::TYPE_VAR);
$code .= "if ({$object}.isNull()) { return " . self::VALUE_NULL . '; }';
if ($item[3]) {
$code .= "if ({$object}.isNull()) { return " . self::VALUE_NULL . '; }';
}
if ($item[0] == 'property') {
$update = $this->escapeBool($this->isPropertyFetchUpdate($item[2]));
$code .= $this->getIndent() . "{$tmpVar} = {$object}.attr({$item[1]}, {$update});";
@ -7600,6 +7618,21 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
return "{$tmpFn}()";
}
private function containsNullsafeChain(NodeAbstract $expr): bool
{
while ($expr instanceof Expr\PropertyFetch
|| $expr instanceof Expr\MethodCall
|| $expr instanceof Expr\NullsafePropertyFetch
|| $expr instanceof Expr\NullsafeMethodCall) {
if ($expr instanceof Expr\NullsafePropertyFetch || $expr instanceof Expr\NullsafeMethodCall) {
return true;
}
$expr = $expr->var;
}
return false;
}
private function checkNullsafePropertyAccesses(NodeAbstract $baseExpr, array $list): void
{
$properties = [];

@ -0,0 +1,35 @@
--TEST--
Nullsafe chain does not suppress normal method call on null return value
--FILE--
<?php
class NullsafeNormalChainRoot
{
public function next(): ?NullsafeNormalChainLeaf
{
return null;
}
}
class NullsafeNormalChainLeaf
{
public function value(): string
{
return 'value';
}
}
function main(): void
{
$root = new NullsafeNormalChainRoot();
try {
var_dump($root?->next()->value());
echo "not caught\n";
} catch (Error $e) {
echo "caught\n";
}
}
?>
--EXPECT--
caught
Loading…
Cancel
Save