Generators overview

(PHP 5 >= 5.5.0)

Generators provide an easy way to implement simple iterators without the overhead or complexity of implementing a class that implements the Iterator interface.

A generator allows you to write code that uses foreach to iterate over a set of data without needing to build an array in memory, which may cause you to exceed a memory limit, or require a considerable amount of processing time to generate. Instead, you can write a generator function, which is the same as a normal function, except that instead of returning once, a generator can yield as many times as it needs to in order to provide the values to be iterated over.

A simple example of this is to reimplement the range() function as a generator. The standard range() function has to generate an array with every value in it and return it, which can result in large arrays: for example, calling range(0, 1000000) will result in well over 100 MB of memory being used.

As an alternative, we can implement an xrange() generator, which will only ever need enough memory to create an Iterator object and track the current state of the generator internally, which turns out to be less than 1 kilobyte.

Пример #1 Implementing range() as a generator

<?php
function xrange($start$limit$step 1) {
    if (
$start $limit) {
        if (
$step <= 0) {
            throw new 
LogicException('Step must be +ve');
        }

        for (
$i $start$i <= $limit$i += $step) {
            yield 
$i;
        }
    } else {
        if (
$step >= 0) {
            throw new 
LogicException('Step must be -ve');
        }

        for (
$i $start$i >= $limit$i += $step) {
            yield 
$i;
        }
    }
}

/*
 * Note that both range() and xrange() result in the same
 * output below.
 */

echo 'Single digit odd numbers from range():  ';
foreach (
range(192) as $number) {
    echo 
"$number ";
}
echo 
"\n";

echo 
'Single digit odd numbers from xrange(): ';
foreach (
xrange(192) as $number) {
    echo 
"$number ";
}
?>

Результат выполнения данного примера:

Single digit odd numbers from range():  1 3 5 7 9 
Single digit odd numbers from xrange(): 1 3 5 7 9 

Коментарии

for the protection from the leaking of resources 
see RFC https://wiki.php.net/rfc/generators#closing_a_generator

and use finnaly

sample code

function getLines($file) {
    $f = fopen($file, 'r');
    try {
        while ($line = fgets($f)) {
            yield $line;
        }
    } finally {
        fclose($f);
    }
}

foreach (getLines("file.txt") as $n => $line) {
    if ($n > 5) break;
    echo $line;
}
2013-08-15 10:32:21
http://php5.kiev.ua/manual/ru/language.generators.overview.html
Автор:
Abstract test.
<?php

$start_time
=microtime(true);
$array = array();
$result '';
for(
$count=1000000$count--;)
{
 
$array[]=$count/2;
}
foreach(
$array as $val)
{
 
$val += 145.56;
 
$result .= $val;
}
$end_time=microtime(true);

echo 
"time: "bcsub($end_time$start_time4), "\n";
echo 
"memory (byte): "memory_get_peak_usage(true), "\n";

?>

<?php

$start_time
=microtime(true);
$result '';
function 
it()
{
  for(
$count=1000000$count--;)
  {
    yield 
$count/2;
  }
}
foreach(
it() as $val)
{
 
$val += 145.56;
 
$result .= $val;
}
$end_time=microtime(true);

echo 
"time: "bcsub($end_time$start_time4), "\n";
echo 
"memory (byte): "memory_get_peak_usage(true), "\n";

?>
Result:
----------------------------------
           |  time  | memory, mb |
----------------------------------
| not gen  | 2.1216 | 89.25      |
|---------------------------------
| with gen | 6.1963 | 8.75       |
|---------------------------------
| diff     | < 192% | > 90%      |
----------------------------------
2014-01-15 13:46:01
http://php5.kiev.ua/manual/ru/language.generators.overview.html
Same example, different results:

----------------------------------
           |  time  | memory, mb |
----------------------------------
| not gen  | 0.7589 | 146.75     |
|---------------------------------
| with gen | 0.7469 | 8.75       |
|---------------------------------

Time in results varying from 6.5 to 7.8 on both examples.
So no real drawbacks concerning processing speed.
2014-02-17 16:59:27
http://php5.kiev.ua/manual/ru/language.generators.overview.html
Here's how to detect loop breaks, and how to handle or cleanup after an interruption.

<?php
   
function generator()
    {
       
$complete false;
        try {

            while ((
$result some_function())) {
                yield 
$result;
            }
           
$complete true;

        } finally {
            if (!
$complete) {
               
// cleanup when loop breaks 
           
} else {
               
// cleanup when loop completes
           
}
        }

       
// Do something only after loop completes
   
}
?>
2015-12-21 14:21:09
http://php5.kiev.ua/manual/ru/language.generators.overview.html
Bear in mind that execution of a generator function is postponed until iteration over its result (the Generator object) begins. This might confuse one if the result of a generator is assigned to a variable instead of immediate iteration.

<?php

$some_state 
'initial';

function 
gen() {
    global 
$some_state

    echo 
"gen() execution start\n";
   
$some_state "changed";

    yield 
1;
    yield 
2;
}

function 
peek_state() {
    global 
$some_state;
    echo 
"\$some_state = $some_state\n";
}

echo 
"calling gen()...\n";
$result gen();
echo 
"gen() was called\n";

peek_state();

echo 
"iterating...\n";
foreach (
$result as $val) {
    echo 
"iteration: $val\n";
   
peek_state();
}

?>

If you need to perform some action when the function is called and before the result is used, you'll have to wrap your generator in another function.

<?php
/**
  * @return Generator
  */
function some_generator() {
    global 
$some_state;

   
$some_state "changed";
    return 
gen();
}
?>
2016-05-01 14:55:55
http://php5.kiev.ua/manual/ru/language.generators.overview.html
In addition to the note of "montoriusz at gmail dot com": https://www.php.net/manual/en/language.generators.overview.php#119275

"If you need to perform some action when the function is called and before the result is used, you'll have to wrap your generator in another function."
You can use Generator::rewind instead (https://www.php.net/manual/en/generator.rewind.php)

Sample code:
<?php
/** function/generator definition **/

echo "calling gen()...\n";
$result gen();
$result->rewind();
echo 
"gen() was called\n";

/** iteration **/
?>
2019-10-03 06:09:15
http://php5.kiev.ua/manual/ru/language.generators.overview.html

    Поддержать сайт на родительском проекте КГБ