Jan
29
2017
PHP Recursive Directory File Listing

Iterators

In recent versions of PHP, there is an iterator that you can use for recursively go through a directory. The name of this iterator is RecursiveDirectoryIterator and below is a simple test use:


  1 <?php
  2
  3     $Contentpath = realpath('/tmp/');
  4     $Directory = new RecursiveDirectoryIterator($Contentpath);
  5     $Iterator  = new RecursiveIteratorIterator($Directory);
  6
  7     foreach($Iterator as $name => $object){
  8         echo "$name\n";
  9     }
 10
 11 ?>

the result is something like this:


# php test.php
/tmp/.
/tmp/..
/tmp/sess_td0p1cuohquk966fkit13fhi36
/tmp/sess_et3360aidupdnnifct0te2kr31
/tmp/sess_44rrgbn1em051u64bm49c6pmd2
/tmp/sess_42f9e0mhps120a72kco9nsbn81
/tmp/fresh.log
/tmp/.ICE-unix/.
/tmp/.ICE-unix/..

Filter

One of the benefits of this iterator, is that you can extend the RecursiveFilterIterator class to filter out unwanted values. Here is an example of the extend:


<?php
    $Contentpath = realpath('./');
    $Directory = new RecursiveDirectoryIterator($Contentpath);

    class MyRecursiveFilterIterator extends RecursiveFilterIterator {
        public function accept() {
            return $this->current()->getFilename();
        }
    }   

    $MyFilter  = new MyRecursiveFilterIterator($Directory);
    $Iterator  = new RecursiveIteratorIterator($MyFilter);

    foreach($Iterator as $name => $object){
        echo "$name\n";
    }

?>

at the above example, we did not exclude or filter anything.
But our RecursiveIteratorIterator is now passing through our MyRecursiveFilterIterator !

TXT

Let’s filter out everything, but text files.


  1 <?php
  2     $Contentpath = realpath('./');
  3     $Directory = new RecursiveDirectoryIterator($Contentpath);
  4
  5     class MyRecursiveFilterIterator extends RecursiveFilterIterator {
  6         public function accept() {
  7             $file_parts = pathinfo($this->current()->getFilename());
  8
  9             if ( $file_parts['extension'] == 'txt' ) {
 10                 return $this->current()->getFilename();
 11             }
 12
 13         }
 14     }
 15
 16     $MyFilter = new MyRecursiveFilterIterator($Directory);
 17     $Iterator = new RecursiveIteratorIterator($MyFilter);
 18
 19     foreach($Iterator as $name => $object){
 20         echo "$name\n";
 21     }
 22 ?>

There is a little caveat on the above example !

Seems that the above piece of code is working just fine for a specific directory, but when you are running it against a recursive directory, you are going to have errors like the below one:


PHP Notice:  Undefined index: extension

and that’s why pathinfo will also run against directories !!!

Directories

So, we need to exclude - filter out all the directories:


  1 <?php
  2     $Contentpath = realpath('./');
  3     $Directory = new RecursiveDirectoryIterator($Contentpath);
  4
  5     class MyRecursiveFilterIterator extends RecursiveFilterIterator {
  6         public function accept() {
  7
  8             if ( $this->current()->isDir() )
  9                 return true;
 10
 11              $file_parts = pathinfo($this->current()->getFilename());
 12
 13             if ( $file_parts['extension'] == 'txt' ) {
 14                 return $this->current()->getFilename();
 15             }
 16
 17         }
 18     }
 19
 20     $MyFilter = new MyRecursiveFilterIterator($Directory);
 21     $Iterator = new RecursiveIteratorIterator($MyFilter);
 22
 23     foreach($Iterator as $name => $object){
 24         echo "$name\n";
 25     }
 26 ?>

pretty close.

Dots

Pretty close indeed, but we are not excluding the DOT directories:


.
..

FilesystemIterator

From the FilesystemIterator class we learn that there is a flag that does that:

const integer SKIP_DOTS = 4096 ;

and you can use it on RecursiveDirectoryIterator as the recursive directory iterator is actually an extend of FilesystemIterator

 RecursiveDirectoryIterator extends FilesystemIterator implements SeekableIterator , RecursiveIterator 

so our code is transforming to this one:


  1 <?php
  2     $Contentpath = realpath('./');
  3     $Directory = new RecursiveDirectoryIterator($Contentpath,RecursiveDirectoryIterator::SKIP_DOTS);
  4
  5     class MyRecursiveFilterIterator extends RecursiveFilterIterator {
  6         public function accept() {
  7
  8             if ( $this->current()->isDir() )
  9                 return true;
 10
 11             $file_parts = pathinfo($this->current()->getFilename());
 12
 13             if ( $file_parts['extension'] == 'txt' ) {
 14                 return $this->current()->getFilename();
 15             }
 16
 17         }
 18     }
 19
 20     $MyFilter = new MyRecursiveFilterIterator($Directory);
 21     $Iterator = new RecursiveIteratorIterator($MyFilter);
 22
 23     foreach($Iterator as $name => $object){
 24         echo "$name\n";
 25     }
 26 ?>

That’s It !