7 questions found
What is a generator in PHP, and how does the yield keyword let a function produce a sequence of values without building an entire array in memory at once?
Beginner
A generator is a special kind of function that uses the yield keyword to produce a sequence of values one at a time, pausing its execution after each yield and resuming exactly where it left off the next time a value is requested, which is especially valuable for working with large datasets since the generator only computes and holds one value in memory at a time rather than building and storing an entire array upfront.
function countTo($max) {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(5) as $number) {
echo $number;
}
Real-world example
A log file processing script uses a generator to yield one line at a time from a massive multi gigabyte file, keeping memory usage low regardless of how large the file actually is, rather than loading the entire file into an array first.
Common follow-ups: What is the memory difference between a generator and a function that returns a full array?;Can you use a foreach loop directly on a generator, just like on a regular array?
File Handling & File System Functions;Performance Optimization & OPcache
What is the Iterator interface in PHP, and how does it relate to how a foreach loop actually works behind the scenes?
Beginner
The Iterator interface defines a set of methods, including current, key, next, rewind, and valid, that a class must implement to define exactly how it should be iterated over, and PHP's foreach loop automatically calls these methods in the correct sequence when looping over any object implementing this interface, meaning you can make your own custom class behave just like a native array when used in a foreach loop.
class NumberCollection implements Iterator {
private $items = [1, 2, 3];
private $position = 0;
public function current(): mixed { return $this->items[$this->position]; }
public function key(): mixed { return $this->position; }
public function next(): void { $this->position++; }
public function rewind(): void { $this->position = 0; }
public function valid(): bool { return isset($this->items[$this->position]); }
}
Real-world example
A custom collection class implements the Iterator interface so that developers using it can simply loop over an instance with a familiar foreach statement, without needing to know anything about its internal storage structure.
Common follow-ups: How does a generator relate to the Iterator interface internally?;What is the simpler IteratorAggregate interface and how does it differ from implementing Iterator directly?
OOP;Interfaces & Abstract Classes
What is the IteratorAggregate interface, and how does it provide a simpler alternative to implementing the full Iterator interface directly?
Intermediate
The IteratorAggregate interface requires implementing just a single method, getIterator, which returns an object that itself implements Iterator, often conveniently a generator or an ArrayIterator wrapping an internal array, letting you make a class iterable without needing to manually implement all five of the Iterator interface's individual methods yourself, significantly reducing boilerplate for the common case of simply wrapping an existing collection of data.
class NumberCollection implements IteratorAggregate {
private $items = [1, 2, 3];
public function getIterator(): Iterator {
return new ArrayIterator($this->items);
}
}
Real-world example
A developer building a custom collection class chooses IteratorAggregate over the full Iterator interface, writing a single simple method that delegates to a built in ArrayIterator rather than manually implementing five separate iteration methods.
Common follow-ups: When would you choose to implement Iterator directly instead of using IteratorAggregate?;Can getIterator return a generator instead of an ArrayIterator?
OOP;Interfaces & Abstract Classes
How can a generator receive values sent into it during iteration using the send method, enabling two way communication with the calling code?
Intermediate
In addition to yielding values out to the calling code, a generator can also receive a value sent into it using the send method, which resumes the generator's execution and makes that sent value become the result of the current yield expression inside the generator, enabling more advanced patterns where the generator's behavior can be influenced dynamically based on values provided by the code consuming it.
function echoTimes() {
while (true) {
$value = yield;
echo "Received: $value\n";
}
}
$gen = echoTimes();
$gen->current();
$gen->send('Hello');
Real-world example
A simple task processing generator receives commands sent into it by the calling code, using the sent values to determine what specific work to perform next, effectively acting like a lightweight coroutine within a single script.
Common follow-ups: What is the difference between yield used as an expression versus a statement?;How does this two way communication pattern relate to coroutines in languages that support them more natively?
Asynchronous PHP (ReactPHP & Swoole);Closures & Anonymous Functions
How does yield from let a generator delegate part of its iteration to another generator or an array, and why is this useful?
Intermediate
The yield from construct lets a generator delegate to another iterable, such as a different generator function or a plain array, effectively flattening that iterable's values into the outer generator's own sequence of yielded values, which is useful for composing several smaller generators together into a larger, combined sequence without needing to manually loop over and re yield each individual value from the nested source.
function letters() {
yield 'a';
yield 'b';
}
function combined() {
yield from letters();
yield 1;
yield 2;
}
foreach (combined() as $value) {
echo $value;
}
Real-world example
A data processing pipeline combines several smaller specialized generators together using yield from, building one larger unified sequence out of multiple smaller, independently reusable pieces of iteration logic.
Common follow-ups: Can yield from delegate to a plain array as well as another generator?;How does yield from affect what value is returned to the outer code from the return statement of the inner generator?
Arrays in PHP;OOP
How do generators help implement memory efficient data processing pipelines, chaining multiple transformation steps together without ever loading an entire dataset into memory at once?
Advanced
By chaining together multiple generator functions, where each generator both consumes values from a previous generator and yields transformed values to the next stage, you can build a complete data processing pipeline, such as filtering and then transforming a massive dataset, where at any given moment only a single item is actually being processed and held in memory across the entire pipeline, dramatically reducing memory usage compared to processing the same pipeline using regular arrays and functions that must fully materialize each intermediate result.
function filterEven($numbers) {
foreach ($numbers as $n) {
if ($n % 2 === 0) yield $n;
}
}
function double($numbers) {
foreach ($numbers as $n) {
yield $n * 2;
}
}
foreach (double(filterEven(range(1, 1000000))) as $result) {
}
Real-world example
A data engineering script processes a massive CSV file through a pipeline of chained generators handling parsing, filtering, and transformation, keeping memory usage constant regardless of whether the file contains a thousand or ten million rows.
Common follow-ups: How does this generator based pipeline approach compare to using array_filter and array_map on the entire dataset at once?;What are the limitations of this approach if you need random access to previously processed items?
Arrays in PHP;Performance Optimization & OPcache
What are the limitations of PHP generators compared to regular arrays, particularly around rewinding and multiple iteration passes?
Advanced
Unlike a regular array, a PHP generator can typically only be iterated over once from start to finish, since it represents a single, forward only sequence of values that are computed lazily as they are requested, meaning attempting to iterate over the same generator instance a second time, or explicitly calling rewind on a generator that has already started producing values, will throw an exception, which is an important limitation to keep in mind when designing code that might need to process the same sequence of data multiple times.
function numbers() {
yield 1;
yield 2;
}
$gen = numbers();
foreach ($gen as $n) { echo $n; }
foreach ($gen as $n) { echo $n; } // Throws an exception on second iteration
Real-world example
A developer debugging an unexpected exception discovers their code was attempting to loop over the same generator instance twice, and fixes the issue by calling the generator producing function again to create a fresh generator for the second iteration instead.
Common follow-ups: Why can't a generator simply be rewound like an array pointer can?;What pattern should you use if you genuinely need to iterate over the same underlying data multiple times?
Arrays in PHP;OOP