Üreteçlere giriş

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

Üreteçler, Iterator arayüzünü gerçekleyen bir sınıfı gerçeklemenin karmaşıklığı ve giderleri olmaksızın basit yineleyicileri gerçeklemek için kolay bir yol sağlar.

Bir üreteç, önemli miktarda işlem süresi gerektirmeden ve bellek sınırını aşabilecek bir dizi oluşturmaya gerek kalmadan bir dizi veriyi yinelemek için foreach kullanan bir kod yazmanızı sağlar. Bunun için bir üreteç işlevini normal bir işlev yazar gibi yazabilirsiniz, siz bir kez değer döndürmeyi, beklerken bir üreteç üzerinde yinelenecek değerleri sağlamak için gerektiği kadar çok kez değer döndürebilir.

Buna basit bir örnek range() işlevini bir üreteç olarak tekrar gerçeklemektir. Standard range() işlevi her değeri ve onun dönen değerini içeren devasa boyutlara ulaşabilen bir dizi üretmek zorundadır. Örneğin, range(0, 1000000) çağrısı 100MB üzerinde bellek kullanımıyla sonuçlanabilir.

Bir seçenek olarak, bir xrange() üreteci gerçekleyebiliriz, tek ihtiyacımız bir Iterator nesnesi oluşturmaya yetecek bellek ayırmak ve 1 kilobayttan azıyla dönmesi için üretecin dahili durumunu izlemektir.

Örnek 1 - range() işlevini bir üreteç olarak gerçeklemek

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

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

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

/*
* range() ve xrange() işlevlerinin ikisi de aşağıda
* benzer çıktıyı verecektir.
*/

echo 'range() içindeki bir haneli tek sayılar: ';
foreach (
range(1, 9, 2) as $number) {
echo
"$number ";
}
echo
"\n";

echo
'xrange() içindeki bir haneli tek sayılar: ';
foreach (
xrange(1, 9, 2) as $number) {
echo
"$number ";
}
?>

Yukarıdaki örneğin çıktısı:

range() içindeki bir haneli tek sayılar: 1 3 5 7 9
xrange() içindeki bir haneli tek sayılar: 1 3 5 7 9

Generator nesneleri

Bir üreteç işlevi çağrıldığında dahili Generator sınıfının yeni bir nesnesi döner. Bu nesne Iterator arayüzünü gerçekler. Bu, sadece ileri doğru yineleme yapan bir yineleme nesnesinin yaptığını yapacak ve değer gönderme ve döndürme dahil, üretecin durumunu da değiştirmeye yarayacak çağırılabilir yöntemler sağlayacaktır.

add a note add a note

User Contributed Notes 8 notes

up
173
bloodjazman at gmail dot com
11 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
40
montoriusz at gmail dot com
8 years ago
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();
}
?>
up
27
info at boukeversteegh dot nl
8 years ago
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
   
}
?>
up
9
chung1905 at gmail dot com
5 years ago
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 **/
?>
up
18
lubaev
10 years 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
13
dc at libertyskull dot com
10 years 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
-10
youssefbenhssaien at gmail dot com
7 years ago
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
up
-33
Anonymous
5 years 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 exassmples.
So no real drawbacks concerning processing speed.
To Top