Posted by sdboyer on December 13, 2008 at 4:19am
Per the great drewish's request... :) Some quick sample pseudo-code for how an SPL-based replacement for file_scan_directory() could work.
Note that this isn't tested, but is probably pretty close.
<?php
/<strong>
* This pseudo-code neglects the following parameters:
* - $key: this can be handled a few different ways, but isn't super-important right now.
* - $callback: probably the most transparent way to handle this would be in an extension
* to the iterator we use.
* - $nomask: I can't remember off the top of my head if there's easy ways to do
* negative filters like this. I've just included the CVS one directly at the moment.
* - $min_depth: I'm not 100% on this off the top of my head, but I think this'd
* best be done in whatever RecursiveIteratorIterator we ultimately wrap the scan object with.
*
*/
function file_scan_directory($dir, $mask, $nomask = '/(..?|CVS)$/', $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth =0, $depth = 0) {
$scan = $recurse ? new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) : new DirectoryIterator($dir);
$scan = new CVSFilter(new DotFilter($scan));
$scan = new RegexIterator($scan, $mask);
return $scan;
}
class ExtensionFilter extends FilterIterator {
/</strong>
* The extension we are checking for.
* @var string
*/
protected $ext;
public function __construct(Iterator $item, $ext) {
$this->ext = $ext;
parent::__construct($item);
}
public function accept() {
return $this->ext == pathinfo($this->current(), PATHINFO_EXTENSION);
}
}
class DotFilter extends FilterIterator {
public function accept() {
return !$this->current()->isDot();
}
}
class CVSFilter extends FilterIterator {
public function accept() {
return !($this->current()->isDir() && $this->current()->getFilename() == 'CVS');
}
}
?>It's also kinda inefficiently written as-is...just a proof of concept, really.

Comments
Example of a caller using
Example of a caller using this. Since I forgot to put a way of using the extension filter in directly, lemme be a bit lazy and just give another, slightly different version of file_scan_directory():
<?php
function file_scan_directory($dir, $mask, $extension, $recurse = TRUE) {
$scan = $recurse ? new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) : new DirectoryIterator($dir);
$scan = new CVSFilter(new DotFilter($scan));
$scan = new ExtensionFilter($scan, $extension);
$scan = new RegexIterator($scan, $mask);
return $scan;
?>
OK, imagining that function:
<?php$dir = file_scan_directory(DRUPAL_ROOT . '/modules', '@/?user.*(?!/)@', 'inc');
foreach ($dir as $item) {
echo $item . "\n"; // will output the path to the current item, including the filename. I can't recall off the top of my head if it's the realpath or not, but that can be worked with.
echo $item->getBasename(); . "\n"; // pops out what used to be in $file->basename
echo substr($item->getBasename(), 0, strpos($item->getBasename(), pathinfo($item, PATHINFO_EXTENSION)) - 1) . "\n"; // just the plain filename. Long and horrendous here, but can be made cleaner and put in a method, too
}
?>
Don't mind the fugly regex; it's just so that this code ACTUALLY works using an unmodified RecursiveDirectoryIterator, because the RegexIterator runs on the string generated by RecursiveDirectoryIterator::__toString(), which by default gives the full pathname. With a DirectoryIterator, plain ol'
'/^user.*/'would be sufficient, and we can optionally override the behavior of our RecursiveDirectoryIterator to work the same way. Or do it some other way. There are so many options :)Aaanyway. That will perform those three echo operations on all the files that begin with 'user' and have the extension 'inc' which are anywhere under the system 'modules' directory. In other words, on the current HEAD, it'd look like this:
<DRUPAL_ROOT>/modules/user/user.admin.incuser.admin.inc
user.admin
<DRUPAL_ROOT>/modules/user/user.pages.inc
user.pages.inc
user.pages
Welcome to the wonderful world of OOP!
Am I allowed to use this as the example of everything that I hate in OOP?
But of course :)
But of course :)