Using PHP Generator: A Practical Guide

Updated: January 10, 2024 By: Guest Contributor Post a comment

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.