Using SPL Iterators, Part 2

Share this article

In part one of this series I introduced you to some of the SPL Iterators and how to use them. There may be times however when the provided iterators are insufficient for your needs and you’ll want to roll your own custom iterator. Luckily, SPL provides interfaces that will allow you to do just that.

For an object to be traversable in a foreach loop, PHP requires that it be an instance of Traversable. You cannot however implement this interface directly (though you can use it in instaceof checks); instead you’ll need to implement either SPL’s Iterator or IteratorAggregate interfaces.

Iterator

Iterator allows you to create objects that are are iterated directly or for the creation of external Iterators. It specifies five methods that need to be implemented: rewind(), current(), key(), next(), and valid().

Assume we have a library of books and we want to be able to iterate over the collection of books in the library. Here’s an example that does so by implementing Iterator:

<?php
class Library implements Iterator
{
    // an internal pointer to the current position
    // in the dataset
    protected $position = 0;

    // an array of the titles of the books in the library
    protected $books = [
        "Professional PHP Programming",
        "Programming Perl",
        "A Byte of Python",
        "The Ruby Way"
    ];

    // this method takes the pointer back to the beginning
    // of the dataset to restart the iteration
    public function rewind() {
        echo "rewinding <br>";
        $this->position = 0;
    }

    // this method returns the value at the current
    // position of the dataset
    public function current() {
        echo "current <br>";
        return $this->books[$this->position];
    }

    // this should return the current value of the pointer
    public function key() {
        echo "key <br>";
        return $this->position;
    }

    // this method moves the pointer to the next value
    // in the data set
    public function next() {
        echo "next <br>";
        ++$this->position;
    }

    / /this returns a boolean indicating if the there
    // is data at the current position in the dataset
    public function valid() {
        echo "valid <br>";
        return isset($this->books[$this->position]);
    }
}

$library = new Library();
foreach ($library as $key => $value) {
    echo $key . ": " . $value . "<br>";
}

The output of the above code is:

rewinding 
valid 
current 
key 
0: Professional PHP Programming
next 
valid 
current 
key 
1: Programming Perl
next 
valid 
current 
key 
2: A Byte of Python
next 
valid 
current 
key 
3: The Ruby Way
next 
valid

When the iteration starts, PHP calls the rewind() method to take the pointer back to the beginning of the dataset. It then checks to see if there is valid data at that point by calling valid(). If it receives true, it then calls current() to get the value at that point in the iteration. Since you are asking for the $key in the loop construct, PHP calls the key() method to get the value of the current key. PHP then calls next() to advance the pointer and calls valid() again to check if there is valid data. The cycle continues until valid() returns false.

Iterator gives you the ability to implement very powerful iterators from scratch and is best suited for creating internal iterators like above. Of course, the example above is simple and is only intended to give you an overview of the required methods and how they work. Ideally, with a dataset such as the one above (an array), you will be better off using IteratorAggregate.

IteratorAggregate

IteratorAggregate requires you to implement a single method, getIterator(). This method is required to return an external iterator which will be used for iteration. Rewriting the example above to use IteratorAggregate gives us:

<?php
class Library implements IteratorAggregate
{
    protected $books = [
        "Professional PHP Programming",
        "Programming Perl",
        "A Byte of Python",
        "The Ruby Way"
    ];

    // return an Iterator of your data
    public function getIterator() {
        echo "getIterator <br>";
        return new ArrayIterator($this->books);
    }
}

$library = new Library();
foreach($library as $key => $value) {
    echo $key . ": " . $value . "<br>";
}

The output of the above code is:

getIterator 
0: Professional PHP Programming
1: Programming Perl
2: A Byte of Python
3: The Ruby Way

At the start of the iteration, PHP calls getIterator() which returns an iterator, in this case SPL’s ArrayIterator. PHP then makes all the appropriate calls on that iterator. Basically, you hand over the data to the external iterator which does all the work for you while you sit back and relax.

Since both interfaces provide the same functionality, knowing whether to use Iterator or IteratorAggregate can sometimes be tricky. As a general rule of thumb, you should use Iterator when you need to actually define the behavior of your iterator from scratch because IteratorAggregate is not a good fit.

Summary

I’ve introduced you to two interfaces that allow you to create your own iterators in this article. Hopefully, you understand how easy it is to create your very own iterators and are ready to add this cool feature of SPL to your arsenal. Of course, if you feel I missed something or erred in any way, or even just have something to share, please don’t hesitate to let me know in the comments!

Image via Mushakesa / Shutterstock

Stefan FroelichStefan Froelich
View Author

Stefan "frostymarvelous" Froelich is a hobby programmer who loves to work with scripting languages. He's always up for a challenge and the opportunity to learn something new. At times he freelances, but mostly helps out in forums and various Q&A sites. His aim is to contribute to the community that has given so much to him over the years. Stefan dreams of a world where knowledge is freely shared.

Intermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week