为了理解两者之间的区别,让我们编写两个将目录内容读入数组的函数——一个使用过程方法,另一个面向对象:
程序化,使用 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_p
也returns array('h', 'a', 'g')
可扩展性
以上两个函数展示了性能和可读性。请注意,如果您的代码需要做进一步的操作,代码 usingDirectoryIterator
更具可扩展性。
例如,在上面的函数list_directory_oo
中,该$fileinfo
对象为您提供了一堆方法,例如getMTime()
,getOwner()
等isReadable()
(其中大部分的返回值被缓存,不需要系统调用)。
因此,根据您的用例(即您打算对输入目录的每个子元素执行的操作),代码 using 的DirectoryIterator
性能可能与 using 的代码一样好,有时甚至更好opendir
。
您可以自己修改代码list_directory_oo
并进行测试。
概括
决定使用哪个完全取决于用例。
如果我要在 PHP 中编写一个 cronjob 递归扫描包含数千个文件的目录(及其子目录)并对它们进行某些操作,我会选择过程方法。
但如果我的要求是编写一种网络界面来显示上传的文件(比如在 CMS 中)及其元数据,我会选择DirectoryIterator
.
您可以根据自己的需要进行选择。