PHP 7.0.0 RC 1 Released

Generators - Visão Geral

(PHP 5 >= 5.5.0)

Os generators fornecem uma maneira fácil de implementar simples iterators sem a sobrecarga ou complexidade de criar uma classe que implemente a interface Iterator.

Um generator permite que você escreva código que use foreach para iterar em um conjunto de dados sem precisar construir um array em memória, o que pode fazer com que o limite de memória seja ultrapassado ou exigir uma quantidade considerável de tempo de processamento para a geração. Em vez disso, você pode escrever uma função generator, que é o mesmo que uma função normal, exceto que ao invés do retorno ocorrer única vez, um generator pode entregar o resultado quantas vezes forem necessárias para permitir que os valores sejam iterados.

Um exemplo simples para isso é reimplementar a função range(). A função range() padrão tem que gerar um array com cada valor dentro dele e retorná-lo, o que pode resultar em grandes arrays: por exemplo, chamando range(0, 1000000) irá resultar numa utilização de memória de mais de 100 MB.

Como alternativa, nós podemos implementar um generator xrange(), que só precisará de memória suficiente para criar um objeto Iterator e acompanhar o estado atual do generator internamente, que utiliza menos de 1 kilobyte de memória.

Exemplo #1 Implementando range() como um 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 ";
}
?>

O exemplo acima irá imprimir:

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

User Contributed Notes 4 notes

up
41
bloodjazman at gmail dot com
2 years ago
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;
}
up
21
dc at libertyskull dot com
1 year ago
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.
up
14
lubaev
1 year ago
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_time, 4), "\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_time, 4), "\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%      |
----------------------------------
up
-1
damirchilo at outlook dot com
25 days ago
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);

?>
To Top