I think "foreach" is a mess. When the foreach loop starts, an iterator is created, and I think it cannot handle the fact that I inject new things into the generator.
It:
<?php function action() { for($i=0; $i<10; ++$i) { $ans = (yield expensive($i)); echo "action $ans\n"; } } function expensive($i) { return $i*2; } $gen = action(); while($gen->valid()) { $x = $gen->current(); echo "loop $x\n"; $gen->send($x); }
Prints out what I expect:
loop 0 action 0 loop 2 action 2 loop 4 action 4 loop 6 action 6 loop 8 action 8 loop 10 action 10 loop 12 action 12 loop 14 action 14 loop 16 action 16 loop 18 action 18
Things get weird though if you send more than once per loop:
<?php function action() { for($i=0; $i<10; ++$i) { $ans = (yield expensive($i)); echo "action $ans\n"; } } function expensive($i) { echo "expensive $i\n"; return $i; } $gen = action(); while($gen->valid()) { $x = $gen->current(); echo "loop $x\n"; $gen->send($x); $gen->send($x); }
Print
expensive 0 loop 0 action 0 expensive 1 action 0 expensive 2 loop 2 action 2 expensive 3 action 2 expensive 4 loop 4 action 4 expensive 5 action 4 expensive 6 loop 6 action 6 expensive 7 action 6 expensive 8 loop 8 action 8 expensive 9 action 8
I think what happens here is that send invokes the action iteration twice for each while iteration. If we remove the two sends() , then we are stuck in an infinite loop. So ... send() promotes the iterator, but current() does not. And I think this explains what happens with the foreach - both foreach and send() promoted an iterator, so every other result was skipped!
source share