1.2.1119. Should Yield With Key

iterator_to_array() overwrite generated values with the same key.

PHP generators are based on the yield keyword. They also delegate some generating to other methods, with yield from.

When delegating, yield from uses the keys that are generated with yield, and otherwise, it uses auto-generated index, starting with 0.

The trap is that each yield from reset the index generation and start again with 0. Coupled with iterator_to_array(), this means that the final generated array may lack some values, while a foreach() loop would yield all of them.

Thanks to Holger Woltersdorf for pointing this.

<?php

function g1() : Generator {
     for ( $i = 0; $i < 4; $i++ ) { yield $i; }
}

function g2() : Generator {
     for ( $i = 5; $i < 10; $i++ ) { yield $i; }
}

function aggregator() : Generator {
     yield from g1();
     yield from g2();
}

print_r(iterator_to_array());

/*
Array
(
    [0] => 6
    [1] => 7
    [2] => 8
    [3] => 9
    [4] => 4  // Note that 4 and 5 still appears
    [5] => 5  // They are not overwritten by the second yield
)
*/


foreach ( aggregator() as $i ) {
     print $i.PHP_EOL;
}

/*
0  // Foreach has no overlap and yield it all.
1
2
3
4
5
6
7
8
9
*/

?>

See also Generator syntax and Yielding values with keys.

1.2.1119.1. Suggestions

  • Use iterator_to_array() on each generator separately, and use array_merge() to merge all the arrays.

  • Always yield with distinct keys

  • Avoid iterator_to_array() and use foreach()

1.2.1119.2. Specs

Short name

Functions/ShouldYieldWithKey

Rulesets

All, Analyze, CE, CI-checks, Top10

Exakat since

1.5.2

PHP Version

All

Severity

Major

Time To Fix

Slow (1 hour)

Precision

Very high

Features

yield, key

Available in

Entreprise Edition, Community Edition, Exakat Cloud