From: Ryan Panning on
Philip Graham wrote:
> Here's a RecursiveDirectoryIterator class I've written and find quite useful:
>
> <?php
> /**
> * This class encapsulates an iterator that iterates over all the files in a
> * directory recursively. Only files that don't begin with a '.' are included
> * in this iteration. This class essentially wraps an SPL DirectoryIterator
> * object and returns each file in the iteration as an SplFileInfo.
> *
> * @author <a href="mailto:philip(a)lightbox.org">Philip Graham</a>
> */
> class Util_RecursiveFileIterator implements Iterator {
> CONST SHOW_DOT_FILES = true;
>
> private $_basePath;
> private $_curDirIter;
> private $_dirStack;
> private $_showDots;
>
> /**
> * Constructor. By default the iteration will skip any files that begin
> * with a '.' character but this can be changed by passing the class
> * constant SHOW_DOT_FILES as the second parameter.
> *
> * @param string $basePath - The base path of the directory to iterate
> over
> * @param boolean $showDots - Whether or not to include files that begin
> * with a '.' character in the iteration.
> */
> public function __construct($basePath, $showDots = false) {
> // PREPARE ... THE ... HUMANOID
> if(!is_dir($basePath)) {
> $basePath = dirname($basePath);
> }
> if(!$basePath) {
> $basePath = '.';
> }
> // If the given path is relative this function will transform it
> // into an equivalent path. This call assumes that the relative path
> // is relative to the file that created this iterator.
> $this->_basePath = Util_File::getAbsolutePath($basePath, 2);
>
> $this->_showDots = $showDots;
>
> }
>
> /**
> * Returns the current value of the iteration
> *
> * @return FileInfoInfo
> */
> public function current() {
> return $this->_curDirIter->current();
> }
>
> /**
> * Returns the index of the current entry.
> *
> * @return string
> */
> public function key() {
> $curPath = $this->_curDirIter->getPathname();
> $relativeToBase = substr($curPath, strlen($this->_basePath));
> return $relativeToBase;
> }
>
> /**
> * Moves the directory iterator to the next element of the iteration.
> */
> public function next() {
> $this->_curDirIter->next();
> $good = false;
> while(!$good) {
> if(!$this->_curDirIter->valid()) {
> if(count($this->_dirStack) == 0) {
> $good = true;
> } else {
> $this->_curDirIter = array_pop($this->_dirStack);
> }
> } else if($this->_curDirIter->isDot()) {
> if(!$this->_showDots) {
> $this->_curDirIter->next();
> } else {
> $good = true;
> }
> } else if(!$this->_showDots &&
> substr($this->_curDirIter->getFileName(), 0, 1) == '.') {
> $this->_curDirIter->next();
> } else if($this->_curDirIter->isDir()) {
> // Create a new iterator for the sub-dir
> $newIter = new DirectoryIterator(
> $this->_curDirIter->getPathname());
>
> // Make sure the current iterator is ready to go when it
> // gets popped off the stack
> $this->_curDirIter->next();
>
> // Push it... push it real good
> array_push($this->_dirStack, $this->_curDirIter);
>
> // Set the new iterator
> $this->_curDirIter = $newIter;
> } else {
> $good = true;
> }
> }
> }
>
> /**
> * Resets the iterator to first file in the object's base path.
> */
> public function rewind() {
> $this->_curDirIter = new DirectoryIterator($this->_basePath);
> $this->_dirStack = array();
> if(!$this->_showDots && $this->_curDirIter->isDot()) {
> $this->next();
> }
> }
>
> /**
> * Returns a boolean indicating wether or not there are anymore elements
> left
> * in the iteration.
> */
> public function valid() {
> return $this->_curDirIter->valid();
> }
> }
>
> Usage:
>
> $dirIter = new Util_RecursiveFileIterator('/path/to/directory');
> foreach($dirIter AS $fileName => $fileInfo) {
> // $fileName is the basename of the file and $fileInfo is a SplFileInfo
> // for the file
> }
>
> Hope this helps!

