Codementor Events

Generators in PHP

Published Nov 02, 2018Last updated Apr 30, 2019

generators.png

Over times we're face with issues where PHP apps are found to consume quite a lot of memory and not memory efficient because of the way we write our apps or when we don't take advantage of some features provided PHP itself which helps memory management. An example of a feature that can be used as an advantage in reducing memory use is Generators. You say what? Yes - Generators! So, let's dive in...

What are Generators?

Generators are basically functions that returns many values that can be iterated over. Unlike iterating over an array, you have control over the process of iterating over the values returned by a generator. In other words we can control the iteration of the values and do things like returning values when we want or as much as we want or pause iteration or even rewind iteration and taking it to another level, we can send back values through generators.

Unlike normal function that directly returns something a generator yields something, even though PHP 7 now supports functions yielding values as well as returning values but such a function is still regarded as a generator. Once a yield keyword appears in the function body, PHP will recognize such a function as a Generator.

Putting the explanation in code

We have our normal function with return like this:

<?php 
  function foo($a, $b) { return $a + $b; }

But such a function like the one below is not regarded as function, but rather a Generator:

<?php 
  function bar($a, $b) { yield $a + $b; }

To proof this, I'll use this code:

<?php 
  function foo($a, $b) { 
    	return $a + $b; 
    } 
    
    function bar($a, $b) { 
    	yield $a + $b; 
    } 
    
    var_dump(foo(2, 3)); 
    echo PHP_EOL; 
    var_dump(bar(2, 3));

When I run this, I got a result like this:

int(5) class Generator#1 (0) {
}

The function call foo(2, 3) returns int(5) as expected but the function call bar(2, 3) returns an instance of theGenerator class. It can be seen clearly that the only difference in the function body is the yield keyword used in place of the return keyword. So, to summarize this, anytime you use the yield key word in a function, PHP automatically takes it as a Generator and it returns a Generator object.

But how do we access the generator value?

The Generator class implements the IteratorInterface which provides a handful of methods that can be used to access the values in the Generator. You can read about the IteratorInterfacehere.

To proof this, we'll add this to our initial code:

<?php 
  function foo($a, $b) { 
    	return $a + $b; 
    } 
    
    function bar($a, $b) { 
    	yield $a + $b; 
    } 
    
    var_dump(foo(2, 3)); 
    echo PHP_EOL; 
    var_dump(bar(2, 3)); 
    echo PHP_EOL; 
    var_dump(bar(2, 3) instanceof Iterator);

And we get the result below:

int(5) class Generator#1 (0) {
} 

bool(true)

We get a true meaning that the Generator class implements the Iterator interface.

Getting result

Now if we call the ->current() method on the bar(2, 3) function call like this:

<?php 
  // ... 
    echo PHP_EOL; 
    var_dump(bar(2, 3)->current());

We'll get the expected int(5) as result. We were able to use the ->current() method because the Generator class implemented the Iterator interface.

As seen from the Iterator documentation here, other methods that can be accessed are next(), previous(), key() and valid(), the documentation explains the workings of these methods as well as their return values. I recommend you read more on it.

Another example

In the initial example, we yielded just a single result, but a a generator actually allows many results to be yielded, let's see an example:

<?php 
  function compute($x, $y) { 
    	yield $x + $y; 
        yield $x * $y; 
        yield $x / $y; 
        yield $x - $y; 
    }

The function above computes the addition, subtraction, multiplication and division of two numbers. Now let's access the result by adding this to our code:

<?php 
  // ...
  $computation = compute(2, 3);
    $addition = $computation->current();
    
    echo "Addition: $addition" . PHP_EOL; 
    
    $computation->next(); 
    $multiplication = $computation->current(); 
    
    echo "Multiplication: $multiplication" . PHP_EOL; 
    
    $computation->next(); 
    $division = $computation->current(); 
    
    echo "Division: $division" . PHP_EOL; 
    $computation->next(); 
    
    $subtraction = $computation->current(); 
    
    echo "Subtraction: $subtraction" . PHP_EOL; 
    // $computation->next(); - No need for this since we are already reached the end

This infrastructure is what provides Generators flexibility and power to be used in many cases that helps the program we write in many ways such as Speed, Memory management, streaming etc.

In this article, we'll only examine what generators are and how we can use them, in further articles, I'll show how Generators can help in saving Memory and in later articles, I'll be explaining how they can be used to improve speed as well as other applications.

You can read more about the generator syntax here.

Discover and read more posts from Bosun Egberinde
get started
post commentsBe the first to share your opinion
Show more replies