How does RecursiveIteratorIterator work?
The PHP manual has nothing much documented or explained. What is the difference between IteratorIterator and RecursiveIteratorIterator?
Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.
Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.
Lost your password? Please enter your email address. You will receive a link and will create a new password via email.
Please briefly explain why you feel this question should be reported.
Please briefly explain why you feel this answer should be reported.
Please briefly explain why you feel this user should be reported.
RecursiveIteratorIteratoris a concreteIteratorimplementing tree traversal. It enables a programmer to traverse a container object that implements theRecursiveIteratorinterface, see Iterator in Wikipedia for the general principles, types, semantics and patterns of iterators.In difference to
IteratorIteratorwhich is a concreteIteratorimplementing object traversal in linear order (and by default accepting any kind ofTraversablein its constructor), theRecursiveIteratorIteratorallows looping over all nodes in an ordered tree of objects and its constructor takes aRecursiveIterator.In short:
RecursiveIteratorIteratorallows you to loop over a tree,IteratorIteratorallows you to loop over a list. I show that with some code examples below soon.Technically this works by breaking out of linearity by traversing all of a nodes’ children (if any). This is possible because by definition all children of a node are again a
RecursiveIterator. The toplevelIteratorthen internally stacks the differentRecursiveIterators by their depth and keeps a pointer to the current active subIteratorfor traversal.This allows to visit all nodes of a tree.
The underlying principles are the same as with
IteratorIterator: An interface specifies the type of iteration and the base iterator class is the implementation of these semantics. Compare with the examples below, for linear looping withforeachyou normally do not think about the implementation details much unless you need to define a newIterator(e.g. when some concrete type itself does not implementTraversable).For recursive traversal – unless you do not use a pre-defined
Traversalthat already has recursive traversal iteration – you normally need to instantiate the existingRecursiveIteratorIteratoriteration or even write a recursive traversal iteration that is aTraversableyour own to have this type of traversal iteration withforeach.Technical differences in short:
IteratorIteratortakes anyTraversablefor linear traversal,RecursiveIteratorIteratorneeds a more specificRecursiveIteratorto loop over a tree.IteratorIteratorexposes its mainIteratorviagetInnerIerator(),RecursiveIteratorIteratorprovides the current active sub-Iteratoronly via that method.IteratorIteratoris totally not aware of anything like parent or children,RecursiveIteratorIteratorknows how to get and traverse children as well.IteratorIteratordoes not need a stack of iterators,RecursiveIteratorIteratorhas such a stack and knows the active sub-iterator.IteratorIteratorhas its order due to linearity and no choice,RecursiveIteratorIteratorhas a choice for further traversal and needs to decide per each node (decided via mode perRecursiveIteratorIterator).RecursiveIteratorIteratorhas more methods thanIteratorIterator.To summarize:
RecursiveIteratoris a concrete type of iteration (looping over a tree) that works on its own iterators, namelyRecursiveIterator. That is the same underlying principle as withIteratorIerator, but the type of iteration is different (linear order).Ideally you can create your own set, too. The only thing necessary is that your iterator implements
Traversablewhich is possible viaIteratororIteratorAggregate. Then you can use it withforeach. For example some kind of ternary tree traversal recursive iteration object together with the according iteration interface for the container object(s).Let’s review with some real-life examples that are not that abstract. Between interfaces, concrete iterators, container objects and iteration semantics this maybe is not a that bad idea.
Take a directory listing as an example. Consider you have got the following file and directory tree on disk:
While a iterator with linear order just traverse over the toplevel folder and files (a single directory listing), the recursive iterator traverses through subfolders as well and list all folders and files (a directory listing with listings of its subdirectories):
You can easily compare this with
IteratorIteratorwhich does no recursion for traversing the directory tree. And theRecursiveIteratorIteratorwhich can traverse into the tree as the Recursive listing shows.At first a very basic example with a
DirectoryIteratorthat implementsTraversablewhich allowsforeachto iterate over it:The exemplary output for the directory structure above then is:
As you see this is not yet using
IteratorIteratororRecursiveIteratorIterator. Instead it just just usingforeachthat operates on theTraversableinterface.As
foreachby default only knows the type of iteration named linear order, we might want to specify the type of iteration explicitly. At first glance it might seem too verbose, but for demonstration purposes (and to make the difference withRecursiveIteratorIteratormore visible later), lets specify the linear type of iteration explicitly specifying theIteratorIteratortype of iteration for the directory listing:This example is nearly identical with the first one, the difference is that
$filesis now anIteratorIteratortype of iteration forTraversable$dir:As usual the act of iteration is performed by the
foreach:The output is exactly the same. So what is different? Different is the object used within the
foreach. In the first example it is aDirectoryIteratorin the second example it is theIteratorIterator. This shows the flexibility iterators have: You can replace them with each other, the code insideforeachjust continue to work as expected.Lets start to get the whole listing, including subdirectories.
As we now have specified the type of iteration, let’s consider to change it to another type of iteration.
We know we need to traverse the whole tree now, not only the first level. To have that work with a simple
foreachwe need a different type of iterator:RecursiveIteratorIterator. And that one can only iterate over container objects that have theRecursiveIteratorinterface.The interface is a contract. Any class implementing it can be used together with the
RecursiveIteratorIterator. An example of such a class is theRecursiveDirectoryIterator, which is something like the recursive variant ofDirectoryIterator.Lets see a first code example before writing any other sentence with the I-word:
This third example is nearly identical with the first one, however it creates some different output:
Okay, not that different, the filename now contains the pathname in front, but the rest looks similar as well.
As the example shows, even the directory object already imlements the
RecursiveIteratorinterface, this is not yet enough to makeforeachtraverse the whole directory tree. This is where theRecursiveIteratorIteratorcomes into action. Example 4 shows how:Using the
RecursiveIteratorIteratorinstead of just the previous$dirobject will makeforeachto traverse over all files and directories in a recursive manner. This then lists all files, as the type of object iteration has been specified now:This should already demonstrate the difference between flat and tree traversal. The
RecursiveIteratorIteratoris able to traverse any tree-like structure as a list of elements. Because there is more information (like the level the iteration takes currently place), it is possible to access the iterator object while iterating over it and for example indent the output:And output of Example 5:
Sure this does not win a beauty contest, but it shows that with the recursive iterator there is more information available than just the linear order of key and value. Even
foreachcan only express this kind of linearity, accessing the iterator itself allows to obtain more information.Similar to the meta-information there are also different ways possible how to traverse the tree and therefore order the output. This is the Mode of the
RecursiveIteratorIteratorand it can be set with the constructor.The next example will tell the
RecursiveDirectoryIteratorto remove the dot entries (.and..) as we do not need them. But also the recursion mode will be changed to take the parent element (the subdirectory) first (SELF_FIRST) before the children (the files and sub-subdirs in the subdirectory):The output now shows the subdirectory entries properly listed, if you compare with the previous output those were not there:
The recursion mode therefore controls what and when a brach or leaf in the tree is returned, for the directory example:
LEAVES_ONLY(default): Only list files, no directories.SELF_FIRST(above): List directory and then the files in there.CHILD_FIRST(w/o example): List files in subdirectory first, then the directory.Output of Example 5 with the two other modes:
When you compare that with standard traversal, all these things are not available. Recursive iteration therefore is a little bit more complex when you need to wrap your head around it, however it is easy to use because it behaves just like an iterator, you put it into a
foreachand done.I think these are enough examples for one answer. You can find the full source-code as well as an example to display nice-looking ascii-trees in this gist: https://gist.github.com/3599532
Example 5 demonstrated that there is meta-information about the iterator’s state available. However, this was purposefully demonstrated within the
foreachiteration. In real life this naturally belongs inside theRecursiveIterator.A better example is the
RecursiveTreeIterator, it takes care of indenting, prefixing and so on. See the following code fragment:The
RecursiveTreeIteratoris intended to work line by line, the output is pretty straight forward with one little problem:When used in combination with a
RecursiveDirectoryIteratorit displays the whole pathname and not just the filename. The rest looks good. This is because the file-names are generated bySplFileInfo. Those should be displayed as the basename instead. The desired output is the following:Create a decorator class that can be used with
RecursiveTreeIteratorinstead of theRecursiveDirectoryIterator. It should provide the basename of the currentSplFileInfoinstead of the pathname. The final code fragment could then look like:These fragments including
$unicodeTreePrefixare part of the gist in Appendix: Do It Yourself: Make theRecursiveTreeIteratorWork Line by Line..