E Wierda
E Wierda

Reputation: 1315

How to do only selected Folders with a PHP Iterator?

I am trying to use the PHP recursive directory iterator to find php and html files in order to list those by date. The code used is as follows:

        $filelist = array();
    $iterator = new RecursiveDirectoryIterator( $this->_jrootpath );
    foreach(new RecursiveIteratorIterator($iterator) as $file) {
        if ( !$file->isDir() ) {
            if ( preg_match( "/.(html|htm|php)$/", $file->getFilename() ) ) {
                $filelist[$file->getPathname()] = $file->getMTime();
            }
        }
    }
    arsort($filelist);
    foreach ($filelist as $key => $val) {
        $resultoutput .= '          <tr>
                                        <td class="filepath">'.$key.'</td>
                                        <td class="filedate">'.date("d-m-Y H:i:s",$val).'</td>
                                    </tr>';
    }

The above works, but I need to restrict the iteration to certain folders. This script will start in a parent folder where I expect some particular subfolders like 'administrator', 'components', 'modules', 'plugins' and 'templates' (in other words the standard folders of a Joomla installation). I also expect a few files in the parent folder, like 'index.php'. These files and folders should be iterated (frankly, writing this I am not sure if the parent folder itself is being iterated by my code!). However, it is possible that the parent folder also contains other subfolders which should not be iterated. These could be subfolders for subdomains or addon domains, named for example 'mysub' and 'myaddon.com'. There are two reasons why I want to skip any folder except for the normal Joomla installation folders. One is that I don't need to know about files in other folders. Moreover, iterating all folders can take so much time and resources that I get 500 error pages.

My question is how I would best change my code in order to make sure that the right folders are excluded from and included in the iteration? I have seen iterator examples around the web checking the name of a directory, but that does not help me enough, because I only need to check the second level folder names (meaning only the highest level subfolder in a path like parent/secondlevel/noneedtocheck/file.php).

Upvotes: 1

Views: 1615

Answers (5)

Lars Strojny
Lars Strojny

Reputation: 665

Usually you use an RecursiveIteratorIteratorto resolve the "recursiveness" in RecursiveDirectoryIterator. A RecursiveIteratorIterator is just a simple Iterator that can be used like any other you might be already familiar with. This includes using FilterIterator with it. In PHP 5.4 it looks like that (CallbackIteratoris new)

<?php
$directories = new CallbackIterator(
    new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator('path/to/directory')
    ),
    function($current) {
        return $current->isDir();
    }
);

foreach ($directories as $directory) {
    ...
}

For PHP <5.4 you need to implement CallbackIteratoryourself

<?php
class CallbackIterator extends FilterIterator
{
    private $callback;

    public function __construct(Traversable $iterator, $callback)
    {
        $this->callback = $callback;
    }

    public function accept()
    {
        return call_user_func($this->callback, $this->current());
    }
}

Upvotes: 0

CpILL
CpILL

Reputation: 7009

no no! YOu want something like:

<?php
class FilteredRecursiveDirectoryIterator extends FilterIterator
{   
    public function __construct($path)
    {
        parent::__construct(new RecursiveDirectoryIterator($path));
    }
    public function accept()
    {
        if($this->current()->isFile() and in_array($this->getExtention(), array('html', 'php')))
            return true;

        return false;
    }

    private function getExtention()
    {
        return end(explode('.', $this->current()->getFilename()));
    }
}

where the accept() method does all the filtering for you. I haven't tested this but I'm sure it good to go ;)

Upvotes: 1

Franz
Franz

Reputation: 11553

You could try using the RecursiveRegexIterator.

EDIT This is your example using the RecursiveRegexIterator:

$filelist = array();
$iterator = new RecursiveDirectoryIterator( $this->_jrootpath );
foreach(new RecursiveRegexIterator($iterator, '/\.(html|htm|php)$/') as $file) {
    if ( !$file->isDir() ) {
        $filelist[$file->getPathname()] = $file->getMTime();
    }
}
arsort($filelist);
foreach ($filelist as $key => $val) {
    $resultoutput .= '                      <tr>
                                                                            <td class="filepath">'.$key.'</td>
                                                                            <td class="filedate">'.date("d-m-Y H:i:s",$val).'</td>
                                                                    </tr>';
}

By the way, it looks like $resultoutput is not created yet. In that case, you should create it as an empty string somewhere...

EDIT2: Oops, this does not really answer your question. For some reason I thought it was only about the recursive stuff. This is just a more elegant solution to loop over the files, but it doesn't solve your problem, sorry. You should probably combine this with one of the other answers.

Upvotes: 0

E Wierda
E Wierda

Reputation: 1315

I am going to try the below: by putting the iterator in a function I can use it repeatedly for known folders to be included in order to build the result array. Still I hope one day I will find a solution that will more elegantly use the PHP iterators without a function nested in my method.

    $filelist = array();
    $filelist[$this->_jrootpath.DS.'index.php'] = date("d-m-Y H:i:s",filemtime($this->_jrootpath.DS.'index.php'));
    $filelist[$this->_jrootpath.DS.'index2.php'] = date("d-m-Y H:i:s",filemtime($this->_jrootpath.DS.'index2.php'));
    $joomlafolders = array( $this->_jrootpath.DS.'administrator', $this->_jrootpath.DS.'cache', $this->_jrootpath.DS.'components', $this->_jrootpath.DS.'images', $this->_jrootpath.DS.'includes', $this->_jrootpath.DS.'language', $this->_jrootpath.DS.'libraries', $this->_jrootpath.DS.'logs', $this->_jrootpath.DS.'media', $this->_jrootpath.DS.'modules', $this->_jrootpath.DS.'plugins', $this->_jrootpath.DS.'templates', $this->_jrootpath.DS.'tmp', $this->_jrootpath.DS.'xmlrpc' );
    foreach ( $joomlafolders as $folder ) {
        $iterator = new RecursiveDirectoryIterator( $folder );
        foreach(new RecursiveIteratorIterator($iterator) as $file) {
            if ( !$file->isDir() ) {
                if ( preg_match( "/.(html|htm|php)$/", $file->getFilename() ) ) {
                    $filelist[$file->getPathname()] = $file->getMTime();
                }
            }
        }
    }
    arsort($filelist);
    foreach ($filelist as $key => $val) {
        $resultoutput .= '          <tr>
                                        <td class="filepath">'.$key.'</td>
                                        <td class="filedate">'.date("d-m-Y H:i:s",$val).'</td>
                                    </tr>';
    }

Upvotes: 0

streetparade
streetparade

Reputation: 32918

    $filelist = array();

// make an array of excludedirs $excludeDirs= array("/var/www/joomla/admin/");

    $iterator = new RecursiveDirectoryIterator( $this->_jrootpath );
    foreach(new RecursiveIteratorIterator($iterator,RecursiveIteratorIterator::CHILD_FIRST) as $file) {
    if($file->getPathname() == excludeDirs[0])
       continue;
    if ( !$file->isDir() ) {
                    if ( preg_match( "/.(html|htm|php)$/", $file->getFilename() ) ) {
                            $filelist[$file->getPathname()] = $file->getMTime();
                    }
            }
    }
    arsort($filelist);
    foreach ($filelist as $key => $val) {
            $resultoutput .= '<tr>
            <td class="filepath">'.$key.'</td>
           <td class="filedate">'.date("d-m-Y H:i:s",$val).'</td>
           </tr>';
    }

Upvotes: 0

Related Questions