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