Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unable to skip unreadable directories with RecursiveDirectoryIterator

I want to get a list of all the subdirectories and my below code works except when I have readonly permissions on certain folders.

In the below question it shows how to skip a directory with RecursiveDirectoryIterator Can I make RecursiveDirectoryIterator skip unreadable directories? however my code is slightly different here and I am not able to get around the problem.

$path = 'www/';
foreach (new RecursiveIteratorIterator(
       new RecursiveDirectoryIterator($path,RecursiveDirectoryIterator::KEY_AS_PATHNAME),
            RecursiveIteratorIterator::CHILD_FIRST) as $file => $info) 
            {
                if ($info->isDir())
                {
                       echo $file . '<br>';                     
                }
            }   

I get the error

Uncaught exception 'UnexpectedValueException' with message 'RecursiveDirectoryIterator::__construct(../../www/special): failed to open dir: Permission denied'

I have tried replacing it with the accepted answer in the other question.

new RecursiveIteratorIterator(
new RecursiveDirectoryIterator("."), 
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CATCH_GET_CHILD);

However this code will not give me a list of all the directories inside of www like I want, where am I going wrong here?

like image 880
ak85 Avatar asked Jan 19 '13 03:01

ak85


2 Answers

Introduction

The main issue with your code is using CHILD_FIRST

FROM PHP DOC

Optional mode. Possible values are

  • RecursiveIteratorIterator::LEAVES_ONLY - The default. Lists only leaves in iteration.
  • RecursiveIteratorIterator::SELF_FIRST - Lists leaves and parents in iteration with parents coming first.
  • RecursiveIteratorIterator::CHILD_FIRST - Lists leaves and parents in iteration with leaves coming first.

What you should use is SELF_FIRST so that the current directory is included. You also forgot to add optional parameters RecursiveIteratorIterator::CATCH_GET_CHILD

FROM PHP DOC

Optional flag. Possible values are RecursiveIteratorIterator::CATCH_GET_CHILD which will then ignore exceptions thrown in calls to RecursiveIteratorIterator::getChildren().

Your CODE Revisited

foreach (new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($path,RecursiveDirectoryIterator::KEY_AS_PATHNAME),
        RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD) as $file => $info)
{
    if ($info->isDir())
    {
        echo $file . '<br>';
    }
}

You really want CHILD_FIRST

If you really want to maintain the CHILD_FIRST structure then i suggest you use ReadableDirectoryIterator

Example

foreach ( new RecursiveIteratorIterator(
        new ReadableDirectoryIterator($path),RecursiveIteratorIterator::CHILD_FIRST) as $file ) {
    echo $file . '<br>';
}

Class Used

class ReadableDirectoryIterator extends RecursiveFilterIterator {
    function __construct($path) {
        if (!$path instanceof RecursiveDirectoryIterator) {
            if (! is_readable($path) || ! is_dir($path))
                throw new InvalidArgumentException("$path is not a valid directory or not readable");
            $path = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
        }
        parent::__construct($path);
    }

    public function accept() {
        return $this->current()->isReadable() && $this->current()->isDir();
    }
}
like image 54
Baba Avatar answered Oct 23 '22 19:10

Baba


function dirScan($dir, $fullpath = false){

    $ignore = array(".","..");
    if (isset($dir) && is_readable($dir)){
        $dlist = array();
        $dir = realpath($dir);
        $objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir,RecursiveDirectoryIterator::KEY_AS_PATHNAME),RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);

        foreach($objects as $entry){    
                    if(!in_array(basename($entry), $ignore)){
                        if (!$fullpath){
                            $entry = str_replace($dir, '', $entry);
                        }           
                            $dlist[] = $entry;
                    }                        
        }       
        return $dlist;
    }

}

This code works 100%...

You can simply use this function in order to scan for files and folders in your desired directory or drive. You just need to pass the path of the desired directory into the function. The second parameter of the function is to show full-path of the scanned files and folder. False value of the second parameter means not to show full-path.

The array $ignore is used to exclude any desired filename or foldername from the listing.

The function returns the array containing list of files and folders.

This function skips the files and folders that are unreadable while recursion.

like image 21
yuva Avatar answered Oct 23 '22 19:10

yuva