PHP like a Boss: Understanding Generators
Yeah, maybe it was the generator cover image but I am glad you are here Now what are Generators and how do they work in PHP?
Generators are special routines that can be used to control the iteration behaviour of a loop. Generators are used in the implementation of fibonacci.
Visit here to have the overview of these implementation.
Generators are similar to Iterators. For many PHP Programmers, Iterators have been about the only choice until the release of PHP 5. I am guessing that this is the reason why we do not have as many write ups in generators as we have with Iterators. While generators are unpopular in the PHP language, it’s not a new concept. They already exist in languages such as C#, Python, Javascript and Ruby (enumerables).
A brief overview of Iterators: They help in looping/traversing through an array. The keyword “foreach” is mostly used when iterating.
However, Generators provide an easy way to implement simple Iterators. Much less boilerplate code is written and the code is generally more readable compared to the Iterators.
In this article, I am going to share with you a very simple implementation of generators. If you are ready, I am ready, then let’s dive in:
Simple Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?php /** * We declare a generator the same way we declare a function. Inside the function we * iterate through the number and yielded number * * @params $number * @return Integer: note that the word yield is used in generator and not return */ function square($number) { foreach($number as $num) { yield $num * $num; } } /** * Consider this to be the array we want to get the number. */ $array = [1,2,3,4,5]; /** * Generators are objects, they can’t be instantiated via new. The next line, we * Iterate through the object and we echoed out the result. Isn’t it simple? */ $newSquare = square($array); // Generators are objects, they can’t be instantiated via new foreach($newSquare as $result) { echo $result; } ?> //The Output //1 4 9 16 25 |
Firstly, the generators are declared just like functions. An iterator is then used with the ‘yield’ keyword instead of ‘return’. The generators are objects except that they cannot be instantiated with the ‘new’ keyword. Then followed by another foreach loop which echoes the square of the array as seen in the output.
Printing Keys and Values
We can easily define a generator function which returns a key and a value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
<?php /** * We declare a generator the same way we declare a function. Inside the function we * iterate through the number and yielded the key and the value * * @params $number * @return Array: note that the word yield is used in generator and not return */ function keyValue($number) { foreach($number as $key => $val) { yield $key => [$val]; } } /** * Consider this to be the array we want to get the keys and the values for */ $array = [001 => "Ademola", 002 => "Raimi", 003 => "programmer"]; /** * When calling the object, the $array is inserted as a parameter, and in the next * line, we loop through the object. We can then print the keys or values based on * what we want. */ $newkeyValue = keyValue($array); foreach($newkeyValue as $key => $val) { print_r($val); } ?> //The Output //Array ( [0] => Ademola ) Array ( [0] => Raimi ) Array ( [0] => programmer ) |
Generators also allow us to print out the keys and the values of an array. In the above example, we yielded the key and the value like this: “yield $key => [$val]”. Outside the function, while calling the generator object, we passed in the array. The for loop then iterates over the object and prints out the value as seen above.
Generator function by reference
We can define a generator function that the values yielded are returned by reference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?php /** * We declare a generator the same way we declare a function. In this case, ampersand * sign is added before the name of the function. * * @return Integer: note that the word yield is used in generator and not return */ function &specialGenerator () { $num = 1; while ($num <= 10) { yield $num; } } /** * Generators are objects, they can't be instantiated via new, and in the next * line, we loop through the object. We printed the number. */ $result = specialGenerator(); foreach($result as &$number) { echo $number++; } ?> //The Output //1 2 3 4 5 6 7 8 9 10 |
Generator returns Expression
PHP 7 allows us to include a return statement within a generator function in order for the final expression to be returned. The final expression can be fetched by calling the getReturn()
function on the generator object. The possibility to return a value from a generator allows us to get the return of a calculation that is executed inside the generator function.
Let’s dive in:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?php /** * We declare a generator the same way we declare a function as usual. We initialize * the value of sum to 0. In the later part, we return the sum in order to fetch the * expression. * * @return Integer: note that the word yield is used in generator and not return */ function number() { $sum = 0; for ($i = 1; $i <= 10; $i++) { $sum += 1; yield $i * $i; } return $sum; } /** * Generators are objects, they can't be instantiated via new, and in the next * line, we loop through the object. We echoed the value. At the later part, we * echoed the fetched expression by calling the getReturn() function on the generator * object */ $result = number(); foreach($result as $val) { echo $val; } echo $result->getReturn(); ?> //The Output //1 4 9 16 25 36 49 64 81 100 10 |
Generator delegation
PHP 7 also allows us to yield an expression inside the generator function. From the example below, the iteration occurs within the generator function and the result of the iteration got yielded inside the generator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?php /** * We declare a generator the same way we declare a function as usual. Inside the * function, we declared two arrays: $array1 and $array2. At the later part, we * yielded from the two arrays separately. The keyword ‘from’ is used to do this. * * @return Integer: note that the word yield is used in generator and not return */ function numbers() { $array1 = [1, 2, 3, 4]; $array2 = [10, 20, 30, 60]; yield from $array1; yield from $array2; } /** * Generators are objects, they can't be instantiated via new, and in the next * line, we loop through the object. We echoed the value. The value gets the yielded * values from both arrays */ $result = numbers(); foreach($result as $val) { echo $val; } //The Output //1 2 3 4 10 20 30 60 |
In PHP 7, generator allows us to yield values from an array using the keyword ‘yield from’ as seen in the above example. The generator object will yield all the values inside the array. As seen in the above example, the generator yielded all the values in the two arrays declared inside the function.
Conclusion
For every Iteration/looping you need to do, consider making use of the powerful tool called generators. With the introduction of generators, we can write a more readable code that iterates and simultaneously saves us memory. The elePHPant giant has blessed us with a powerful weapon, we should take huge advantage of it.
Please if you have any questions or observations, let me know in the comments section.
- PHP like a Boss: Understanding Generators - March 14, 2016