From ba354c62c8304fc19f2508a5376a7e08dfb631c3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 7 Jul 2026 18:14:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=A9=BA=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E9=93=BE=E4=B8=AD=E6=99=AE=E9=80=9A=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E8=AF=AF=E5=88=A4=E4=B8=BA=E7=A9=BA=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Php/CompilerBase.php | 41 +++++++++++++++++-- ...lsafe-normal-method-after-null-return.phpt | 35 ++++++++++++++++ 2 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 tests/aot/nullsafe/nullsafe-normal-method-after-null-return.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 4b02ea8e..3b3cf0cd 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -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 = []; diff --git a/tests/aot/nullsafe/nullsafe-normal-method-after-null-return.phpt b/tests/aot/nullsafe/nullsafe-normal-method-after-null-return.phpt new file mode 100644 index 00000000..9d47a448 --- /dev/null +++ b/tests/aot/nullsafe/nullsafe-normal-method-after-null-return.phpt @@ -0,0 +1,35 @@ +--TEST-- +Nullsafe chain does not suppress normal method call on null return value +--FILE-- +next()->value()); + echo "not caught\n"; + } catch (Error $e) { + echo "caught\n"; + } +} +?> +--EXPECT-- +caught