Невозможно пропустить нечитаемые каталоги с помощью 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
Дополнительный режим. Возможные значения
- RecursiveIteratorIterator::LEAVES_ONLY - по умолчанию. Списки только уходит в итерацию.
- RecursiveIteratorIterator:: SELF_FIRST - Перечисляет листья и родителей в итерации, причем родители идут первыми.
- RecursiveIteratorIterator:: CHILD_FIRST - Перечисляет листья и родителей в итерации, причем листья идут первыми.
То, что вы должны использовать, это SELF_FIRST
так что текущий каталог включен. Вы также забыли добавить дополнительные параметры RecursiveIteratorIterator::CATCH_GET_CHILD
Необязательный флаг. Возможные значения: 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 автоматически пропускает ошибки чтения и должна немного упростить ваш код.