A function that contains yield is a generator function (PHP 5.5). Calling it runs none of its body; it returns a Generator object, which implements Iterator. Each time foreach asks for a value, the body runs to the next yield, hands out that value (and a key, with yield $key => $value) and freezes with its local variables intact. Only the current row exists.
<?php
function ordersLazy(int $n): Generator {
for ($id = 1; $id <= $n; $id++) yield "ORD-$id" => ['id' => $id, 'total' => $id % 97 * 100];
}
function ordersArray(int $n): array { return iterator_to_array(ordersLazy($n)); }
function measure(string $label, Closure $source): void {
memory_reset_peak_usage(); // PHP 8.2+
$base = memory_get_usage();
$sum = 0;
foreach ($source() as $key => $order) $sum += $order['total'];
$kb = number_format((memory_get_peak_usage() - $base) / 1024, 1);
printf("%-9s last %s, sum %d, peak %11s KB\n", $label, $key, $sum, $kb);
}
measure('array', fn() => ordersArray(1_000_000));
measure('generator', fn() => ordersLazy(1_000_000));Output
array last ORD-1000000, sum 4799908200, peak 447,206.3 KB generator last ORD-1000000, sum 4799908200, peak 1.5 KB
A million orders cost about 437 MB as an array and 1.5 KB as a generator. A generator runs once: a second foreach over a finished one throws Cannot traverse an already closed generator. Use generators to stream anything too large to hold, such as fgets() over a log (Files, Directories and Streams) or PDOStatement::fetch() in a loop (Databases with PDO).