В чем преимущества использования PHP 5 DirectoryIterator над PHP 4 "opendir/readdir/closedir"?

В чем преимущества использования 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?

Ответы

Ответ 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.

Вы можете выбрать, исходя из ваших потребностей.

Ответ 2

Пособие 1: вы можете скрыть все детали расточки.

При использовании итераторов вы обычно определяете их где-то в другом месте, поэтому реальный код будет выглядеть примерно так:

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

Особенности итерации через каталоги, подкаталоги и фильтрация нежелательных элементов скрыты от приложения. Вероятно, это не интересная часть вашего приложения, поэтому приятно скрыть эти фрагменты в другом месте.

Пособие 2: то, что вы делаете с результатом, отделено от получения результата.

В приведенном выше примере вы можете поменять этот конкретный итератор на другой итератор, и вам не нужно вообще изменять то, что вы делаете с результатом. Это делает код немного легче поддерживать и добавлять новые функции в дальнейшем.

Ответ 3

A DirectoryIterator предоставляет вам предметы, которые имеют смысл сами по себе. Например, DirectoryIterator::getPathname() вернет всю информацию, необходимую для доступа к содержимому файла.

Информация, которую readdir() предоставляет вам только локальный смысл, а именно в сочетании с параметром, который вы передали opendir().

DirectoryIterator реализуется в терминах оберток вокруг функций php_stream_*, поэтому не следует ожидать принципиально разных характеристик производительности. В частности, элементы из каталога считываются только тогда, когда они запрашиваются. Подробности можно найти в файле

внутр/УЗД/spl_directory.c

исходного кода PHP.

Ответ 4

Он короче, чище и проще печатать и читать.

Попробуйте перечитать ваши примеры. Просто "для каждого в $dir" в первом примере.

Что вы хотите, чтобы писать...