PHP 7.0 added yield from $inner, which passes through every value of another generator, array or Traversable and evaluates to the inner generator's return value; getReturn() reads that value once a generator finishes. Since 5.5, yield has also been an expression: $in = yield $out hands $out to the caller and receives what the caller passes to send(), so a generator can consume values as well as produce them.
<?php
function books(): Generator { yield 'BK-PHP-01' => 3990; yield 'BK-SQL-01' => 4450; return 2; }
function accessories(): Generator { yield 'AC-MUG-01' => 1250; return 1; }
function catalog(): Generator {
$n = yield from books(); // pass values through, collect return
$n += yield from accessories();
return $n;
}
$cat = catalog();
foreach ($cat as $sku => $cents) echo "$sku $cents | ";
echo 'items ', $cat->getReturn(), "\n";
function till(): Generator { // consumes prices, yields the subtotal
$total = 0;
while (($cents = yield $total) !== null) $total += $cents;
return $total;
}
$t = till();
foreach ([3990, 4450, 1250] as $c) echo 'subtotal ', $t->send($c), "\n";
$t->send(null);
echo 'final ', $t->getReturn(), "\n";BK-PHP-01 3990 | BK-SQL-01 4450 | AC-MUG-01 1250 | items 3 subtotal 3990 subtotal 8440 subtotal 9690 final 9690
The first send() runs the generator to its first yield before delivering the value, so no priming call is needed. Each send() makes the paused yield evaluate to the sent value and returns the next yielded one; throw() resumes with an exception. Watch the keys: yield from keeps the inner ones, so yield from [10, 20]; yield from [30]; yields key 0 twice, and iterator_to_array() keeps only the later value unless its second argument is false. To suspend deep inside ordinary calls rather than at a yield, use Fibers (Asynchronous PHP).