Iterator or IteratorAggregate?

Fabien Potencier

June 24, 2010

In my last two posts, I talked about PHP iterators. Here is a quick tip on the same topic.

If you have ever used iterators in your code, you have probably implemented the Iterator interface. Objects of a class that implements Iterator can be iterated over with the foreach loop:

$foo = new Foo();
 
foreach ($foo as $key => $value) {
    // do something with $key and $value
}
 

The Iterator interface has five simple methods that must be implemented:

class Foo implements Iterator
{
    protected $attributes;
 
    public function rewind()
    {
        reset($this->attributes);
    }
 
    public function current()
    {
        return current($this->attributes);
    }
 
    public function key()
    {
        return key($this->attributes);
    }
 
    public function next()
    {
        return next($this->attributes);
    }
 
    public function valid()
    {
        return false !== current($this->attributes);
    }
}
 

The IteratorAggregate interface is quite similar (both interfaces implement Traversable) but creates an external Iterator. But when the iterator is based on an array, creating an external Iterator for this array gives you a more concise and more readable code:

class Foo implements IteratorAggregate
{
    protected $attributes;
 
    public function getIterator()
    {
        return new ArrayIterator($this->attributes);
    }
}
 

Discussion

gravatar jblotus  — June 25, 2010 00:32   #1
you got my attention with this article but i think it is too vague without a practical real world example so I can understand where I would use this.
gravatar Michael Gauthier  — June 25, 2010 02:22   #2
One practical application of IteratorAggregate would be supporting nested iteration.

If you implement Iterator and try to iterate the same object inside an existing iteration, you'll mess up your internal index.

With IteratorAggregate, you can return a new external iterator object for each iteration.
gravatar David Muir  — June 25, 2010 02:46   #3
I just discovered IteratorAggregate + ArrayIterator recently. I don't know why I never picked up on it before. I used to just implement the Iterator interface each time... :-D

@jblotus: The two code examples are functionally identical. The point of the article is not to show how Iterators can be used. Fabien already wrote about that earlier. It's about making Traversable classes simpler, and easier to maintain.
gravatar Stephan Hochdoerfer  — June 25, 2010 09:25   #4
IteratorAggregate helps to separate concerns. In case you want to iterate over the members of an business object I`d rather implement the IteratorAggregate interface instead of turning the business object into an iterator itself.
gravatar S├ębastien Charrier  — June 28, 2010 10:07   #5
Another argument : IteratorAggregate is a LOT faster than Iterator.
gravatar Nikita Popov  — July 11, 2010 14:20   #6
As Sebastien already said returning *a built in* Iterator instead of implementing one yourself is way faster, because this saves PHP calling three user-land functions per iteration. But what I would like to know, how it is with memory usage? Will using ArrayIterator result in higher memory usage? Anyone done some tests?
gravatar David Muir  — August 17, 2010 15:00   #7
Haven't tested, but I was under the impression that iterators are less memory intensive. That could be compared to the old days of foreach creating an array copy rather than using a refernce Not sure about the technical details here. Just remember seeing "optimisation tips" saying to use
foreach(array_keys($array) as $key){
...
}
to avoid duplicating the array.

I guess it all depends on what you're iterating over.