You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Nested "yield from" yields a value twice when the middle generator ends with "yield from []" #23301
Commit 0ccff76 ("Fix GH-15375: nested yield from skips items after valid()/next()", merged to PHP-8.4/PHP-8.5/master on 2026-08-12) introduces a regression: in a three-level yield from delegation chain, a value is yielded twice when the middle generator ends with yield from [] (delegation to a non-generator empty iterable).
This is not a synthetic pattern. Twig compiles every template's doDisplay() to end with yield from [];, and rendering any template containing a block/include goes through exactly this three-level chain, so every affected template duplicates all output emitted after the block.
The duplicated value is whatever the middle generator yielded last before the trailing yield from []; with two tail yields (yield "C"; yield "D"; yield from [];) the result is ABCDD.
Narrowing:
Shape
Result
3 levels, middle ends yield from []
BROKEN
3 levels, no trailing empty delegate
OK
3 levels, middle ends yield from <empty generator>
OK
3 levels, inner is an array literal rather than a generator
OK
2 levels (no outer delegation)
OK
So it requires: an outer generator delegating to a middle generator, the middle delegating to an inner generator, and the middle then delegating to an empty non-generator iterable.
Bisected by reverting only the Zend/zend_generators.c hunks of 0ccff76 on top of master 5332ab0c291361abe3d12db21dd55e5dc7baa829 — that alone restores correct output, and also restores a full Twig render-test suite (22 tests) that fails 15 cases without it.
The relevant change is in zend_generator_resume(), where the DO_INIT re-advance guard now reads the flag from the delegating generator:
if (UNEXPECTED(generator->execute_data&&generator->execute_data->opline->opcode==ZEND_YIELD_FROM)) {
delegator=generator;
generator=zend_generator_get_current(orig_generator);
goto try_again;
}
When the middle generator sits on a ZEND_YIELD_FROM opline for an empty array, delegator becomes the middle generator, whose DO_INIT is clear, so the guard that previously stopped the re-advance (via orig_generator) no longer fires and the already-yielded value is presented again.
PHP Version
PHP 8.6.0-dev (built from master 5332ab0c291361abe3d12db21dd55e5dc7baa829)
Also present on the PHP-8.4 and PHP-8.5 branches, since the fix was merged to all three — i.e. this is heading for 8.4.26 and 8.5.11.
Operating System
Debian bookworm (also reproduced on GitHub Actions ubuntu-24.04 with the shivammathur/setup-php 8.6 nightly)
@php/release-managers-84 @php/release-managers-85 for visibility: This seems like a significant regression and you'll likely want to monitor this issue to either revert the original fix in your release branches or to pick a follow-up fix.
Activity
ci(isolated-tests): don't let the pre-release PHP 8.6 job cancel or b…
Fix phpGH-23301: nested "yield from" repeats a value after "yield fro…
bug #65367 Fix PHP 8.6 deprecations (nicolas-grekas)
TimWolla commentedon Aug 18, 2026
@php/release-managers-84 @php/release-managers-85 for visibility: This seems like a significant regression and you'll likely want to monitor this issue to either revert the original fix in your release branches or to pick a follow-up fix.
Fix phpGH-23301: clear ZEND_GENERATOR_DO_INIT on the delegating gener…
Fix GH-23301: nested "yield from" repeats a value after "yield from […
Merge branch 'PHP-8.5'
Merge branch 'PHP-8.4' into PHP-8.5