From 715abf458d316a5615212deddc3aadba6b5703b5 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 15 Jan 2026 18:30:03 +0800 Subject: [PATCH] =?UTF-8?q?refactor(php):=20=E9=87=8D=E6=9E=84=20foreach?= =?UTF-8?q?=20=E5=BE=AA=E7=8E=AF=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E5=AF=B9=E8=B1=A1=E8=BF=AD=E4=BB=A3?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将原 parseForeach 方法拆分为 parseForeach 和 parseForeachArray 两个方法 - 添加对 Iterator 和 IteratorAggregate 接口的支持 - 实现对象属性遍历的备用方案,使用 get_object_vars 函数 - 添加新的测试用例 iterators_004.phpt 验证迭代器功能 - 添加新的测试用例 iterators_005.phpt 验证 Traversable 接口实现检查 - 为对象迭代创建临时变量并设置正确的类型标识 --- src/Php/CompilerBase.php | 55 ++++++++++++++------------ src/Php/Translator.php | 7 ++++ tests/core/classes/iterators_004.phpt | 56 +++++++++++++++++++++++++++ tests/core/classes/iterators_005.phpt | 20 ++++++++++ 4 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 tests/core/classes/iterators_004.phpt create mode 100644 tests/core/classes/iterators_005.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 0402f4bd..7df51191 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -1980,35 +1980,13 @@ class CompilerBase extends \PhpAot\Core\Translator } } - protected function parseForeach(Foreach_ $node): string + protected function parseForeachArray(Foreach_ $node, string $iteratorVar): string { - if ($node->byRef) { - $this->fatalError($node, 'Cannot use & with foreach'); - } - if ($this->isVarExpr($node->expr)) { - $name = $this->parseIdentifier($node->expr); - if ($this->hasVar($name)) { - $type = $this->getVarType($name); - if ($type === self::TYPE_OBJECT) { - return $this->parseForeachObject($node); - } - } - } - - $iteratorVar = $this->genTmpVarName(); - - $stmts = $node->stmts; - $code = ''; if ($node->keyVar) { $keyVar = $this->parseIdentifier($node->keyVar); } - $valueVar = $this->parseIdentifier($node->valueVar); - - $expr = $this->parseIdentifier($node->expr); - $code .= self::TYPE_ARRAY . " $iteratorVar = " . $expr . ';' . PHP_EOL; - $code .= $this->parseBeforeStmtLines() . PHP_EOL; - $code .= 'for (auto iter = ' . $iteratorVar . '.begin(); iter != ' . $iteratorVar . '.end(); ++iter) {' . PHP_EOL; + $code = 'for (auto iter = ' . $iteratorVar . '.begin(); iter != ' . $iteratorVar . '.end(); ++iter) {' . PHP_EOL; $this->indentLevel++; if ($node->keyVar) { $code .= self::TYPE_VAR . ' ' . $this->getIndent() . ' ' . $keyVar . ' = iter.key();' . PHP_EOL; @@ -2022,9 +2000,10 @@ class CompilerBase extends \PhpAot\Core\Translator $dim = $this->parseIdentifier($node->valueVar->dim); $code .= $this->getIndent() . "$array.offsetSet($dim, iter.value());"; } else { + $valueVar = $this->parseIdentifier($node->valueVar); $code .= self::TYPE_VAR . ' ' . $this->getIndent() . ' ' . $valueVar . ' = iter.value();' . PHP_EOL; } - $code .= $this->parseStmts($stmts); + $code .= $this->parseStmts($node->stmts); $this->indentLevel--; $code .= $this->getIndent() . '}'; @@ -2032,6 +2011,32 @@ class CompilerBase extends \PhpAot\Core\Translator return $code; } + protected function parseForeach(Foreach_ $node): string + { + if ($node->byRef) { + $this->fatalError($node, 'Cannot use & with foreach'); + } + if ($this->isVarExpr($node->expr)) { + $name = $this->parseIdentifier($node->expr); + if ($this->hasVar($name)) { + $type = $this->getVarType($name); + if ($type === self::TYPE_OBJECT) { + return $this->parseForeachObject($node); + } + } + } + + $iteratorVar = $this->genTmpVarName(); + + $code = ''; + $expr = $this->parseIdentifier($node->expr); + $code .= self::TYPE_ARRAY . " $iteratorVar = " . $expr . ';' . PHP_EOL; + $code .= $this->parseBeforeStmtLines() . PHP_EOL; + $code .= $this->parseForeachArray($node, $iteratorVar); + + return $code; + } + protected function formatCppCode(string $file): void { $cmd = 'cd ' . $this->rootPath . ' && clang-format -i ' . $file; diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 9a1ccbc8..8aba168d 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -819,6 +819,10 @@ class Translator extends Preprocessor $obj = $this->parseIdentifier($node->expr); $tmpVar = $this->genTmpVarName(); $this->addLocalVar($tmpVar, self::TYPE_OBJECT); + + $tmpArrayVar = $this->genTmpVarName(); + $this->addLocalVar($tmpArrayVar, self::TYPE_ARRAY); + $code = 'if (' . $obj . '.instanceOf("IteratorAggregate")) {' . PHP_EOL; $code .= $this->getIndent() . $tmpVar . ' = ' . $obj . '.exec("getIterator");' . PHP_EOL . '}' . PHP_EOL; $code .= 'else if (' . $obj . '.instanceOf("Iterator")) {' . PHP_EOL; @@ -839,6 +843,9 @@ class Translator extends Preprocessor $code .= $this->parseStmts($node->stmts); $code .= '}' . PHP_EOL; $this->indentLevel--; + $code .= $this->getIndent() . '} else {' . PHP_EOL; + $code .= $this->getIndent() . $tmpArrayVar . ' = php::call("get_object_vars", {' . $obj . '});' . PHP_EOL; + $code .= $this->parseForeachArray($node, $tmpArrayVar); $this->indentLevel--; $code .= '}' . PHP_EOL; diff --git a/tests/core/classes/iterators_004.phpt b/tests/core/classes/iterators_004.phpt new file mode 100644 index 00000000..857afdd9 --- /dev/null +++ b/tests/core/classes/iterators_004.phpt @@ -0,0 +1,56 @@ +--TEST-- +ZE2 iterators must be implemented +--FILE-- +num; + } + function next(): void { + echo __METHOD__ . "\n"; + $this->num++; + } + function valid(): bool { + echo __METHOD__ . "\n"; + return $this->num < $this->max; + } + function key(): mixed { + echo __METHOD__ . "\n"; + switch($this->num) { + case 0: return "1st"; + case 1: return "2nd"; + case 2: return "3rd"; + default: return "???"; + } + } +} +function main() { + echo "1st try\n"; + $obj = new c1(); + + foreach($obj as $w) { + echo "object:$w\n"; + } + + echo "2nd try\n"; + $obj = new c2(); + + foreach($obj as $v => $w) { + echo "object:$v=>$w\n"; + } + + print "Done\n"; +} +?> +--EXPECT-- +1st try +2nd try +object:max=>3 +object:num=>0 +Done diff --git a/tests/core/classes/iterators_005.phpt b/tests/core/classes/iterators_005.phpt new file mode 100644 index 00000000..f9997868 --- /dev/null +++ b/tests/core/classes/iterators_005.phpt @@ -0,0 +1,20 @@ +--TEST-- +ZE2 iterators cannot implement Traversable alone +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Class test must implement interface Traversable as part of either Iterator or IteratorAggregate in %s on line %d