Lost In Iteration

TL;DR: When using iterator_to_array() with default parameters, some extracted elements are lost if they share the same key.

Problem Context

When writing tests I often find myself using iterator_to_array() to quickly inspect the items contained in a traversable object:

$items = iterator_to_array($myTraversableObject);

Converting an object into an array this way allows me to rely on the huge amount of PHP’s array functions for further checks.

However, there is a catch when using iterator_to_array() without its second argument: Usually your are only interested in the elements, but iterator_to_array() keeps the keys of the \Traversable per default. And it is perfectly fine when a \Traversable uses the same key multiple times.
Wonder how this can happen (accidentally)? Have a look at this simple example:

$firstPart = new \ArrayIterator(array('a', 'b', 'c'));
$secondPart = new \ArrayIterator(array('d', 'e', 'f'));
$combined = new \AppendIterator();

The idea is simple: An \AppendIterator is used to concatenate several other iterators. The keys of the source iterators ($firstPart and $secondPart) were not explicitly specified as they are not important. Each source iterator uses numeric keys in the range 0..2.

This works as expected in most cases:

foreach ($combined as $value) {
    // This loop iterates over all 6 values.

In contrast to that…


… returns only the 3 values ["d", "e", "f"], as elements with same keys are overwritten. In a scenario like the one above this behavior is… unexpected at best.


The second parameter of iterator_to_array() controls the handling of keys. When setting it to false, the keys of the traversed object are ignored and a numerically indexed array with all values is returned:

iterator_to_array($combined, false);

This returns all 6 values (["a", "b", "c", "d", "e", "f"]), as the keys are not taken into account.
However, there is a drawback: Each caller has to be aware of the possible problem and must remember to explicitly define the key handling behavior.

If the desired behavior is a numerically indexed concatenation of several iterators, then that should be modeled explicitly. We can rely on established design patterns and create a simple decorator that enforces the desired key behavior:

class NumericKeysIterator extends \IteratorIterator {
     * @var integer
    private $key = 0;

    public function rewind() {
        $this->key = 0;

     * @return integer
    public function key() {
        return $this->key;
    public function next() {

When plugging this together, we get an iterator whose keys are always ascending integer values:

$combined = new NumericKeysIterator($combined);
$items = iterator_to_array($combined);

Therefore, iterator_to_array() returns all 6 items even if the default key handling is used. The caller does not have to think about the edge case with duplicated keys.


Pass false as second parameter to iterator_to_array(), if you are only interested in the elements of an iterator.
When designing a \Traversable, think about the desired key behavior and model it explicitly.

Improve me on GitHub