SplRecurisveDirectoryIterator и иерархический массив

Я хотел поиграть с некоторыми итераторами PHP, и мне удалось получить прочную (насколько я понимаю) сборку. Моя цель состояла в том, чтобы выполнить итерацию внутри родительской папки и отключить 2 узла; построение иерархического древовидного массива в процессе. Очевидно, я мог бы сделать это довольно легко, используя glob и пару вложенных циклов, но я хочу использовать для этого классы Spl.

Все это не по пути, я играл с SplHeap и SplObjectStore для иерархии и потерпел неудачу. Что мешает моей лапше, так это то, что мои обычные методы рекурсии терпят неудачу (ошибки нехватки памяти), и мой единственный успех приходится на рекурсивный метод, который зацикливается на каждом узле, добавляя к массиву. Проблема в том, что он игнорирует метод setMaxDepth() и проходит через все дочерние элементы. Я думал о том, чтобы установить $var++ для увеличения в цикле, ограничивая количество узлов, но я не верю, что это «правильный путь».

В любом случае, код (извините за любой потерянный код, если он есть - просто игнорируйте его)...

<?php
namespace Tree;

use RecursiveFilterIterator,
    RecursiveDirectoryIterator,
    RecursiveIteratorIterator;

class Filter extends RecursiveFilterIterator {
    public static $FILTERS = array(
        '.git', '.gitattributes', '.gitignore', 'index.php'
    );

    public function accept() {
        if (!$this->isDot() && !in_array($this->current()->getFilename(), self::$FILTERS))
            return TRUE;

        return FALSE;
    }
}

class DirTree {
    const MAX_DEPTH = 2;

    private static $iterator;
    private static $objectStore;

    public function __construct() {

        error_reporting(8191);
        $path       = realpath('./');

        try {

            $dirItr     = new RecursiveDirectoryIterator($path);
            $filterItr  = new Filter($dirItr);
            $objects    = new RecursiveIteratorIterator($filterItr, RecursiveIteratorIterator::SELF_FIRST);

            $objects->setMaxDepth(self::MAX_DEPTH);

            echo '<pre>';
            print_r($this->build_hierarchy($objects));

        } catch(Exception $e) {
            die($e->getMessage());
        }
    }

    public function build_hierarchy($iterator){
        $array = array();
        foreach ($iterator as $fileinfo) {

            if ($fileinfo->isDir()) {
                // Directories and files have labels
                $current = array(
                    'label' => $fileinfo->getFilename()
                );
                // Only directories have children
                if ($fileinfo->isDir()) {
                    $current['children'] = $this->build_hierarchy($iterator->getChildren());
                }
                // Append the current item to this level
                $array[] = $current;
            }
        }
        return $array;
    }
}

$d = new DirTree;

person Jason Oakley    schedule 30.10.2012    source источник


Ответы (1)


RecursiveIteratorIterator предназначен в первую очередь для того, чтобы дать вам итератор, который ведет себя как итератор для плоского списка, но плоский список на самом деле является просто последовательностью в рекурсивном обходе. Он делает это, внутренне управляя стеком рекурсивных итераторов, вызывая для них getChildren() по мере необходимости. Клиент RecursiveIteratorIterator на самом деле должен вызывать только обычные методы Iterator, такие как current() и next() и т. д.... за исключением методов с добавленной стоимостью, таких как setMaxDepth()

Ваша проблема в том, что вы пытаетесь выполнить рекурсию самостоятельно, вызвав getChildren(). Если вы хотите вручную управлять рекурсией, это нормально, но это делает RecursiveIteratorIterator излишним. На самом деле, я очень удивлен, что вызов getChildren() для RecursiveIteratorIterator не привел к фатальной ошибке. Это RecursiveIterator метод. spl, вероятно, просто перенаправляет вызов метода внутреннему итератору (некоторые классы spl пересылают вызовы методов неопределенным методам, чтобы упростить использование шаблона проектирования Decorator).

Правильный путь:

    $dirItr     = new RecursiveDirectoryIterator($path);
    $filterItr  = new Filter($dirItr);
    $objects    = new RecursiveIteratorIterator($filterItr, RecursiveIteratorIterator::SELF_FIRST);

    $objects->setMaxDepth(self::MAX_DEPTH);

    echo '<pre>';
    foreach ($objects as $splFileInfo) {
        echo $splFileInfo;
        echo "\n";
    }

Я не собираюсь вдаваться в формирование иерархического массива в какой-то конкретной структуре для вас, но, возможно, этот связанный вопрос поможет вам понять разницу между RecursiveIteratorIterator и RecursiveIterator Как работает RecursiveIteratorIterator в PHP?

person goat    schedule 11.11.2012