Невозможно пропустить нечитаемые каталоги с помощью RecursiveDirectoryIterator

Я хочу получить список всех подкаталогов, и мой приведенный ниже код работает, за исключением случаев, когда у меня есть разрешения на чтение только для определенных папок.

В приведенном ниже вопросе показано, как пропустить каталог с помощью RecursiveDirectoryIterator. Можно ли заставить RecursiveDirectoryIterator пропускать нечитаемые каталоги? однако мой код здесь немного отличается, и я не могу обойти эту проблему.

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

Я получаю ошибку

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

Я попытался заменить его принятым ответом в другом вопросе.

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

Однако этот код не даст мне список всех каталогов внутри www, как я хочу, где я здесь не так?

8 ответов

Решение

Вступление

Основная проблема с вашим кодом заключается в использовании CHILD_FIRST

ИЗ PHP DOC

Дополнительный режим. Возможные значения

  • RecursiveIteratorIterator::LEAVES_ONLY - по умолчанию. Списки только уходит в итерацию.
  • RecursiveIteratorIterator:: SELF_FIRST - Перечисляет листья и родителей в итерации, причем родители идут первыми.
  • RecursiveIteratorIterator:: CHILD_FIRST - Перечисляет листья и родителей в итерации, причем листья идут первыми.

То, что вы должны использовать, это SELF_FIRST так что текущий каталог включен. Вы также забыли добавить дополнительные параметры RecursiveIteratorIterator::CATCH_GET_CHILD

ИЗ PHP DOC

Необязательный флаг. Возможные значения: RecursiveIteratorIterator::CATCH_GET_CHILD, который будет игнорировать исключения, возникающие при вызовах RecursiveIteratorIterator::getChildren().

Ваш код пересмотрен

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>';
    }
}

Вы действительно хотитеCHILD_FIRST

Если вы действительно хотите сохранить CHILD_FIRST структура, то я предлагаю вам использовать ReadableDirectoryIterator

пример

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

Используемый класс

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

}

Этот код работает на 100%...

Вы можете просто использовать эту функцию для сканирования файлов и папок в нужном вам каталоге или диске. Вам просто нужно передать путь к нужному каталогу в функцию. Вторым параметром функции является отображение полного пути отсканированных файлов и папок. Ложное значение второго параметра означает не показывать полный путь.

Массив $ignore используется для исключения любого желаемого имени файла или имени папки из списка.

Функция возвращает массив, содержащий список файлов и папок.

Эта функция пропускает файлы и папки, которые невозможно прочитать во время рекурсии.

Я установил следующую структуру каталогов:

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

Я запустил следующий код (я добавил SKIP_DOTS флаг пропустить . а также .. кстати):

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

print_r(iterator_to_array($i));

Это выводит следующее:

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
        )

)

Это работает как ожидалось.

Обновить

Добавлены флаги, которые были у вас в исходном примере (хотя я все равно считаю, что они по умолчанию):

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>';
        }
}

Выход:

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

Как насчет того, чтобы попытаться поймать исключение UnexpectedValueException. Возможно, есть даже уникальный код исключения для этой ошибки, который вы можете проверить. В противном случае вы можете разбор сообщения об исключении "отказано в разрешении".

Я бы предложил изучить http://php.net/manual/de/class.unexpectedvalueexception.php

Если вы получаете необработанные исключения, почему бы вам не поместить этот код в блок try с блоком catch для исключения ошибок, когда он не может прочитать каталоги? Просто простое предложение, глядя на ваш код и вашу проблему. Вероятно, есть более удобный способ сделать это в PHP.

Вам нужно использовать SELF_FIRST константа, если вы хотите вернуть нечитаемое имя каталога. Когда вы делаете CHILD_FIRST, он пытается попасть в каталог, не удается, и текущее имя каталога не включено.

$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";
    }
}

Функция glob автоматически пропускает ошибки чтения и должна немного упростить ваш код.

Другие вопросы по тегам