使用PHP 5 DirectoryIterator而不是PHP 4"opendir/readdir/closedir"的好处是什么?

e-s*_*shi 13 php

使用PHP 5 DirectoryIterator的好处是什么?

$dir = new DirectoryIterator(dirname(__FILE__));
foreach ($dir as $fileinfo) 
{
    // handle what has been found
}
Run Code Online (Sandbox Code Playgroud)

超过PHP 4"opendir/readdir/closedir"

if($handle = opendir(dirname(__FILE__))) 
{
    while (false !== ($file = readdir($handle))) 
    {
        // handle what has been found
    }
    closedir($handle);
}
Run Code Online (Sandbox Code Playgroud)

除了OOP附带的子类选项?

Man*_*ath 21

为了理解两者之间的区别,让我们编写两个将目录内容读入数组的函数 - 一个使用过程方法,另一个使用面向对象:

程序,使用opendir/readdir/closedir

function list_directory_p($dirpath) {
    if (!is_dir($dirpath) || !is_readable($dirpath)) {
        error_log(__FUNCTION__ . ": Argument should be a path to valid, readable directory (" . var_export($dirpath, true) . " provided)");
        return null;
    }
    $paths = array();
    $dir = realpath($dirpath);
    $dh = opendir($dir);
    while (false !== ($f = readdir($dh))) {
        if ("$f" != '.' && "$f" != '..') {
            $paths[] = "$dir" . DIRECTORY_SEPARATOR . "$f";
        }
    }
    closedir($dh);
    return $paths;
}
Run Code Online (Sandbox Code Playgroud)

面向对象,使用DirectoryIterator

function list_directory_oo($dirpath) {
    if (!is_dir($dirpath) || !is_readable($dirpath)) {
        error_log(__FUNCTION__ . ": Argument should be a path to valid, readable directory (" . var_export($dirpath, true) . " provided)");
        return null;
    }
    $paths = array();
    $dir = realpath($dirpath);
    $di = new DirectoryIterator($dir);
    foreach ($di as $fileinfo) {
        if (!$fileinfo->isDot()) {
            $paths[] = $fileinfo->getRealPath();
        }
    }
    return $paths;
}
Run Code Online (Sandbox Code Playgroud)

性能

让我们先评估一下他们​​的表现:

$start_t = microtime(true);
for ($i = 0; $i < $num_iterations; $i++) {
    $paths = list_directory_oo(".");
}
$end_t = microtime(true);
$time_diff_micro = (($end_t - $start_t) * 1000000) / $num_iterations;
echo "Time taken per call (list_directory_oo) = " . round($time_diff_micro / 1000, 2) . "ms (" . count($paths) . " files)\n";

$start_t = microtime(true);
for ($i = 0; $i < $num_iterations; $i++) {
    $paths = list_directory_p(".");
}
$end_t = microtime(true);
$time_diff_micro = (($end_t - $start_t) * 1000000) / $num_iterations;
echo "Time taken per call (list_directory_p) = " . round($time_diff_micro / 1000, 2) . "ms (" . count($paths) . " files)\n";
Run Code Online (Sandbox Code Playgroud)

在我的笔记本电脑(Win 7/NTFS)上,程序方法似乎是明显的赢家:

C:\code>"C:\Program Files (x86)\PHP\php.exe" list_directory.php
Time taken per call (list_directory_oo) = 4.46ms (161 files)
Time taken per call (list_directory_p) = 0.34ms (161 files)
Run Code Online (Sandbox Code Playgroud)

在入门级AWS机器(CentOS)上:

[~]$ php list_directory.php
Time taken per call (list_directory_oo) = 0.84ms (203 files)
Time taken per call (list_directory_p) = 0.36ms (203 files)
Run Code Online (Sandbox Code Playgroud)

以上是PHP 5.4的结果.使用PHP 5.3和5.2可以看到类似的结果.当PHP在Apache或NGINX上运行时,结果类似.

代码可读性

虽然速度较慢,但​​代码使用DirectoryIterator更具可读性.

文件阅读顺序

使用任一方法读取的目录内容的顺序完全相同.也就是说,如果list_directory_oo退货array('h', 'a', 'g'),list_directory_preturns array('h', 'a', 'g')

可扩展性

以上两个功能展示了性能和可读性.请注意,如果您的代码需要进行进一步的操作,则代码使用DirectoryIterator更具可扩展性.

例如,在功能list_directory_oo上面,该$fileinfo对象提供了一堆的方法,例如getMTime(),getOwner(),isReadable()等(最,其中被缓存返回值,也没有要求系统调用).

因此,根据您的用例(即您打算对输入目录的每个子元素执行的操作),使用的代码可能与使用的代码DirectoryIterator一样好或有时更好opendir.

您可以修改代码list_directory_oo并自行测试.

摘要

完全使用的决定取决于用例.

如果我在PHP中编写一个cronjob,它递归扫描包含数千个文件的目录(及其子目录)并对它们进行某些操作,我会选择程序方法.

但是,如果我的要求是编写一种Web界面来显示上传的文件(比如在CMS中)及其元数据,我会选择DirectoryIterator.

您可以根据自己的需要进行选择.

  • @ e-sushi:哦,这里是jPony :)我从来没有说你应该使用它来进行目录访问:)而且DirectoryItertor并不慢.这仅仅取决于您的文件系统,并不是瓶颈.这就是这个答案具有误导性的点,那些完成的统计数据没有用.能够提高实际上是瓶颈的速度(当你意识到它实际上是一个时),而不重写大部分代码实际上是.这就是迭代者闪耀的地方.我在这个答案中发现有点不平衡,这是我的评论想要解决的问题. (7认同)
  • 表现论证是情绪.您可以从DirectoryIterator扩展,实现自己的遍历(例如,基于文件系统),并随意使用`readdir`.不需要这样做才能开始,这是一个争论,而不是反对它.快速向客户发送工作代码具有更好的性能.如果你可以更容易地维护它,例如,如果遍历确实是瓶颈,你可以轻松加快速度.如果没有,过早照顾它是错误的.您应该在答案中看到这些性能比较容易隐藏重要的事情. (6认同)

Lev*_*son 19

好处1:你可以隐藏所有无聊的细节.

使用迭代器时,通常将它们定义在其他地方,因此现实代码看起来更像是:

// ImageFinder is an abstraction over an Iterator
$images = new ImageFinder($base_directory);
foreach ($images as $image) {
    // application logic goes here.
}
Run Code Online (Sandbox Code Playgroud)

迭代目录,子目录和过滤掉不需要的项目的细节都是从应用程序隐藏的.无论如何,这可能不是你的应用程序中有趣的部分,所以能够将这些内容隐藏在其他地方是很好的.

好处2:您对结果的处理与获得结果分开.

在上面的示例中,您可以将该特定迭代器替换为另一个迭代器,并且您根本不必更改对结果执行的操作.这使代码更容易维护并在以后添加新功能.


Osw*_*ald 7

A DirectoryIterator为您提供有意义的物品.例如,DirectoryIterator::getPathname()将返回访问文件内容所需的所有信息.

readdir()提供给您的信息仅在本地有意义,即与您传递给的参数结合使用opendir().

DirectoryIterator是根据php_stream_*函数的包装器实现的,因此不会出现根本不同的性能特征.特别是,目录中的项目仅在请求时才被读取.详细信息可以在文件中找到

EXT/SPL/spl_directory.c

PHP源代码.