The default mode for the RecursiveIteratorIterator is to provide
leaves only. For a RecursiveDirectoryIterator, that means
files only.
In order to include directories, you can use one of the other
iteration modes that RecursiveIteratorIterator recognises: namely either
children first (directory contents are shown earlier than the directory itself) or
self first (directory contents are shown after the directory itself).
Note about snippets: short variable names and crazy line-wrapping is used to avoid horizontal scrolling due to the teeny tiny code boxes here.
Showing files and directories with SELF_FIRST
PHP Code:
$dit = new RecursiveDirectoryIterator(__DIR__);
$rit = new RecursiveIteratorIterator(
$dit, RecursiveIteratorIterator::SELF_FIRST);
foreach ($rit as $fileinfo) {
echo $fileinfo->getPathname() . PHP_EOL;
}
The above uses
RecursiveIteratorIterator::SELF_FIRST, as you should hopefully see, to iterator over
__DIR__ (or whatever path you choose) showing directories and their contents.
Now, how about getting
only directories? Well there are a couple of different ways to do this and I'll go over some just to give an idea: it's entirely up to you which you prefer (I have my favourite).
Filtering within the loop
PHP Code:
$dit = new RecursiveDirectoryIterator(__DIR__);
$rit = new RecursiveIteratorIterator(
$dit, RecursiveIteratorIterator::SELF_FIRST);
foreach ($rit as $fileinfo) {
if ( ! $fileinfo->isDir()) continue; // Skip non-directories
echo $fileinfo->getPathname() . PHP_EOL;
}
This approach is quick and might be the most obvious approach to ignoring what we don't want. It's also the most crude and easiest to get out of hand (imagine a number of different checks that you want to do).
Filtering using a Filtering Iterator
PHP Code:
class DirsOnlyFilter extends RecursiveFilterIterator {
public function __construct($path) {
// Custom constructor takes a filesystem path
parent::__construct(new RecursiveDirectoryIterator($path));
}
public function accept() {
// Only accept directories
return $this->getInnerIterator()->isDir();
}
public function getChildren() {
// Get children (directory contents) wrapped in this filter
return new DirsOnlyFilter($this->getInnerIterator()->getPathname());
}
}
$dirs = new DirsOnlyFilter(__DIR__);
$rit = new RecursiveIteratorIterator(
$dirs, RecursiveIteratorIterator::SELF_FIRST);
foreach ($rit as $fileinfo) {
echo $fileinfo->getPathname() . PHP_EOL;
}
Here we make use of a filtering iterator which implements the
accept method (whose task is to determine what we want to accept when iterating). The other methods are in place to ensure that the filtering happens throughout the recursion. This may seem complicated but is the best way (of those outlined in this post) of moving towards the more complex, customised filtering that you might want to do (e.g. give my only audio/video files greater than 10MB, last modified after this time last week, owned by user bob or in group developers) which would be a nightmare if you had to do it
every time it is needed in a foreach loop. The "inner iterator" referred to will be the RecursiveDirectoryIterator.
Shortcut with the ParentIterator
PHP Code:
$dit = new RecursiveDirectoryIterator(__DIR__);
$pit = new ParentIterator($dit);
$rit = new RecursiveIteratorIterator(
$pit, RecursiveIteratorIterator::SELF_FIRST);
foreach ($rit as $fileinfo) {
echo $fileinfo->getPathname() . PHP_EOL;
}
This quickie works because the ParentIterator only iterates over items which have children -- so it only iterates over directories.
Aside: If you have any feedback or criticism about the SPL docs, please do email me (see my signature) and I'll take on board any thoughts.
P.S. Sorry for any typos.
