Ответ 1
Чтобы понять разницу между ними, напишите две функции, которые читают содержимое каталога в массив - один использует процедурный метод и другой объект, ориентированный:
Процедурный, используя 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')
расширяемость
Выше две функции продемонстрировали производительность и удобочитаемость. Обратите внимание, что если вашему коду необходимо выполнить дальнейшие операции, код с использованием DirectoryIterator
более расширяемый.
например. В функции list_directory_oo
выше объект $fileinfo
предоставляет вам множество методов, таких как getMTime()
, getOwner()
, isReadable()
и т.д. (Возвращаемые значения большинства из них кэшируются и не требуют системных вызовов).
Поэтому, в зависимости от вашего прецедента (то есть, что вы намереваетесь делать с каждым дочерним элементом каталога ввода), возможно, что код с использованием DirectoryIterator
работает так же хорошо или иногда лучше, чем код, используя opendir
.
Вы можете изменить код list_directory_oo
и протестировать его самостоятельно.
Резюме
Решение о том, что использовать полностью, зависит от варианта использования.
Если бы мне пришлось писать cronjob в PHP, который рекурсивно сканирует каталог (и его подкаталоги), содержащий тысячи файлов и выполняющий определенные операции над ними, я бы выбрал процедурный метод.
Но если мое требование - написать своего рода веб-интерфейс для отображения загруженных файлов (скажем, в CMS) и их метаданных, я бы выбрал DirectoryIterator
.
Вы можете выбрать, исходя из ваших потребностей.