Hi, although I don't think this will work in my situation, there is an
interesting piece of code that you have.

array_push($this->_dirStack, $this->_curDirIter);

Are you able to "inject" the iterator with more items? I would like to
do this with the RecursiveDirectoryIterator where-by I "inject" fake
files into the iterator. Is this what you're doing here?
From: Philip Graham on
On February 23, 2009 12:32:26 Ryan Panning wrote:
> Hi, although I don't think this will work in my situation, there is an
> interesting piece of code that you have.
>
> array_push($this->_dirStack, $this->_curDirIter);
>
> Are you able to "inject" the iterator with more items? I would like to
> do this with the RecursiveDirectoryIterator where-by I "inject" fake
> files into the iterator. Is this what you're doing here?

Yes, in essence that is what this is doing but I am not manipulating the
DirectoryIterator objects. I am instead using my own structure and logic to
do this.

Basically I'm using normal (i.e flat) DirectoryIterator objects but when I
encounter a sub directory, I instantiate another DirectoryIterator for the
sub-directory to continue the iteration. In order to remember where I was
once I've finished iterating over the sub-directory the current iterator is
pushed onto a stack. Then once the iteration of the sub-directory is finished
the iterator for the parent directory is popped off the stack and the
iteration over that directory continues from where it was interrupted.

Does this explanation satisfy your question?


--
Philip Graham
Lightbox Technologies
Suite 312 240 Catherine St.
Ottawa, ON, K2P 2G8
613-686-1661 ext. 102
From: Nathan Nobbe on
On Mon, Feb 23, 2009 at 10:27 AM, Ryan Panning <rpanning(a)gmail.com> wrote:

> Nathan Nobbe wrote:
>
>> if youre trying to do recursive iteration whereby you 'flatten' the tree
>> structure, drop the RecursiveDirectoryIterator into a
>> RecursiveIteratorIterator (its for iterating over RecursiveIterators),
>> then
>> you dont have to bother w/ calling hasChildren() at all. you probly also
>> want to look at the constructor args, since by default, it only returns
>> leaf
>> nodes.
>>
>> $dir = new RecursiveDirectoryIterator('/path/to/dir');
>>
>> $itt = new RecursiveIteratorIterator($dir,
>> RecursiveIteratorIterator::CHILD_FIRST);
>>
>> foreach ($itt as $item) {
>>> print get_class($item) . "\r\n";
>>> }
>>>
>>
>>
>> -nathan
>
>
im not sure i fully understand the problem, but from the sound of it, as a
work-around, why not something like this,

$dirIt = new RecursiveDirectoryIterator($path);
foreach($dirIt as $item) {
if($item->isDir()) {
$subdir = new RecursiveDirectoryIterator($item);
// now you have the RecursiveDirectoryIterator you wanted ??
}
}

-nathan
From: Nathan Rixham on
Ryan Panning wrote:
> I have discovered that when I foreach over a RecursiveDirectoryIterator
> (see example below) the $item actually turns into a SplFileInfo object.
> I would expect it to be a RecursiveDirectoryIterator. How do I do a
> hasChildren() on SplFileInfo?

seems like expected functionality to me, you're looping over the
contents of the directory and that can only be a file or a directory -
thus SplFileInfo seems correct?

in short you don't call hasChildren() on SplFileInfo, you call it on
RecursiveDirectoryIterator.

From the docs:
RecursiveDirectoryIterator::hasChildren � Returns whether current entry
is a directory and not '.' or '..'

RecursiveDirectoryIterator::getChildren � Returns an iterator for the
current entry if it is a directory

Thus this is how you call getChildren properly:

$dir = new RecursiveDirectoryIterator( dirname(dirname(__FILE__)) );
foreach($dir as $splFileInfo) {
if( $dir->hasChildren() ) {
$childDir = $dir->getChildren();
echo get_class($childDir) . ' ' . $childDir->getPath() . PHP_EOL;
}
}

many regards,

Nathan