When you use yield
, is creating a Generator Function, and according to the documentation, her return is always an object of the type Generator
. We can see that changing your code a little bit:
function getValues(&$iterator){
while($iterator < 1) {
$iterator++;
yield ['valor1', 'valor2'];
}
}
$iterator = 0;
echo "primeira chamada\n";
$values = getValues($iterator);
var_dump($values);
if ($values) {
print_r(iterator_to_array($values));
} else {
echo "generator terminou";
}
echo "\nsegunda chamada\n";
$values = getValues($iterator);
var_dump($values);
if ($values) {
print_r(iterator_to_array($values));
} else {
echo "generator terminou";
}
echo "\nterceira chamada\n";
$values = getValues($iterator);
var_dump($values);
if ($values) {
print_r(iterator_to_array($values));
} else {
echo "generator terminou";
}
Instead of a loop, I called getValues
several times to see what happens with each iteration. The output is:
primeira chamada
object(Generator)#8 (0) {
}
Array
(
[0] => Array
(
[0] => valor1
[1] => valor2
)
)
segunda chamada
object(Generator)#10 (0) {
}
Array
(
)
terceira chamada
object(Generator)#8 (0) {
}
Array
(
)
That is, on the first call it returns the array and from the second on, it has no value anymore, because it has already ended.
But getValues
always returns the object Generator
, and this is always evaluated as true
. That’s why your loop never ends.
The ideal is to iterate with a foreach
even then it automatically closes when the Generator break up.
But if you want to do it with while
, one way is to check whether a Generator already "finished", using the method valid
:
function getValues(&$iterator){
while($iterator < 1) {
$iterator++;
yield ['valor1', 'valor2'];
}
}
$iterator = 0;
echo "primeira chamada\n";
$values = getValues($iterator);
var_dump($values);
if ($values->valid()) { // <--- aqui
print_r(iterator_to_array($values));
} else {
echo "generator terminou";
}
echo "\nsegunda chamada\n";
$values = getValues($iterator);
var_dump($values);
if ($values->valid()) { // <--- aqui
print_r(iterator_to_array($values));
} else {
echo "generator terminou";
}
echo "\nterceira chamada\n";
$values = getValues($iterator);
var_dump($values);
if ($values->valid()) { // <--- aqui
print_r(iterator_to_array($values));
} else {
echo "generator terminou";
}
Now he’s no longer in if
from the second iteration, as there are no more values in the Generator. The exit is:
primeira chamada
object(Generator)#8 (0) {
}
Array
(
[0] => Array
(
[0] => valor1
[1] => valor2
)
)
segunda chamada
object(Generator)#10 (0) {
}
generator terminou
terceira chamada
object(Generator)#8 (0) {
}
generator terminou
So in your case you could use a loop and only interrupt it when the Generator finish (using valid
to find out if you’re finished):
while(1) {
$values = getValues($iterator);
if (! $values->valid()) break; // generator terminou, interrompe o while
var_dump($iterator);
$values = iterator_to_array($values);
print_r($values);
}
Or do it all at once in the condition of while
:
while(($values = getValues($iterator))->valid()) {
var_dump($iterator);
$values = iterator_to_array($values);
print_r($values);
}
Although you don’t have to keep calling getValues
all the time:
$gen = getValues($iterator);
while ($gen->valid()) {
$values = $gen->current(); // pega o valor atual
var_dump($iterator);
print_r($values);
$gen->next(); // avança para o próximo
}
But I still prefer to use the foreach
even:
foreach (getValues($iterator) as $values) {
var_dump($iterator);
print_r($values);
}
thank you very much for the detailed explanation!
– Thiago