Создание нового итератора из результатов другого итератора - PullRequest
3 голосов
/ 22 февраля 2009

Я пытаюсь понять, как эффективно использовать итераторы в PHP 5, и без большого количества достойных примеров в сети это будет немного сложно.

Я пытаюсь перебрать каталог и прочитать все файлы (php) внутри, чтобы найти определенные классы. Затем я хочу получить ассоциативный массив с именами классов в качестве ключей, а пути к файлам в качестве значений.

Используя RecursiveDirectoryIterator (), я могу проходить через каталоги. Передав это в RecursiveIteratorIterator, я могу получить содержимое каталога как одномерный итератор. Затем, используя фильтр, я могу отфильтровать все каталоги и не-php файлы, которые просто оставят мне файлы, которые я хочу рассмотреть.

Теперь я хочу передать этот итератор другому итератору (не уверен, какой из них подойдет), чтобы при циклическом переборе каждой записи он мог получить массив, который нужно объединить в мастер массив.

Это немного сложно объяснить, поэтому вот пример кода:

// $php_files now represents an array of SplFileInfo objects representing files under $dir that match our criteria
$php_files = new PhpFileFilter(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)));

class ClassDetector extends FilterIterator {
    public function accept() {
        $file = $this->current(); // get the current item, which will be an SplFileInfo object

        // Match all the classes contained within this file
        if (preg_match($regex, $file->getContents(), $match)) {
            // Return an assoc array of all the classes matched, the class name as key and the filepath as value
            return array(
                'class1' => $file->getFilename(),
                'class2' => $file->getFilename(),
                'class3' => $file->getFilename(),
            );
        }
    }
}

foreach (new ClassDetector($php_files) as $class => $file) {
    print "{$class} => {$file}\n";
}

// Expected output:
// class1 => /foo.php
// class2 => /foo.php
// class3 => /foo.php
// class4 => /bar.php
// class5 => /bar.php
// ... etc ...

Как вы можете видеть из этого примера, я как бы угоняю метод accept () для FilterIterator, что, как я знаю, является совершенно некорректным использованием, но я использую его только в качестве примера, чтобы продемонстрировать, как я просто хочу одну функцию быть вызванным и для него вернуть массив, который объединен в основной массив.

В данный момент я думаю, что мне придется использовать один из RecursionIterator, поскольку, похоже, это то, что они делают, но мне не нравится идея использовать два разных метода (hasChildren () и getChildren ()) для достижения цели.

Короче говоря, я пытаюсь определить, какой итератор я могу использовать (или расширить), чтобы заставить его проходить по одномерному массиву (?) Объектов, и заставить его объединить полученный массив в основной. и верни это.

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

$master = array();
foreach($php_files as $file) {
    if (preg_match($regex, $file->getContents(), $match)) {
        // create $match_results
        $master = array_merge($master, $match_results);
    }
}

но это отрицательно сказывается на цели использования итераторов и не очень элегантно в качестве решения.

В любом случае, я надеюсь, что объяснил это достаточно хорошо. Спасибо, что прочитали это далеко, и за ваши ответы заранее:)

Ответы [ 2 ]

1 голос
/ 26 февраля 2009

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

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

class
    ClassMatcher
extends
    IteratorIterator
implements
    RecursiveIterator
{

    protected $matches;

    public function hasChildren() {
        return preg_match_all(
            '#class (\w+)\b#ism',
            file_get_contents($this->current()->getPathname()),
            $this->matches
        );
    }

    public function getChildren() {
        $classes = $this->matches[1];

        return new RecursiveArrayIterator(
            array_combine(
                $classes,                                                       // class name as key
                array_fill(0, count($classes), $this->current()->getPathname()) // file path as value
            )
        );
    }
}
0 голосов
/ 23 февраля 2009

Я когда-то делал нечто подобное. Источник прямо здесь , и я считаю, что это легко понять. Если у вас есть какие-либо проблемы, пожалуйста, дайте мне знать.

Основная идея состоит в том, чтобы расширить SplFileInfo и затем использовать RecursiveIteratorIterator :: setInfoClass ($ className); для получения информации об исходном коде. Фильтр для разбора только PHP-файлов мог бы подойти, хотя тогда я решил отфильтровать их по расширению в основном цикле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...