Bir üreteç işlevi, normal bir işlev gibi görünür fakat bir üreteç tek bir değer döndürmek yerine ihtiyaç duyulduğu kadar çok değer üretir (yield).
Bir üreteç işlevi çağrıldığında üzerinde yineleme yapılabilecek bir nesne döndürür. Bu nesne üzerinde yineleme yaptığınızda (örneğin foreach ile), PHP bir değere her ihtiyaç duyuşunda nesnenin yineleme yöntemlerini çağırır ve üreteç bir değer ürettiğinde üretecin durumunu kaydeder, böylece yeni bir değere her ihtiyaç duyuluşunda üreteç kaldığı yerden devam edebilir.
Üretilecek değer kalmadığında, üreteç basitçe çıkar ve adeta bir dizi değerlerini tüketmiş gibi kod çağrılmaya devam eder.
Bilginize:
PHP 5'te, bir üreteç bir değer döndüremezdi: bu bir derleme hatasıyla sonuçlanırdı. Üreteç içinde geçerli sözdizimi boş bir return deyimi idi ve üreteci sonlandırırdı. PHP 7.0 itibariyle, üreteç değerleri döndürebilmekte ve Generator::getReturn() kullanarak bunlar alınabilmektedir.
Üreteç işlevinin kalbi yield sözcüğüdür. En basit halinde, bir yield deyimi çoğunlukla bir return deyimi gibi görünür. İşlevin çalışmasını durdurmak ve değer döndürmek yerine, yield, üreteç üzerindeki kod döngüsüne bir değer verir ve üreteç işlevini bekletir.
Örnek 1 - Basit bir yield örneği
<?php
function gen_one_to_three() {
for ($i = 1; $i <= 3; $i++) {
// yield'ler arasında $i korunur.
yield $i;
}
}
$generator = gen_one_to_three();
foreach ($generator as $value) {
echo "$value\n";
}
?>
Yukarıdaki örneğin çıktısı:
1 2 3
Bilginize:
Dahili olarak, ilişkisel olmayan dizilerdeki gibi sıralı tamsayı anahtarlar üretilen (yield) değerlerle çiftler oluşturur.
yield sözcüğünü bir ifade bağlamında kullanıyorsanız, (örneğin bir atamanın sağ tarfında) PHP5'te yield deyimini parantez içine almanız gerekir. Örneğin aşağıdaki geçerlidir:
$data = (yield $value);
Bu yapılmazsa PHP 5'te bir çözümleme hatası ortaya çıkar:
$data = yield $value;
Parantez kısıtlamaları PHP7'de uygulanmaz.
Generator::send() yöntemine
$data değiştirgesine atanan değer ya da yerine
Generator::next() çağılırsa NULL
atanır.
PHP ilişkisel dizileri de destekler ve üreteçlerin bir farkı yoktur. Basit değerlerin üretilmesinin yanında, yukarıda gösterildiği gibi, aynı anda bir anahtar da üretebilirsiniz.
Bir anahtar/değer çifti üretmenin (yielding) sözdizimi, aşağıda gösterildiği gibi, bir ilişkisel dizi tanımlamada kullanılan sözdizimine çok benzer.
Örnek 2 - Bir anahtar/değer çifti üretimi
<?php
/*
* girdi noktalı virgülle ayrılmış alanlardır,
* ilk alan anahtar olarak kullanılacak ID'dir
*/
$input = <<<'EOF'
1;PHP;Dolar imlerini sever
2;Python;Boşlukları sever
3;Ruby;Blokları sever
EOF;
function input_parser($input) {
foreach (explode("\n", $input) as $line) {
$fields = explode(';', $line);
$id = array_shift($fields);
yield $id => $fields;
}
}
foreach (input_parser($input) as $id => $fields) {
echo "$id:\n";
echo " $fields[0]\n";
echo " $fields[1]\n";
}
?>
Yukarıdaki örneğin çıktısı:
1: PHP Dolar imlerini sever 2: Python Boşlukları sever 3: Ruby Blokları sever
Evvelce gösterilen basit değerli üretimler gibi, bir anahtar/değer çiftini bir ifade bağlamı içinde üretmek, yield deyimin parantez içine alınmasını gerektirir (PHP 5 için):
$data = (yield $key => $value);
Yield deyimini değiştirgesiz kullanmak NULL
değerinin özdevinimli bir
anahtarla birlikte üretilmesini sağlar.
Örnek 3 - NULL
üretmek
<?php
function gen_three_nulls() {
foreach (range(1, 3) as $i) {
yield;
}
}
var_dump(iterator_to_array(gen_three_nulls()));
?>
Yukarıdaki örneğin çıktısı:
array(3) { [0]=> NULL [1]=> NULL [2]=> NULL }
Üreteç işlevleri üretimi, değerlerle yapabildiği gibi başvurularla da yapabilir. Bu, işlev isminin önüne bir '&' imi yerleştirip işlevlerden başvurları döndürerek yapılabilir:
Örnek 4 - Değerleri başvuruya göre üretmek
<?php
function &gen_reference() {
$value = 3;
while ($value > 0) {
yield $value;
}
}
/*
* $number döngü içinde değiştirilebilir,
* üreteç başvuruları ürettiğinden,
* gen_reference() içindeki $value değişir.
*/
foreach (gen_reference() as &$number) {
echo (--$number).'... ';
}
?>
Yukarıdaki örneğin çıktısı:
2... 1... 0...
PHP 7'de, üreteç ihalesi, değerlerin başka bir üreteçte (yield from deyimini kullanarak array veya Traversable nesnesinden) üretilmesini sağlar. Dış üreteç tüm değerleri iç üreteç, nesne veya diziden geçerliliğini yitirene kadar ürettikten sonra üretime dış üreteçte devam edilecektir.
Bir üreteç yield from ile kullanılırsa, yield from ifadesi ayrıca, iç üreteçten döndürülen değerleri de döndürecektir.
yield from anahtarları sıfırlamaz, Traversable nesnesinden veya diziden döndürülen anahtarları korur. Böylece, bazı değerler başka bir yield veya yield from ile ortaklaşılan bir anahtarla paylaşılır (bir diziye değer girilmesiyle, anahtar ilk değerlerin üzerine yazılmasına sebep olur).
Bu konuda alışılmış durum, iterator_to_array()
işlevinin öntanımlı olarak bir anahtarlı dizi döndürmesidir
(muhtemelen beklenmedik sonuçlara yol açarak).
iterator_to_array() ikinci bir değiştirgeye
sahiptir: Generator tarafından döndürülen
anahtarları yoksayarken tüm değerleri toplamak için FALSE
atanabilen
use_keys
değiştirgesi.
Örnek 5 - iterator_to_array() ile yield from
<?php
function from() {
yield 1; // key 0
yield 2; // key 1
yield 3; // key 2
}
function gen() {
yield 0; // key 0
yield from from(); // keys 0-2
yield 4; // key 1
}
// [0, 1, 2, 3, 4] dizisini almak için ikinci değiştirgeye false aktaralım
var_dump(iterator_to_array(gen()));
?>
Yukarıdaki örneğin çıktısı:
array(3) { [0]=> int(1) [1]=> int(4) [2]=> int(3) }
Örnek 6 - yield from için temel kullanım
<?php
function count_to_ten() {
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
yield 9;
yield 10;
}
function seven_eight() {
yield 7;
yield from eight();
}
function eight() {
yield 8;
}
foreach (count_to_ten() as $num) {
echo "$num ";
}
?>
Yukarıdaki örneğin çıktısı:
1 2 3 4 5 6 7 8 9 10
Örnek 7 - yield from ve dönen değerler
<?php
function count_to_ten() {
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
return yield from nine_ten();
}
function seven_eight() {
yield 7;
yield from eight();
}
function eight() {
yield 8;
}
function nine_ten() {
yield 9;
return 10;
}
$gen = count_to_ten();
foreach ($gen as $num) {
echo "$num ";
}
echo $gen->getReturn();
?>
Yukarıdaki örneğin çıktısı:
1 2 3 4 5 6 7 8 9 10