diff --git a/examples/ref_prop.php b/examples/ref_prop.php new file mode 100644 index 00000000..e4701cb8 --- /dev/null +++ b/examples/ref_prop.php @@ -0,0 +1,7 @@ +a = 1999; +parse_str("c=2000", $o->a); +$prop = $o->a; +$prop = 111; +var_dump($o->a); diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 7df51191..89103e9c 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -1402,17 +1402,20 @@ class CompilerBase extends \PhpAot\Core\Translator if (!$this->hasVar($name)) { $this->addLocalVar($name, self::TYPE_REF); $this->beforeStmtLines[] = $name . ' = php::newReference();'; - } elseif (!$nativeFunction and $funcName) { - $funcArg = Reflection::getFunctionParameter($funcName, $i); + } elseif (!$nativeFunction and $funcName and Reflection::isReferenceArg($funcName, $i)) { // 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数 - if ($funcArg and $funcArg->isPassedByReference()) { - $tmpVar = $this->genTmpVarName(); - $this->addLocalVar($tmpVar, self::TYPE_REF); - $this->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();'; - $this->afterStmtLines[] = $name . ' = *' . $tmpVar . ';'; - $list_args[] = $tmpVar; - continue; - } + $tmpVar = $this->genTmpVarName(); + $this->addLocalVar($tmpVar, self::TYPE_REF); + $this->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();'; + $this->afterStmtLines[] = $name . ' = *' . $tmpVar . ';'; + $list_args[] = $tmpVar; + continue; + } + } elseif ($this->isPropertyFetch($arg->value)) { + if (!$nativeFunction and $funcName and Reflection::isReferenceArg($funcName, $i)) { + $obj = $this->parseIdentifier($arg->value->var); + $list_args[] = $obj . '.getPropertyReference(' . $this->identifierToStr($arg->value->name) . ')'; + continue; } } // 不支持变长参数展开的语法,例如:array_merge(...$arr) diff --git a/src/Php/Reflection.php b/src/Php/Reflection.php index 33c71eff..33bacb9e 100644 --- a/src/Php/Reflection.php +++ b/src/Php/Reflection.php @@ -52,4 +52,13 @@ class Reflection } return $args[$index]; } + + public static function isReferenceArg(string $fn, int $index): ?string + { + $param = self::getFunctionParameter($fn, $index); + if (!$param) { + return null; + } + return $param->isPassedByReference() ? $param->getName() : null; + } } diff --git a/tests/core/classes/iterators_006.phpt b/tests/core/classes/iterators_006.phpt new file mode 100644 index 00000000..1cb34097 --- /dev/null +++ b/tests/core/classes/iterators_006.phpt @@ -0,0 +1,85 @@ +--TEST-- +ZE2 iterators and array wrapping +--FILE-- +array = array('foo', 'bar', 'baz'); + } + + function rewind(): void { + reset($this->array); + $this->next(); + } + + function valid(): bool { + return $this->key !== NULL; + } + + function key(): mixed { + return $this->key; + } + + function current(): mixed { + return $this->current; + } + + function next(): void { + $this->key = key($this->array); + $this->current = current($this->array); + next($this->array); + } +} + +class a implements IteratorAggregate { + + public function getIterator(): Traversable { + return new ai(); + } +} + +function main() { + $array = new a(); + + foreach ($array as $property => $value) { + print "$property: $value\n"; + } + + #$array = $array->getIterator(); + #$array->rewind(); + #$array->valid(); + #var_dump($array->key()); + #var_dump($array->current()); + echo "===2nd===\n"; + + $array = new ai(); + + foreach ($array as $property => $value) { + print "$property: $value\n"; + } + + echo "===3rd===\n"; + + foreach ($array as $property => $value) { + print "$property: $value\n"; + } +} +?> +--EXPECT-- +0: foo +1: bar +2: baz +===2nd=== +0: foo +1: bar +2: baz +===3rd=== +0: foo +1: bar +2: baz diff --git a/tests/core/classes/iterators_007.phpt b/tests/core/classes/iterators_007.phpt new file mode 100644 index 00000000..5c4a8e35 --- /dev/null +++ b/tests/core/classes/iterators_007.phpt @@ -0,0 +1,41 @@ +--TEST-- +ZE2 iterators and exceptions +--FILE-- +x == 0) throw new Exception(__METHOD__); reset($this->arr); } + public function current(): mixed { if ($this->x == 1) throw new Exception(__METHOD__); return current($this->arr); } + public function key(): mixed { if ($this->x == 2) throw new Exception(__METHOD__); return key($this->arr); } + public function next(): void { if ($this->x == 3) throw new Exception(__METHOD__); next($this->arr); } + public function valid(): bool { if ($this->x == 4) throw new Exception(__METHOD__); return (key($this->arr) !== NULL); } +} +function main() { + $t = new Test(); + while($t->x < 5) + { + try + { + foreach($t as $k => $v) + { + echo "Current\n"; + } + } + catch(Exception $e) + { + echo "Caught in " . $e->getMessage() . "()\n"; + } + $t->x++; + } +} +?> +--EXPECT-- +Caught in Test::rewind() +Caught in Test::current() +Caught in Test::key() +Current +Caught in Test::next() +Caught in Test::valid()