Advanced Generators

Delegation, Return Values and Two-Way Generators

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.

A catalog built by delegation and a till fed by send()PHP
<?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";
Output
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).