From 324bf532bce242046d7e2d71ed69c2c2c8a143d2 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 13 Jul 2026 15:26:23 +0800 Subject: [PATCH] test(compiler): add foreach array snapshot stability test case - Add test case for foreach by value behavior when current element is removed - Verify that foreach maintains stable array snapshot during iteration - Confirm expected output matches array traversal before modifications - Document behavior where unset operations don't affect ongoing loop refactor(parser): simplify foreach array handling logic - Remove dedicated parseForeachArray method from ForeachTrait - Consolidate array iteration to use existing parseForeachIterable method - Maintain same functional behavior while reducing code duplication - Update type checking logic to route arrays --- src/Parser/ForeachTrait.php | 21 +------------- .../compiler/loop/foreach-unset-current.phpt | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 tests/compiler/loop/foreach-unset-current.phpt diff --git a/src/Parser/ForeachTrait.php b/src/Parser/ForeachTrait.php index df1c990f..f51e5a0b 100644 --- a/src/Parser/ForeachTrait.php +++ b/src/Parser/ForeachTrait.php @@ -107,25 +107,6 @@ trait ForeachTrait return $this->getIndent() . ' ' . $valueVar . ' = ' . $valueExpr . ';' . PHP_EOL; } - protected function parseForeachArray(Foreach_ $node, string $iteratorVar): string - { - $tmpVar = $this->genTmpVarName(); - $code = "for (auto $tmpVar = $iteratorVar.begin(); $tmpVar != $iteratorVar.end(); ++$tmpVar) {" . PHP_EOL; - $this->indentLevel++; - $code .= $this->parseForeachKeyAssignment($node, $tmpVar . '.key()'); - $code .= $this->parseForeachValueAssignment($node, $tmpVar . '.value()', $tmpVar . '.valueRef()'); - - $body = $this->parseForeachBody($node); - $this->indentLevel--; - - $code .= $this->parseBeforeStmtLines() . PHP_EOL; - $code .= $body . PHP_EOL; - - $code .= $this->getIndent() . '}'; - - return $code; - } - protected function parseForeachIterable(Foreach_ $node, string $iterableVar): string { $iterator = $this->genTmpVarName(); @@ -158,7 +139,7 @@ trait ForeachTrait if ($this->hasVar($name)) { $type = $this->getVarType($name); if ($type === Type::ARRAY) { - return $this->parseForeachArray($node, $name); + return $this->parseForeachIterable($node, $name); } elseif ($type === Type::OBJECT) { if ($node->byRef) { $this->fatalError($node, 'Cannot use & with foreach'); diff --git a/tests/compiler/loop/foreach-unset-current.phpt b/tests/compiler/loop/foreach-unset-current.phpt new file mode 100644 index 00000000..b28f3fc6 --- /dev/null +++ b/tests/compiler/loop/foreach-unset-current.phpt @@ -0,0 +1,29 @@ +--TEST-- +foreach by value keeps a stable array snapshot when the current element is removed +--FILE-- + 1, 'b' => 2, 'c' => 3]; + $seen = []; + + foreach ($values as $key => $value) { + $seen[] = $key . ':' . $value; + unset($values[$key]); + } + + var_dump($seen); + var_dump($values); +} +?> +--EXPECT-- +array(3) { + [0]=> + string(3) "a:1" + [1]=> + string(3) "b:2" + [2]=> + string(3) "c:3" +} +array(0) { +}