Suppose I have the following recursive function to turn an array into a Generator:
function traverse(array $items): Generator {
if (!empty($items)) {
yield $items[0];
array_shift($items);
yield from traverse($items);
}
}
Running this function and iterating over the Generator through a foreach
gives the expected result:
$values = traverse(['a', 'b', 'c', 'd', 'e']);
foreach ($values as $value) {
echo $value;
}
// 'abcde' is echoed
However, when I use the built-in function iterator_to_array()
, I get the following result:
$values = iterator_to_array(traverse(['a', 'b', 'c', 'd', 'e']));
foreach ($values as $value) {
echo $value;
}
// Only 'e' is echoed
I would expect both pieces of code to behave identically, yet they don't. Why is their behavior different?
I am running this on PHP 8.1.
CodePudding user response:
From the manual:
yield from does not reset the keys. It preserves the keys returned by the Traversable object, or array. Thus some values may share a common key with another yield or yield from, which, upon insertion into an array, will overwrite former values with that key.
A common case where this matters is iterator_to_array() returning a keyed array by default, leading to possibly unexpected results. iterator_to_array() has a second parameter use_keys which can be set to false to collect all the values while ignoring the keys returned by the Generator.
Your generator is returning an item with the same key each time (because they are reset after the array_shift
call), so iterator_to_array
is overwriting each item with the following one. This is why you're only seeing the final element being returned.
If you pass the use_keys
argument to the function as false, you'll get the desired behaviour, e.g.
$values = iterator_to_array(traverse(['a', 'b', 'c', 'd', 'e']), false);