Создание нового итератора из результатов другого итератора

Я пытаюсь понять, как эффективно использовать итераторы в 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, что, как я знаю, является совершенно неправильным использованием, но я использую его только в качестве примера, чтобы продемонстрировать, как я просто хочу, чтобы вызывалась одна функция. , и чтобы он возвращал массив, объединенный в главный массив.

На данный момент я думаю, что мне придется использовать один из RecursionIterators, поскольку это похоже на то, что они делают, но мне не нравится идея использования двух разных методов (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);
    }
}

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

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


person JamShady    schedule 22.02.2009    source источник


Ответы (2)


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

В любом случае, вот пример кода, на всякий случай, если это поможет кому-то еще. Это предполагает, что вы передали массив объектов 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
            )
        );
    }
}
person JamShady    schedule 25.02.2009

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

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

person Ionuț G. Stan    schedule 23.02.2009