【问题标题】:unable to skip unreadable directories with RecursiveDirectoryIterator无法使用 RecursiveDirectoryIterator 跳过不可读的目录
【发布时间】:2013-01-02 20:53:13
【问题描述】:

我想获取所有子目录的列表,并且我的以下代码可以正常工作,除非我对某些文件夹具有只读权限。

在下面的问题中,它显示了如何使用 RecursiveDirectoryIterator 跳过目录 Can I make RecursiveDirectoryIterator skip unreadable directories? 但是我的代码在这里略有不同,我无法解决这个问题。

$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 内所有目录的列表,我在哪里出错了?

【问题讨论】:

  • 如果不返回www里面的所有目录,返回什么?
  • 它返回目录,直到它得到一个它没有权限读取的目录,我收到错误,我希望能够跳过那些受限文件夹并返回目录中的所有其他文件夹

标签: php directory directory-walk


【解决方案1】:

简介

您的代码的主要问题是使用CHILD_FIRST

FROM PHP DOC

可选模式。可能的值是

  • RecursiveIteratorIterator::LEAVES_ONLY - 默认值。列表只在迭代中离开。
  • RecursiveIteratorIterator::SELF_FIRST - 列出迭代中的叶子和父节点,父节点在前。
  • RecursiveIteratorIterator::CHILD_FIRST - 列出迭代中的叶子和父节点,叶子在前。

您应该使用SELF_FIRST 以便包含当前目录。你也忘了加可选参数RecursiveIteratorIterator::CATCH_GET_CHILD

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

【讨论】:

  • 此代码似乎生成了我所追求的文件夹格式
  • @ak85 它肯定会......我只在第二个例子中添加了一个装饰器
【解决方案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;
    }

}

此代码 100% 有效...

您可以简单地使用此功能来扫描所需目录或驱动器中的文件和文件夹。您只需要将所需目录的路径传递给函数。该函数的第二个参数是显示扫描文件和文件夹的完整路径。第二个参数的值为false表示不显示全路径。

数组 $ignore 用于从列表中排除任何所需的文件名或文件夹名。

该函数返回包含文件和文件夹列表的数组。

此函数会跳过递归时不可读的文件和文件夹。

【讨论】:

    【解决方案3】:

    我已经设置了以下目录结构:

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

    我运行了以下代码(我添加了SKIP_DOTS 标志以跳过... btw):

    $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
    )
    

    【讨论】:

    • 谢谢,这给了我数组中的结果,我需要在 iterator_to_array 上阅读更多内容,因为当我尝试将您的 sn-p 放入我的数组时,它会运行代码,但在初始测试中没有给出任何结果.
    • @ak85 当您将我的答案作为独立代码运行时会发生什么?
    • 我得到了您所描述的跳过安全文件夹的数组,但到目前为止,我无法将您的代码转换为我在示例中的文件夹名称列表。
    • @ak85 我已经更新了我的答案,以模仿你在问题开始时的回答;看看这是否适合你。
    • 这看起来不错,我现在离开我的测试环境,明天会确认。
    【解决方案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】:

    glob 函数会自动跳过读取错误,并且还应该稍微简化您的代码。

    【讨论】:

    • 我确实查看了这个谢谢,但无法获得 www 的子目录?
    • 您应该能够通过将 glob 模式设置为 **/* 来获取子目录。
    【解决方案6】:

    如果您遇到未处理的异常,为什么不将该代码放在 try 块中,并在无法读取目录时使用异常捕获块来捕获错误?通过查看您的代码和您的问题只是一个简单的建议。在 PHP 中可能有一种更简洁的方法。

    【讨论】:

      【解决方案7】:

      如果要返回不可读的目录名称,则需要使用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";
          }
      }
      

      【讨论】:

        【解决方案8】:

        尝试捕获 UnexpectedValueException 怎么样。也许您甚至可以检查该错误的唯一异常代码。否则,您可以邪恶地解析“权限被拒绝”的异常消息。

        我建议检查http://php.net/manual/de/class.unexpectedvalueexception.php

        【讨论】:

          猜你喜欢
          • 2011-05-31
          • 2015-11-24
          • 1970-01-01
          • 2023-03-18
          • 2013-07-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-25
          相关资源
          最近更新 更多