diff --git a/ext/spl/ArrayObjectBuiltin.php b/ext/spl/ArrayObjectBuiltin.php index 4c74e9abb48..c97f1869a6b 100644 --- a/ext/spl/ArrayObjectBuiltin.php +++ b/ext/spl/ArrayObjectBuiltin.php @@ -206,9 +206,6 @@ public function execute(Frame $frame): void 0, 'iteratorClass' ); - if (null === $frame->returnVar) { - return; - } $entry = VmReflection::resolveClassEntry($frame->vmContext, $iteratorClass); if (null === $entry || !InterfaceCheck::entryIsInstanceOf($entry, ArrayIteratorBuiltin::CLASS_LC, $frame->vmContext)) { diff --git a/lib/Compiler.php b/lib/Compiler.php index 694e95242fc..9aab4a631ba 100755 --- a/lib/Compiler.php +++ b/lib/Compiler.php @@ -12778,7 +12778,41 @@ private function precedingInlineCallArgProducersBeforeCfgOp(array $cfgChildren, array_unshift($producers, $child); } - return $producers; + return $this->filterDeadVoidStatementMethodCallProducers($producers); + } + + /** + * Drop void statement MethodCalls before a sibling MethodCall inline call-arg (#10778). + * + * php-cfg: `$ao->setIteratorClass('X'); echo var_export($ao->getIteratorClass(), true);` + * hoists both MethodCalls; the void setter must not map to var_export arg 0. + * + * @param list $producers + * + * @return list + */ + private function filterDeadVoidStatementMethodCallProducers(array $producers): array + { + if (\count($producers) < 2) { + return $producers; + } + $filtered = []; + $count = \count($producers); + for ($i = 0; $i < $count; ++$i) { + $producer = $producers[$i]; + if ( + $producer instanceof Op\Expr\MethodCall + && property_exists($producer, 'result') + && empty($producer->result->usages) + && $i + 1 < $count + && $producers[$i + 1] instanceof Op\Expr\MethodCall + ) { + continue; + } + $filtered[] = $producer; + } + + return $filtered; } /** diff --git a/test/compliance/cases/stdlib/arrayobject_getiterator_after_set.phpt b/test/compliance/cases/stdlib/arrayobject_getiterator_after_set.phpt new file mode 100644 index 00000000000..ff23f9a74ba --- /dev/null +++ b/test/compliance/cases/stdlib/arrayobject_getiterator_after_set.phpt @@ -0,0 +1,14 @@ +--TEST-- +stdlib ArrayObject::getIteratorClass() after void setIteratorClass() in var_export expr (#10778, ext/spl/spl_array.c) +--FILE-- +setIteratorClass('ArrayIterator'); +echo var_export($ao->getIteratorClass(), true), "\n"; +if ('ArrayIterator' !== $ao->getIteratorClass()) { + exit(1); +} +?> +--EXPECT-- +'ArrayIterator' diff --git a/test/repro/maintainer_gap_arrayobject_getiterator_after_set.php b/test/repro/maintainer_gap_arrayobject_getiterator_after_set.php new file mode 100644 index 00000000000..b970354eef1 --- /dev/null +++ b/test/repro/maintainer_gap_arrayobject_getiterator_after_set.php @@ -0,0 +1,8 @@ +setIteratorClass('ArrayIterator'); +echo var_export($ao->getIteratorClass(), true), "\n"; +if ('ArrayIterator' !== $ao->getIteratorClass()) { + exit(1); +}