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
You can also declare generator functions in a function scope.

<?php
function gen_n($n){
    function 
gen_t($len){
        for(
$i 1$i $len$i++)
           
yield $i
    }
   
    foreach(
gen_t($n) as $out)
       
printf("%d, "$out);

   
printf("%d", ++$out);
}

gen_n(15);

?>
2015-08-02 15:45:40
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
A simple function to parse an ini configuration file
<?php
   
function parse_ini($file_path){
        if(!
file_exists($file_path)){
            throw new 
Exception("File not exists ${file_path}");
        }
       
$text fopen($file_path'r');
        while(
$line=fgets($text)){
            list(
$key$param) = explode('='$line);
           
yield $key => $param;
        }
    }
?>
//Usage : parse_ini('param.ini') // returns Generator Object
//Usage : iterator_to_array(parse_ini('param.ini')); // returns an array
2017-04-13 18:29:25
http://php5.kiev.ua/manual/ru/language.generators.overview.html
Автор:
Generators can also be a good (best) alternative for hooks.

Without generator:

<?php

class Crud 
{
    public function 
create(array $datacallable $preSave null
    {
       
$preSave && $preSave($data);

        return 
$this->saveRecord($data);
    }
}
?>

Using generator:

<?php

class Crud
{
    protected function 
runHook($hooks$key, &$arguments
    {
       
is_array($hooks) || $hooks = [$hooks];

        isset(
$hooks[$key]) && $hooks[$key]($arguments);
       
// The good thing about this is that you can create as many hooks as you like without concerning if the caller defines it or not
   
}

    public function 
create(array $data
    {
       
$hooks yield;

       
$this->runHook($hooks'preSave'$data);
        return 
$this->saveRecord($data);       
    }
}

$crud = new Crud;

// this will return a \Generator instance
$generator $crud->create(['firstname' => 'foo''lastname' => 'bar']);

// this is where we define our hook(s)
$generator->send([
   
'preSave' => function(&$data) {
       
$data['lastname'] = 'baz';
    }, 
// and we can have as many as we want here
]);

// getting the result of the "create" method call.
$result $generator->getReturn();
2017-07-19 19:34:16
http://php5.kiev.ua/manual/ru/language.generators.overview.html

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