Samuel Front
Samuel Front

Reputation: 153

Yielding in IteratorAggregates with Foreach in PHP

I have a class in PHP with a private array field, and implement IteratorAggregate by walking over this array and yielding new values.

class Item implements IteratorAggregate{
    private static $items = array(
        'a' => array(1, 2);
        'b' => array(3, 4);
        'c' => array(5, 6);
    );

    public function getIterator() {
        return array_walk(Item::$items, function ($value, $key) {
            yield array('letter' => $key, 'number' => $value[0]);
        });
    }
}

I then try to pass a new instance of this class to a foreach clause:

foreach((new Item()) as $value){
     process($value);
}

However, this fails miserably:

Fatal error: Uncaught exception 'Exception' with message 'Objects returned by Item::getIterator() must be traversable or implement interface Iterator'

However, from my understanding, my class is traversable because it implements IteratorAggregate.

Any suggestions as to what I'm doing wrong? I could rewrite this to turn the yield into an array, but that doesn't explain why I'm getting the error that I'm getting.

Upvotes: 1

Views: 1293

Answers (1)

Wouter J
Wouter J

Reputation: 41934

In your code, the yield applies to the closure, not to the method. This means that the closure passed to array_walk is a generator, getIterator is now just a normal method that returns the return value of array_walk (which is an array).

Simply use foreach instead of array_walk:

class Item implements IteratorAggregate{
    private static $items = array(
        'a' => array(1, 2),
        'b' => array(3, 4),
        'c' => array(5, 6),
    );

    public function getIterator() {
        foreach (self::$items as $key => $value) {
            yield array('letter' => $key, 'number' => $value[0]);
        }
    }
}

Upvotes: 4

Related Questions