I tried to create a DirectoryIterator class implementing the Eclipse Iterator interface, but the nature of the native directory iteration functions in PHP (both the dir() object and opendir()) didn't lend themselves to it very well.
I've done the same thing, implementing a dir iterator, at first I had a simple DirIterator that was really just a container class for the PHP dir functions with an Eclipse Iterator interface...
Verry soon I came to the conclusion I had a small problem; It just fetched the file/dirnames from a directory, what about filesizes and the likes, clearly some other class had to be constructed to accomodate for this...
In the end I designed 3 classes, that worked together like Eclipse's DataFile, DataFileReader and DataFileIterator, but it is far from ideal, and has one drawback -> it stores all the entries from a directory in an array, which would probably not be the way to go if you intended on iterating over a large directory, then again, since the Eclipse DataFile* classes worked likewise, I thought I'd be ok 
I'll just post the code, leaving the comments out of it, maybe y'all can thr(/b)ash it 
PHP Code:
class Dir
{
var $path;
var $reader;
var $entries;
var $count;
function Dir($path, &$reader)
{
$this->path = (substr($path, -1) == '/') ? $path : $path.'/';
$this->reader =& $reader;
$this->entries = array();
$this->count = -1;
$this->reader->setBasePath($this->getPath());
$this->readDir();
}
function readDir()
{
if (!($dir = @dir( $this->getPath() )))
{
return false;
}
while($line = $dir->read())
{
$entry = $this->reader->parseLine($line);
if ($entry !== false)
{
array_push($this->entries, $entry);
}
}
$dir->close();
$this->count = count($this->entries);
}
function getPath()
{
return $this->path;
}
function getCount()
{
return $this->count;
}
function &getEntry($index)
{
return $this->entries[$index];
}
}
define('SPOOF_DIR_STATS_DEFAULT', 1);
define('SPOOF_DIR_STATS_ALL', 2);
class DirReader
{
var $path;
var $statsKeys;
var $callbacks;
function DirReader()
{
$this->path = '';
$this->callbacks = array(
'size' => "filesize",
'owner' => "fileowner",
'group' => "filegroup",
'perms' => "fileperms",
'last_modified' => "filemtime",
'last_accessed' => "fileatime",
'inode_created' => "fileinode",
'inode_changed' => "filectime"
);
$this->statsKeys = array();
}
function parseLine($line)
{
if ($line == '.' || $line == '..')
{
return false;
}
return $this->filter($this->getStats($line));
}
function filter($stats)
{
return $stats;
}
function setBasePath($path)
{
$this->path = $path;
}
function registerStats($stats)
{
if (is_array($stats))
{
$this->statsKeys = $stats;
}
elseif ($stats == SPOOF_DIR_STATS_ALL)
{
$this->statsKeys = array_keys($this->callbacks);
}
elseif ($stats == SPOOF_DIR_STATS_DEFAULT)
{
$this->statsKeys = array('size');
}
else
{
$this->registerStats(SPOOF_DIR_STATS_DEFAULT);
}
}
function getStats($line)
{
$path = $this->path.$line;
$stats = array('name' => $line);
for ( $i=0, $tot=count($this->statsKeys); $i<$tot; $i++)
{
$this->doCallBack( $this->statsKeys[$i], $path, $stats );
}
return $stats;
}
function doCallBack($key, $parameter, &$stats)
{
if (!isset($this->callbacks[$key]))
{
return ;
}
eval(
sprintf(
"\$stats['$key'] = %s('%s');",
$this->callbacks[$key],
$parameter
)
);
}
}
class DirIterator /* implements Iterator */
{
var $dir;
var $current;
var $max;
function DirIterator( &$dir )
{
$this->dir =& $dir;
$this->max = $this->dir->getCount();
$this->reset();
}
function reset()
{
$this->current = 0;
}
function next()
{
$this->current++;
}
function isValid()
{
return $this->current < $this->max;
}
function &getCurrent()
{
return $this->dir->getEntry($this->current);
}
}
It works something like this:
PHP Code:
$dirPath = '/go/home/to/mama/';
$colNames = array(
'name' => 'Naam',
'size' => 'Grootte',
'last_modified' => 'Laatst veranderd',
'last_accessed' => 'Laatst geraadpleegd'
);
$reader =& new DirReader();
$reader->registerStats( array_keys($colNames) );
$printer =& new DataGridPrinter( new ArrayIterator($colNames) );
$printer->registerNumberFormat('size', 0, '', '.');
$directory =& new Dir($dirPath, $reader);
?>
<html>
<head>
<title> Directory test </title>
<link rel="stylesheet" type="text/css" href="DirectoryDataGrid.css" />
</head>
<body>
Total # files: <?php echo($directory->getCount()); ?>
<?php
Loop::run(new DirIterator($directory), $printer);
?>
</body>
</html>
There are some things I don't like about it:
First there's the issue of reading a whole directory structure into an array, which could open up a whole can of worms in a multi-user environment where a lot of changes are made to that specific directory...
Second, I don't like the fact that the DirReader has a public method setBasePath() (because it has to know the full path in order to call the file stats functions...). It seems to me that the reader should get the full path by concatenating $dir->getPath() and $dir->getEntry(), but that would make the workings of the classes different from that of the DataFile* classes, in that I should use it more like this:
PHP Code:
$directory =& new Dir($path);
$reader =& new DirReader($directory);
which doesn't make sense since I reckon you need a reader to construct the directory...
Tertio, all that fuzz with the callbacks, and still haven't implemented a sorting system 
(I'm thinking going Decorator
)
And last, but not least, there's the QuickForm thread, the interface thread and the phppatterns iterator article (I'm probably forgetting a lot of others), which are shifting my 'firm believes' in the Eclipse Iterators.
I really like Eclipse, let there be no doubt about that, but I'm seriously considering throwing the iterator out of it, and just implement a method next() in all object classes that need a once top-to-bottom iteration...
Bookmarks