unable to skip unreadable directories with RecursiveDirectoryIterator

泄露秘密 提交于 2019-12-08 16:00:55

问题


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?


回答1:


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();
    }
}



回答2:


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.




回答3:


I've set up the following directory structure:

/
    test.php <-- the test script
    www/
        test1/ <-- permissions = 000
            file1
        test2/
            file2
        file3

I ran the following code (I've added the SKIP_DOTS flag to skip . and .. btw):

$i = new RecursiveIteratorIterator(
  new RecursiveDirectoryIterator("www", FilesystemIterator::SKIP_DOTS),
  RecursiveIteratorIterator::LEAVES_ONLY,
  RecursiveIteratorIterator::CATCH_GET_CHILD
);

print_r(iterator_to_array($i));

It outputs the following:

Array
(
    [www/test2/file2] => SplFileInfo Object
        (
            [pathName:SplFileInfo:private] => www/test2/file2
            [fileName:SplFileInfo:private] => file2
        )

    [www/file3] => SplFileInfo Object
        (
            [pathName:SplFileInfo:private] => www/file3
            [fileName:SplFileInfo:private] => file3
        )

)

This works as expected.

Update

Added the flags you've had in your original example (although I believe those are default anyway):

foreach (new RecursiveIteratorIterator(
  new RecursiveDirectoryIterator("www", FilesystemIterator::SKIP_DOTS | FilesystemIterator::KEY_AS_PATHNAME),
  RecursiveIteratorIterator::LEAVES_ONLY,
  RecursiveIteratorIterator::CATCH_GET_CHILD | RecursiveIteratorIterator::CHILD_FIRST
) as $file => $info) {
        echo $file, "\n";
        print_r($info);
        if ($info->isDir()) {
            echo $file . '<br>';
        }
}

Output:

www/test2/file2
SplFileInfo Object
(
    [pathName:SplFileInfo:private] => www/test2/file2
    [fileName:SplFileInfo:private] => file2
)
www/file3
SplFileInfo Object
(
    [pathName:SplFileInfo:private] => www/file3
    [fileName:SplFileInfo:private] => file3
)



回答4:


<?php
    $path = "D:/Movies";

    $directory_iterator = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME);

    $files = new RecursiveIteratorIterator($directory_iterator,
            RecursiveIteratorIterator::SELF_FIRST,
            RecursiveIteratorIterator::CATCH_GET_CHILD);

    try {
        foreach( $files as $fullFileName => $file) {
            $path_parts = pathinfo($fullFileName);

            if(is_file($fullFileName)){
                $path_parts = pathinfo($fullFileName);
                $fileName[] =  $path_parts['filename'];
                $extensionName[] =  $path_parts['extension'];
                $dirName[] =  $path_parts['dirname'];
                $baseName[] =  $path_parts['basename'];
                $fullpath[] = $fullFileName;
            }
        }


        foreach ($fullpath as $filles){
                echo $filles;
                echo "</br>";
        }

    }
    catch (UnexpectedValueException $e) {
        printf("Directory [%s] contained a directory we can not recurse into", $directory);
    }
?>



回答5:


The glob function skips read errors automatically and should simplify your code a bit as well.




回答6:


If you are getting unhandled exceptions, why don't you put that code in a try block, with an exception catch block to catch errors when it can't read directories? Just a simple suggestion by looking at your code and your problem. There is probably a neater way to do it in PHP.




回答7:


You need to use SELF_FIRST constant if you want to return the unreadable directory name. When you're doing CHILD_FIRST, it attempt to get into the directory, fails, and the current directory name is not included.

$path = 'testing';

$directory_iterator = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME);

$iterator = new RecursiveIteratorIterator($directory_iterator,
    RecursiveIteratorIterator::SELF_FIRST,
    RecursiveIteratorIterator::CATCH_GET_CHILD);

foreach ($iterator as $file => $info) {
    if ($info->isDir()) {
        echo $file . "\n";
    }
}



回答8:


What about try catch the UnexpectedValueException. Maybe there is even an unique exception code for that error you can check. Otherwise you can evil parse exception message for "permission denied".

I would suggest to examine the http://php.net/manual/de/class.unexpectedvalueexception.php



来源:https://stackoverflow.com/questions/14410899/unable-to-skip-unreadable-directories-with-recursivedirectoryiterator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!