2009-02-22 3 views
3

Я пытаюсь эффективно использовать Итераторы в 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 ... 

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

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

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

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

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

, но это поражение цели использования итераторов, и это не очень элегантно либо в виде раствора.

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

ответ

1

Правильно, в конце концов мне удалось обогнуть его. Мне пришлось использовать рекурсивный итератор, потому что входной итератор по существу генерирует дочерние результаты, и я расширил 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 
      ) 
     ); 
    } 
} 
0

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

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

Смежные вопросы