View Single Post
Old 06-03-2010, 08:15 PM   #6 (permalink)
Salathe
Moderateur
RegEx Guru PHP Guru Top Contributor Advanced Programmer 
 
Salathe's Avatar
 
Join Date: Apr 2007
Posts: 1,393
Thanks: 5
Salathe is on a distinguished road
Default

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(
    
$ditRecursiveIteratorIterator::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(
    
$ditRecursiveIteratorIterator::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(
    
$dirsRecursiveIteratorIterator::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(
    
$pitRecursiveIteratorIterator::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.
Salathe is offline  
Reply With Quote