Note: Here's another great post from the community, specifically from Cal Evans! Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term “computer” loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC’s. For the past 13 years Cal has worked with PHP and MySQL on Linux, OSX, and Windows. He has built a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys writing books and sharing what he has discovered. His most recent being Signaling PHP.
When PHP 5 arrived there was great excitement and rejoicing over the new object model. For many of us who struggled through PHP 4's "Object Based" paradigm, PHP 5 was a ray of sunshine. We were so excited that many of us lost sight of another important addition to PHP 5, the Standard PHP Library (SPL).
Since then, the excitement over the object model has died down. Many authors and speakers have talked about, blogged about, and written about the SPL. However the SPL itself is a very large topic. We are going to narrow our focus down a bit to a subtopic of a specific section of the SPL. This blog post will deal with the
OuterIterator. This is an interface defined in the SPL and used by several of the built in iterators.
What is an Outer Iterator?
Iterators, as the name implies allow you to iterate over a collection. In many cases that collection is an array. However, PHP allows you to iterate over many other types of collections. A directory structure, XML, even database cursors can all be easily iterated over using the built in iterator classes. There are times however, when what you need to iterate over...is another
Iterator that is itself iterating over something. For this very instance, the SPL has defined an interface for us the
OuterIterator interface extends the
Iterator interface but adds a single method,
Why use it
The easiest way to understand the
OuterIterator is by looking at an example. The easiest
OuterIterator example to understand is the
FilterIterator isn't a concrete iterator, it is a abstract class that implements
OuterIterator. If you understand it then you understand the basics of the
OuterIterator and begin to understand why you would use it.
FilterIterator is exactly what you think it should be from it's name. It is a way to intercept the process of iterating and refuse to return the current element based on your logic.
FilterIterator is an abstract class that you extend to create your own class. Because it extends the
IteratorIterator class, you do not have to flesh out the five main methods of an iterator yourself. With
FilterIterator you only have to write code for the method,
accept() returns a boolean that decides whether the current element of the inner iterator is displayed or skipped. Execute whatever code you wish inside of
accept() but if it returns
false, the element will be skipped and the inner iterator will have it's pointer moved to the next element and the process repeated. This will continue until either the end of the inner
Iterator is reached or
accept() returns true.
FilterIterator uses the one method of the
getInnerIterator(), to process the records of the inner iterator as the methods of
FilterIterator are called.
To understand the process, we need to think about how the
FilterIterator works internally. Normally, if you drop an iterable item into a
foreach loop, the loop does this:
- Internally PHP first calls
rewind()to reset the pointer of the iterator. In our case, since we have an
OuterIterator, that is called out the
OuterIteratorand passed down to the main
accept()is called on the first element in the inner
Iterator. If false is returned, the inner
next()is called to move the pointer and the outer iterator's
accept()method is called again. This continue until the end of the inner iterator is reached, or
accept()returns true, the
valid()is called to ensure we are looking at a valid element.
valid()returns true, the
current()is called to return the value of the current element.
- The the code inside the
We could have also written our
foreach to return the current key as well.
This would have added a 6th step to our list above where
key() would have been called.
So now we know that
accept() is called early on in the process and will continue to be called until
true or the end of the inner iterator is reached.
To demonstrate what we have just learned, let's build a quick FilterIterator that will iterate through the Seven Dwarves.
You can copy that code, paste it into a file, save it as
test.php (oh don't look at me like you don't have a hundred
test.php files scattered around your file system) and then run it with
This is the line that actually instantiates the
OuterIterator class we created.
The second parameter,
"y"` determines which of the dwarf names get printed. If you run it as-is, every dwarf name containing a 'y' will be printed.
More importantly though, you see when each method is called in the process. If you want to print an unfiltered list, use
" " as the second parameter. All of the names in the
$dwarves array end in a space, so filtering for the name containing a space will print each of them.
Let's look at a snippet of the output:
- Any line starting with
++was output as part of the
foreachloop. Any line starting with
--is a marker indicating that a method was called.
- The first line tells us that the second element, "Sleepy" was output.
next()method was called to move the pointer. Because this was called on our implementation of an
OuterInterface, it was passed through to the inner
accept()method was called. Notice that since "Bashful " does not contain a "y", we ignore it. Internally, meaning at the Zend Engine level, the
next()method on the inner
Iteratorwas called. We don't see it here because we are only tracking calls on methods of our
accept()is called again to check the new current value in the inner
Iterator. "Sneezy " contains a "y" so we accept it.
current()is called to populate
key()is called to populate
- "Sneezy" is output.
Now you see exactly how an
OuterIterator is used. You also have a good understanding of how iterators work under the hood.
When do you use it?
OuterIterators are used anytime you need to manipulate an
Iterator. It is a wrapper that adds functionality to an existing
Iterator The following is a list of the concrete iterators built into PHP via the SPL that implement the
OuterIterator. As you can see the Core Developers have given us many tools to work with and covered a lot of the situations where you would normally write the code to implement.
OuterIterator is a powerful tool, and yet relatively unknown tool. If you search Github for
OuterIterator you will come up with 1,106 PHP files that have one. (as opposed to 644,917 files that contain the command "eval". That statistic alone should make any professional developer cry.)
Once you understand the
OuterIterator you will find places in your code to use it, or one of it's extenders. The more you know about Iterators and the SPL in general, the faster you will be able to create your projects.