yield from delegates without a loop

Composing generators without yield from means a wrapper loop that yields each item from the inner one, which works and costs a function frame per item on top of the iteration you already had.

function lines(array $files): Generator
{
    foreach ($files as $file) {
        yield from linesOf($file);   // not: foreach (linesOf(...) as $l) yield $l;
    }
}

The subtlety is keys: yield from preserves the inner generator’s keys, so delegating to three generators that each key from zero produces duplicate keys. That is invisible in a foreach and very visible the moment the result goes through iterator_to_array() without false as the second argument — two thirds of the rows disappear.