From 0da07cb06505af8dd665ecdbe18975417220c2ef Mon Sep 17 00:00:00 2001 From: Yurun Date: Wed, 15 Jul 2026 19:06:49 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(compiler):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=AF=B9=E6=9C=AA=E5=AE=9A=E4=B9=89=E5=8F=98=E9=87=8F=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20=3F=3F=3D=20=E6=97=B6=E7=9A=84=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Parser/AssignOpTrait.php | 14 +++++++++++--- .../coalesce/assign-coalesce-undefined-var.phpt | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests/compiler/coalesce/assign-coalesce-undefined-var.phpt diff --git a/src/Parser/AssignOpTrait.php b/src/Parser/AssignOpTrait.php index cf826761..826cd011 100644 --- a/src/Parser/AssignOpTrait.php +++ b/src/Parser/AssignOpTrait.php @@ -862,6 +862,17 @@ trait AssignOpTrait protected function parseAssignOpCoalesce(Expr\AssignOp\Coalesce $expr): string { $this->checkLeftValue($expr->var); + + // PHP 允许对未定义的简单变量使用 ??=(例如 `$a ??= 123`): + // 此时 isset 为 false,直接执行赋值。需要提前声明该局部变量, + // 否则 isset 检查会因变量未定义而报错。此处必须声明为 Type::VAR + // (Variant),使其初值为 NULL,从而 isset 在运行时正确判定为 + // false 并执行赋值;若使用原生类型,isset 恒为 true 会导致取到 + // 未初始化的默认值(如 int(0)、空字符串)。 + if ($this->isVarExpr($expr->var) and !$this->hasVar($this->parseIdentifier($expr->var))) { + $this->addLocalVar($this->parseIdentifier($expr->var), Type::VAR); + } + $isset = $this->parseChainedExpr($expr->var, self::OP_ISSET); $var = $this->parseWritableIdentifier($expr->var); @@ -878,9 +889,6 @@ trait AssignOpTrait if ($this->isVarExpr($expr->expr) and !$this->hasVar($right)) { $this->errorUndefinedVariable($expr->expr); } - if ($this->isVarExpr($expr->var) and !$this->hasVar($var)) { - $this->addLocalVar($var, $this->getNormalAssignType($this->detectTypeOfExpr($expr->expr))); - } return '(' . $isset . '?' . $var . ':(' . $var . ' = ' . $right . '))'; } diff --git a/tests/compiler/coalesce/assign-coalesce-undefined-var.phpt b/tests/compiler/coalesce/assign-coalesce-undefined-var.phpt new file mode 100644 index 00000000..4aad8edb --- /dev/null +++ b/tests/compiler/coalesce/assign-coalesce-undefined-var.phpt @@ -0,0 +1,14 @@ +--TEST-- +assign coalesce on undefined variable +--FILE-- + +--EXPECT-- +int(123) +string(3) "foo" -- 2.34.1 From 8b76f841adad95c52fb3dfb868a4812889098d87 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 19 Jul 2026 10:49:57 +0800 Subject: [PATCH 2/2] test(compiler): strengthen undefined coalesce assignment coverage --- src/Parser/AssignOpTrait.php | 15 ++++++--------- .../coalesce/assign-coalesce-undefined-var.phpt | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Parser/AssignOpTrait.php b/src/Parser/AssignOpTrait.php index 826cd011..dabef684 100644 --- a/src/Parser/AssignOpTrait.php +++ b/src/Parser/AssignOpTrait.php @@ -863,19 +863,16 @@ trait AssignOpTrait { $this->checkLeftValue($expr->var); - // PHP 允许对未定义的简单变量使用 ??=(例如 `$a ??= 123`): - // 此时 isset 为 false,直接执行赋值。需要提前声明该局部变量, - // 否则 isset 检查会因变量未定义而报错。此处必须声明为 Type::VAR - // (Variant),使其初值为 NULL,从而 isset 在运行时正确判定为 - // false 并执行赋值;若使用原生类型,isset 恒为 true 会导致取到 - // 未初始化的默认值(如 int(0)、空字符串)。 - if ($this->isVarExpr($expr->var) and !$this->hasVar($this->parseIdentifier($expr->var))) { - $this->addLocalVar($this->parseIdentifier($expr->var), Type::VAR); + // An undefined variable must exist before generating its isset check. + // Keep it as Variant so NULL remains distinguishable from native defaults. + $var = $this->isVarExpr($expr->var) ? $this->parseIdentifier($expr->var) : null; + if ($var !== null && !$this->hasVar($var)) { + $this->addLocalVar($var, Type::VAR); } $isset = $this->parseChainedExpr($expr->var, self::OP_ISSET); - $var = $this->parseWritableIdentifier($expr->var); + $var ??= $this->parseWritableIdentifier($expr->var); $propertyWriteTarget = $this->preparePropertyWriteTarget($expr->var); if ($propertyWriteTarget !== null) { diff --git a/tests/compiler/coalesce/assign-coalesce-undefined-var.phpt b/tests/compiler/coalesce/assign-coalesce-undefined-var.phpt index 4aad8edb..27dd886b 100644 --- a/tests/compiler/coalesce/assign-coalesce-undefined-var.phpt +++ b/tests/compiler/coalesce/assign-coalesce-undefined-var.phpt @@ -8,7 +8,21 @@ var_dump($a); $b ??= 'foo'; $b ??= 'bar'; var_dump($b); + +$c ??= null; +var_dump(isset($c)); +$c ??= 'after-null'; +var_dump($c); + +for ($i = 0; $i < 2; $i++) { + $d ??= printf("default\n"); +} +var_dump($d); ?> --EXPECT-- int(123) string(3) "foo" +bool(false) +string(10) "after-null" +default +int(8) -- 2.34.1