Repository navigation
Stdlib: IteratorIterator / RegexIterator — SPL iterator adapters missing (ext/spl/spl_iterators.c) #6138
Copy link
Copy link
Closed
Labels
area:vmVirtual machineVirtual machineenhancementNew feature or requestNew feature or requestimplementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claimSpec complete: repro, php-src ref, done-when — safe for workers to claimphase-4:stdlibPhase 4 – stdlib for web appsPhase 4 – stdlib for web apps
Description
Category
stdlib
Problem
Zend registers IteratorIterator (wrap any Traversable as Iterator) and RegexIterator (filter iterator elements by PCRE). These are among the most-used SPL adapters — e.g. new IteratorIterator($generator) and new RegexIterator($it, '/^foo/'). This compiler has partial SPL skeletons (#3330, #4769) but neither class exists (class_exists → false, Jun 2026 parity audit).
#4546 tracks LimitIterator/FilterIterator/etc.; this issue covers the adapter pair specifically because they unblock foreach over foreign iterators and regex-filtered walks.
php-src reference
ext/spl/spl_iterators.c—spl_ce_RegexIterator,spl_ce_IteratorIteratorext/spl/spl_iterators.stub.php—RegexIterator::MATCH,GET_MATCH,USE_KEY, …Zend/zend_interfaces.c—Iterator/Traversablechecks
Repro A — IteratorIterator
<?php
function gen(): Generator { yield 'a'; yield 'b'; }
$it = new IteratorIterator(gen());
foreach ($it as $v) { echo $v; }
echo "\n";Repro B — RegexIterator
<?php
$base = new ArrayIterator(['foo', 'bar', 'food']);
$it = new RegexIterator($base, '/^foo/', RegexIterator::MATCH);
foreach ($it as $v) { echo $v, "\n"; }./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php repro_a.php && php bin/vm.php repro_b.php'
php repro_a.php && php repro_b.php| Runtime | Repro A | Repro B |
|---|---|---|
| Zend PHP 8.x | ab + newline |
foo + newline |
bin/vm.php |
fatal: IteratorIterator not found |
fatal: RegexIterator not found |
Scope (this repo)
| Area | Path | Notes |
|---|---|---|
| ext/spl | new class handlers | Real iterator protocol — no permanent stubs |
| VM | reuse lib/VM.php foreach / VmIteratorWalk.php |
IteratorIterator delegates to inner iterator |
| PCRE | ext/standard/VmPreg.php |
RegexIterator uses existing preg engine |
| Tests | test/compliance/cases/stdlib/spl_iterator_iterator.phpt, spl_regex_iterator.phpt |
|
| Matrix | script/capability-matrix.php |
v1 RegexIterator modes: MATCH only. Defer: REPLACE, SPLIT, GET_MATCH.
Done when
- Repro A prints
abon VM - Repro B prints
fooon VM -
RegexIteratorconstants (MATCH,USE_KEY, …) defined - Non-iterator ctor operand →
TypeErrorper Zend -
./script/ci-fast.sh --filter 'IteratorIterator|RegexIterator'green
Verification
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && vendor/bin/phpunit --filter IteratorIterator'Links
- SPL wrappers umbrella: Stdlib: SPL iterator wrappers — FilterIterator, LimitIterator, AppendIterator, … (ext/spl parity) #4546
- Traversable foreach: Language: foreach over Traversable / Iterator objects (Zend parity) #3234, Language: foreach — array/Traversable iteration semantics parity (Zend/zend_vm_def.h, zend_compile.c) #4431
- North star: Epic: Self-host critical path — compiler compiles itself (M3→M5) #1492
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:vmVirtual machineVirtual machineenhancementNew feature or requestNew feature or requestimplementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claimSpec complete: repro, php-src ref, done-when — safe for workers to claimphase-4:stdlibPhase 4 – stdlib for web appsPhase 4 – stdlib for web apps
Activity
PurHur commentedon Jul 15, 2026
claim: PHP Compiler STD Lib Advanced — batch (issues #4387, #6138, #6563)
Queued after #4387. IteratorIterator works for ArrayIterator; Generator inner yields LogicException on rewind (Expected Generator instance).
PurHur commentedon Jul 15, 2026
claim: PHP Compiler STD Lib Advanced — implementing first (ahead of #4387)
Root cause:
SplDualIteratorStorage(and sibling wrappers) keep bareObjectEntrypointers withoutObjectLifetime::addRef. A temporarygen()dies when the construct arg slot is released;destroyForGc()clearsgeneratorState, so laterrewind()/valid()throwExpected Generator instance. Named$g = gen()stays alive via the user Variable ref.Fix: pin inner iterators on store (same pattern as
RecursiveCallbackFilterIteratorBuiltin::copyVar).PurHur commentedon Jul 15, 2026
Merged via PR #19327.
Root cause: Outer wrappers stored bare
ObjectEntryforgen()temps; frame teardown destroyed the Generator (generatorStatecleared) before foreachrewind().Fix: Pin Variable roots + preserve/reattach
GeneratorStateinSplIteratorSupportbeforecallInner.Verification:
Stdlib: pin IteratorIterator inners across temp Generator GC (#6138) (#…