About the Author

Chris Shiflett

Chris Shiflett is an author and speaker who leads the web application security practice at OmniTI.


PHP Advent Calendar Day 7

Today's entry, provided by Elizabeth Smith, is entitled SPL to the Rescue.

Elizabeth Smith

Name
Elizabeth Smith
Blog
elizabethmariesmith.com
Biography
Elizabeth Smith is a PHP Windows geek, lover of all things PECL, PHPWomen.org charter member, PHP-GTK 2 developer, and generally involved in doing bad things with PHP for fun and profit.
Location
Sturgis, Michigan

I like doing command line scripting with PHP; it's fun and simple. Often, I need to manipulate batches of files by recursively iterating over files in a directory and all subdirectories. Of course, I want to do it quickly and efficiently, so I can get my Christmas shopping done. Long ago, there was opendir(), and then came scandir(), but to make these functions recursive involved convoluted looping that makes my head hurt. I try to avoid recursive functions, probably because I also tend to forget something and end up with infinite loops. PHP 5 with SPL has really nifty tools to make life easier, I promise! Below is my solution for recursively iterating over files in any directory. I'll give it the exciting name of RecursiveFileIterator.

<?php
 
class RecursiveFileIterator extends RecursiveIteratorIterator
{
    /**
     * Takes a path to a directory, checks it, and then recurses into it.
     * @param $path directory to iterate
     */
    public function __construct($path)
    {
        // Use realpath() and make sure it exists; this is probably overkill, but I'm anal.
        $path = realpath($path);
 
        if (!file_exists($path)) {
            throw new Exception("Path $path could not be found.");
        } elseif (!is_dir($path)) {
            throw new Exception("Path $path is not a directory.");
        }
 
        // Use RecursiveDirectoryIterator() to drill down into subdirectories.
        parent::__construct(new RecursiveDirectoryIterator($path));
    }
}
 
// This is how you use it.
foreach (new RecursiveFileIterator('/path/to/something') as $item) {
    // Because $item is actually an SPLFileInfo object, echo gives you the absolute path from __toString() magic.
    echo $item . PHP_EOL;
}
 
?>

See what little code that takes? I added lots of comments (because I'm comment crazy) and checks (because I'm like that when I code), but can you imagine if I had used recursive functions and scandir()?

I'm being extremely boring and simply echoing the absolute path to the file. You aren't limited to that; you actually get an SPLFileInfo instance when you use RecursiveDirectoryIterator(), and it gives you all sorts of goodies such as timestamps, permissions, owner, and more.

When performing voodoo like this, I usually want to not only recursively iterate over files, but also pick which files to look at as well. You can do this with the magic of glob(), but there are some drawbacks. Firstly, glob() is not consistent cross-platform, and secondly, glob() can be really slow, especially on Windows. I prefer to use the magic of SPL's FilterIterator combined with the code above. Your accept() method can be anything you like; below is my generic version for file extension checking. I'll give it the exciting name of RecursiveFileFilterIterator. Just tell it what file extensions to allow and away you go.

<?php
 
class RecursiveFileFilterIterator extends FilterIterator
{
    /**
     * acceptable extensions - array of strings
     */
    protected $ext = array();
 
    /**
     * Takes a path and shoves it into our earlier class.
     * Turns $ext into an array.
     * @param $path directory to iterate
     * @param $ext comma delimited list of acceptable extensions
     */
    public function __construct($path, $ext = 'php')
    {
        $this->ext = explode(',', $ext);
        parent::__construct(new RecursiveFileIterator($path));
    }
 
    /**
     * Checks extension names for files only.
     */
    public function accept()
    {
        $item = $this->getInnerIterator();
 
        // If it's not a file, accept it.
        if (!$item->isFile()) {
            return TRUE;
        }
 
        // If it is a file, grab the file extension and see if it's in the array.
        return in_array(pathinfo($item->getFilename(), PATHINFO_EXTENSION), $this->ext);
    }
}
 
// Same usage as above, but you can indicate allowed extensions with the optional second argument.
foreach (new RecursiveFileFilterIterator('/path/to/something', 'php,txt') as $item) {
    // This is an SPLFileInfo object.
    echo $item . PHP_EOL;
}
 
?>

Notice again the small amount of code. The next time you want to do something to a bunch of PHP files, let SPL come to your rescue.

About This Post

PHP Advent Calendar Day 7 was posted on Fri, 07 Dec 2007 at 16:09:23 GMT.

3 Comments

1. Davey Shafik's GravatarDavey Shafik said:

We need a built in RecursiveDirectoryIteratorIterator - this is a common enough use-case to be included in SPL-proper IMO :)

- Davey

Fri, 07 Dec 2007 at 18:48:55 GMT Link


2. Adam's GravatarAdam said:

It does look very in-depth. Some impressive PHP right there!

Adam @ http://www.talkphp.com/

Sat, 08 Dec 2007 at 00:43:41 GMT Link


3. Jakob's GravatarJakob said:

I don't think the RecursiveFileIterator class is really needed. If you need this kind of functionality you can just iterate over an recursiveIteratorIterator which iterates a RecursiveDirectoryIterator. This is exactly what the created class does, but it could be done right inside the foreach loop.

foreach( new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator(
                    "Your directory goes here"
                )
          ) as $item ) {
    echo $item, PHP_EOL;
}

At first it looks kind of strange but you will get used to it quite fast.

I really like this little article of yours, because it shows other php developers what the spl can do and how it's features make the life of a developer a lot easier.

- Jakob

Sat, 08 Dec 2007 at 17:00:46 GMT Link


Post A Comment

Personal Details and Comment

Style Guide

Line breaks are converted to paragraphs. Also use:

  • <a href="" title="">text</a>1
  • <em>text</em>
  • <blockquote><p>text</p></blockquote>
  • <code>2  <?php  if ($foo) {      $foo = TRUE;  }  ?></code>
  1. Note: <code> can be used inline (e.g. in paragraphs) or in a block as shown. Include whitespace and newlines in blocks.

Please enter Chris (my first name) below. This is a primitive spam prevention technique, and I apologize for the inconvenience.

Preview and Submit

Upcoming Talks

PHP Appalachia

11 - 14 Oct 2008

At Big Bear Lodge, Gatlinburg, Tennessee.

php|works / PyWorks

12 - 14 Nov 2008

At Sheraton Gateway Hotel Atlanta Airport, Atlanta, Georgia.

New Comments

Chris Shiflett wrote:

Miguel, read the post again. PHP 4.4.9 is the final release of PHP 4.

Posted in End of Life for PHP 4
Miguel Palazzo wrote:

I think you're wrong. PHP 4.4 is DEAD, that's so right, because they just released 4.4.9, and you...

Posted in End of Life for PHP 4
alikim wrote:

Hi, Thanks for the article! Tell me please if it's enough to use just session_start(); se...

Posted in
Wayne wrote:

Hi ZX, When taking in data, you should always check to see if magic_quotes is enabled. If it i...

Posted in addslashes() Versus mysql_real_escape_string()
Chris Shiflett wrote:

Thanks, Brandon. I'm glad you liked the talk. Maybe some parts of it would be interesting to some...

Posted in ZendCon

Browse Comments