Generators are usually introduced as a way to produce a sequence lazily, and the arrow is described as pointing one way. It does not: yield is an expression, and its value is whatever the caller passed to send(). That turns a generator into a consumer — something you push rows into, which keeps its own state between pushes.
function batchInsert(PDO $pdo, $size)
{
$batch = array();
while (true) {
$row = yield; // whatever send() handed over
$batch[] = $row;
if (count($batch) === $size) {
flushBatch($pdo, $batch);
$batch = array();
}
}
}
$writer = batchInsert($pdo, 500);
$writer->current(); // run the body up to the first yield
foreach ($rows as $row) {
$writer->send($row);
}
The batching logic now lives in one place instead of being three variables carried through the caller’s loop, and the caller does not know the size. Two things to get right. The generator body does not execute until the generator is advanced, so priming it with current() makes the order of operations explicit even though send() would advance it for you. And a while (true) generator never reaches its end, so the final partial batch is still sitting in the local array when the loop stops — it needs a sentinel value, or a wrapper object with a flush() that the caller invokes. This is cooperative and entirely synchronous: control moves only where send() and yield put it, and nothing in PHP schedules anything on your behalf.