15

使用 PHP 5 DirectoryIterator 到底有什么好处

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

通过 PHP 4 "opendir/readdir/closedir"

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

除了 OOP 附带的子类化选项?

4

4 回答 4

22

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

程序化,使用 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;
}

面向对象,使用 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;
}

表现

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

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

在我的笔记本电脑(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)

在入门级 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)

以上是 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')

可扩展性

以上两个函数展示了性能和可读性。请注意,如果您的代码需要做进一步的操作,代码 usingDirectoryIterator更具可扩展性。

例如,在上面的函数list_directory_oo中,该$fileinfo对象为您提供了一堆方法,例如getMTime(),getOwner()isReadable()(其中大部分的返回值被缓存,不需要系统调用)。

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

您可以自己修改代码list_directory_oo并进行测试。

概括

决定使用哪个完全取决于用例。

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

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

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

于 2013-12-27T13:51:22.820 回答
20

好处一:你可以隐藏所有无聊的细节。

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

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

遍历目录、子目录和过滤掉不需要的项目的细节都对应用程序隐藏。无论如何,这可能不是您的应用程序中有趣的部分,因此能够将这些位隐藏在其他地方是件好事。

好处2:你对结果所做的事情与获得结果是分开的。

在上面的示例中,您可以将该特定迭代器换成另一个迭代器,而您根本不需要更改结果的处理方式。这使得代码更容易维护并在以后添加新功能。

于 2013-12-28T22:30:33.703 回答
7

ADirectoryIterator为您提供本身有意义的项目。例如,DirectoryIterator::getPathname()将返回访问文件内容所需的所有信息。

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

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

ext/spl/spl_directory.c

PHP 源代码。

于 2013-07-21T19:26:19.750 回答
3
于 2013-07-21T19:30:00.460 回答