Résumé sur les générateurs

(PHP 5 >= 5.5.0)

Les générateurs fournissent une façon simple de mettre en place des itérateurs sans le coût ni la complexité du développement d'une classe qui implémente l'interface Iterator.

Un générateur vous permet d'écrire du code qui utilise foreach pour parcourir un jeu de données, sans avoir à construire un tableau en mémoire pouvant conduire à dépasser la limite de la mémoire ou nécessiter un temps important pour sa génération. Au lieu de cela, vous pouvez écrire une fonction générateur, qui est identique à une fonction normale, mis à part le fait qu'au lieu de retourner une seule fois, un générateur peut utiliser yield autant de fois que nécessaire, afin de fournir les valeurs à parcourir.

Un exemple simple de ce mécanisme est la ré-implémentation de la fonction range() sous la forme d'un générateur. La fonction standard range() doit générer un tableau contenant chaque valeur, et le retourner, ce qui peut conduire à des tableaux de taille importante : par exemple, l'appel du code range(0, 1000000) peut consommer nettement plus de 100 Mo de mémoire.

Comme alternative, nous pouvons implémenter un générateur xrange(), qui n'aura en besoin mémoire que la seule création d'un objet Iterator, et devra garder trace en interne du statut courant du générateur, ce qui revient à une consommation mémoire inférieure à 1 Ko.

Exemple #1 Implémentation de la fonction range() sous la forme d'un générateur

<?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;
        }
    }
}

/*
 * Notez que les fonctions range() et xrange() produisent le
 * même affichage, ci-dessous.
 */

echo 'Nombres impairs à un seul chiffre depuis range() :  ';
foreach (
range(192) as $number) {
    echo 
"$number ";
}
echo 
"\n";

echo 
'Nombres impairs à un seul chiffre depuis xrange() : ';
foreach (
xrange(192) as $number) {
    echo 
"$number ";
}
?>

L'exemple ci-dessus va afficher :

Nombres impairs à un seul chiffre depuis range() :  1 3 5 7 9
Nombres impairs à un seul chiffre depuis xrange() : 1 3 5 7 9
add a note add a note

User Contributed Notes 4 notes

up
14
bloodjazman at gmail dot com
1 year 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
7
dc at libertyskull dot com
9 months 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
1
lubaev
10 months 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
-5
swen dot zanon at geoglis dot de
8 months ago
I always get these results (approximately), no matter how many times I request:

without generator / with array:
time: 2.3667 memory (byte): 298057728

with generator:
time: 8.4556 memory (byte): 9175040
To Top