The Utility of PHP Generators

The Utility of PHP Generators

Written by Ghost on Jun 11th, 2019 Views Report Post

A generator in PHP is a function that allows us to iterate over data without needing to build an array in memory. Unlike a standard function, which can return only a single value, a generator can yield multiple values.

When Should I Use a Generator in PHP?

It's fairly common to retrieve data from an API, store it in an array, and then iterate over that array to display information in a user interface. However, there might be a situation where the response is enormous and populating an array with that data would cause a memory overflow.

What would happen, for example, if we tried populating an array with 9 Billion elements?

<?php

$array = [];

for ($i = 0; $i < 9999999999; $i++) {
    $array[] = $i;
}

Executing this code produces the following message: "Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 134217736 bytes)". 

This is, of course, not a practical example; we'd probably never need an array of consecutive integers. However, it does illustrate something important: memory is finite and bad things will happen if memory usage limits are reached.

Array Iteration vs. Generators

Consider the following example:

<?php

$array = [];

function getData() {
    for ($i = 0; $i < 999999; $i++) {
        $array[] = $i;
    }

    return $array;
}

foreach (getData() as $data) {
    echo $data;
}

Here, we are doing the following:

  1. we declare an empty array
  2. we define a function that populates the array with 999,999 elements
  3. we iterate over the invocation of the function (an array) to print each item to the screen

My tests show that the previous code uses approximately 33,521,784 bytes (your tests will likely differ somewhat). Now, consider the following alternative:

<?php

function getData() {
    for ($i = 0; $i < 999999; $i++) {
        yield $i;
    }
}

foreach(getData() as $data) {
    echo $data;
}

This time, we use a generator (which is merely a function that uses the "yield" keyword). This is what happens:

  1. we define a generator function that yields 999,999 elements
  2. we iterate over the invocation of the generator (an iterable object) to print each item to the screen

This code uses zero bytes and accomplishes exactly the same thing as the previous example. At no point in the latter example do we store any data in an array or any kind of data structure, for that matter.

Try it For Yourself

Execute the code in this Gist yourself if you're skeptical. Simply comment out one section to test the other. After all the code is finished executing, you'll see that the last two lines display the "total execution time" and "bytes used".

A Note on Performance

This post suggests that generators, when used in the manner described above, contribute to a "significant performance boost". This appears to depend upon what one means by "performance". If the term is used to refer exclusively to speed of execution, then it's unclear whether this is true. Most of my tests appear to suggest that generators do not improve this kind of performance. However, this might vary across machines and results might differ depending on what version of PHP one is using, or what type of operation is being carried out.

However, if "performance" refers to an app's ability to reliably avoid memory issues, then I believe the performance boost claim should be considered accurate; a useful notion of performance could not possibly exclude reliability metrics. But no matter how we wish to define the term, I'm sure most would agree that clarity and precision matter.

Closing Thoughts

The preceding was an attempt to illustrate just one possible use of generators in PHP. A discussion of other use-cases as well as limitations and additional features associated with generators ought to be saved for a more thorough discussion.

Comments (0)