Overview
Generators in PHP provide an easy way to implement simple iterators without the overhead of implementing the Iterator interface. This guide will walk you through using PHP generators to handle iterations more memory efficiently.
What is a PHP Generator?
Introduced in PHP 5.5, a generator is a function that allows you to pause and resume execution while maintaining the state.yield
keyword within a function makes it a generator, providing a simple way to iterate over a set of data without creating an array in memory. This can result in significant performance improvements for large datasets or streams.
function myGenerator() {
for ($i = 0; $i < 5; $i++) {
yield $i;
}
}
foreach (myGenerator() as $value) {
echo $value . PHP_EOL; // Outputs 0 through 4 each on a new line
}
Basic Usage of Generators
Let’s start with a simple example — a function that yields each character of a string one by one:
function stringGenerator($string) {
for ($i = 0; $i < strlen($string); $i++) {
yield $string[$i];
}
}
foreach (stringGenerator('Hello') as $char) {
echo $char . ' '; // Outputs: H e l l o
}
Note how the generator function maintains state between calls, yielding characters in sequence without storing an array of characters.
Working with Large Datasets
One of the primary benefits of generators is dealing with large datasets. The following example reads a file line-by-line:
function fileLineGenerator($filename) {
$file = fopen($filename, 'r');
while ($line = fgets($file)) {
yield $line;
}
fclose($file);
}
Each call to the generator function reads the next line, without loading the entire file into memory:
foreach (fileLineGenerator('largeFile.txt') as $line) {
echo $line;
}
Combining Generators
Generators can also send values back to the caller, enabling two-way communication.
function printer() {
while (true) {
$string = yield;
echo $string . PHP_EOL;
}
}
$printer = printer();
$printer->send('Hello');
// Outputs: Hello
$printer->send('World');
// Outputs: World
Advanced Usage: Coroutines
In more complex scenarios, generators can be used as coroutines, allowing for asynchronous programming patterns. Here is an example of a coroutine that receives and modifies data:
function dataProcessor() {
while (true) {
$data = (yield);
// Do something with $data
yield strtoupper($data);
}
}
$processor = dataProcessor();
$processor->send(null);
while (
$result = $processor->send('hello')
// Perform operations with the result
}) {
echo 'Processed: ' . $result . PHP_EOL;
// Outputs: Processed: HELLO
}
In this example, each send()
operation sends data into the generator for processing and immediately yields the transformed data.
Handling Infinite Data Streams
Sometimes, you might need to work with an infinite data source. Generators are a perfect fit for this:
function infiniteEvenNumbers() {
$number = 0;
while (true) {
yield $number;
$number += 2;
}
}
You can iterate over the generator and obtain even numbers indefinitely:
foreach (infiniteEvenNumbers() as $number) {
echo $number . PHP_EOL;
if ($number >= 10) { break; } // limit the output for demonstration purposes
// Outputs 0, 2, 4, 6, 8, 10
}
Conclusion
In this practical guide, we’ve explored the functional and expressive power of PHP generators. With simple syntax and greater memory efficiency, generators can help you manage data iteration in a resource-conscious way, suitable for large or infinite datasets. When performance matters, and you need an easy-to-implement solution for data handling, PHP generators are an excellent tool to consider.