2009-03-01 9 views
5

PHPファイルのロードを含むディレクトリを繰り返し処理し、各ファイルにどのようなクラスが定義されているかを検出しようとしています。再帰的イテレータの結果を結合する:親を持つ子どもたち

は、以下を考慮してください

$php_files_and_content = new PhpFileAndContentIterator($dir); 
foreach($php_files_and_content as $filepath => $sourceCode) { 
    // echo $filepath, $sourceCode 
} 

上記$php_files_and_content変数キーがファイルパスであり、かつ、例えばから明らかではなかったかのようにコンテンツがファイルのソースコード(あるイテレータを表します)。

これは、ソースコード内のすべての定義されたクラスに一致する別のイテレータ、ALAに供給される:「クラスC」がで定義されたため、$indexが数値的にシーケンシャルされていない理由である

class DefinedClassDetector extends FilterIterator implements RecursiveIterator { 
    public function accept() { 
     return $this->hasChildren(); 
    } 

    public function hasChildren() { 
     $classes = getDefinedClasses($this->current()); 
     return !empty($classes); 
    } 

    public function getChildren() { 
     return new RecursiveArrayIterator(getDefinedClasses($this->current())); 
    } 
} 

$defined_classes = new RecursiveIteratorIterator(new DefinedClassDetector($php_files_and_content)); 

foreach($defined_classes as $index => $class) { 
    // print "$index => $class"; outputs: 
    // 0 => Class A 
    // 1 => Class B 
    // 0 => Class C 
} 

2番目のソースコードファイルが返され、返される配列はインデックス0から再び開始されます。これは、結果の各セットが別々のイテレータ(したがってキーと値のペア)を表すため、RecursiveIteratorIteratorで保持されます。

とにかく、私が今しようとしているのは、これらを組み合わせる最良の方法を見つけることです。つまり、新しいイテレータを反復するとき、鍵はクラス名($defined_classesイテレータから)元のファイルパス:ala:

foreach($classes_and_paths as $filepath => $class) { 
    // print "$class => $filepath"; outputs 
    // Class A => file1.php 
    // Class B => file1.php 
    // Class C => file2.php 
} 

これまでのところ、私はこれまで立ち往生しています。

現時点では、現在の()メソッドをオーバーライドして外部イテレータのキー()(元のファイルパス)とkey()を返す新しいRecursiveIteratorを作成することだけが考えられます)メソッドを使用して現在のiterator()値を返します。それは私のに対し、コードは恐ろしいなりますと、

  • ビジネスルールは、クラス内でハードコードされている直感的ではありませんを意味している(複雑な音

    • :ので、しかし、私はこのソリューションを好むていませんよいくつかの一般的なイテレータを定義し、必要な結果を生成するような方法でそれらを結合できるようにしたいと思います。

    任意のアイデアや提案を感謝して受け取った。

    私はまた、より効率的で、はるかに高速があります実現これを行う方法、しかしthまた、Iteratorsを使用してコードを再利用するための演習でもあるため、新しいイテレータはできるだけ最小限に抑え、既存の機能を活用するようにしてください。

    おかげで

  • +0

    +1バーを上げてPHPでOOPを使う –

    答えて

    2

    OK、私はついにこれを頭に浮かべていたと思います。ここで私は擬似コードでやったおおよそです:私たちは、このように、我々は次のことを行うことができ、ディレクトリの内容を一覧表示する必要が

    ステップ1

    我々は検討する必要がある
    // Reads through the $dir directory 
    // traversing children, and returns all contents 
    $dirIterator = new RecursiveDirectoryIterator($dir); 
    
    // Flattens the recursive iterator into a single 
    // dimension, so it doesn't need recursive loops 
    $dirContents = new RecursiveIteratorIterator($dirIterator); 
    

    ステップ2 唯一のPHPファイル

    class PhpFileIteratorFilter { 
        public function accept() { 
         $current = $this->current(); 
         return $current instanceof SplFileInfo 
           && $current->isFile() 
           && end(explode('.', $current->getBasename())) == 'php'; 
        } 
    } 
    
    
    // Extends FilterIterator, and accepts only .php files 
    $php_files = new PhpFileIteratorFilter($dirContents); 
    

    PhpFileIteratorFilterは、再利用可能なコードの偉大な使用ではありません。より良い方法は、ファイル拡張子を構築の一部として提供し、そのファイルに一致するフィルタを取得することでした。それは、戦略パターンをより有効に活用するため、必要とされていない建設論から離れ、構成にもっと頼っていこうとしています。 PhpFileIteratorFilterは、単純に汎用のFileExtensionIteratorFilterを使用し、それ自体を内部的に設定することができます。我々は今、ファイルの内容

    class SplFileInfoReader extends FilterIterator { 
    
        public function accept() { 
         // make sure we use parent, this one returns the contents 
         $current = parent::current(); 
         return $current instanceof SplFileInfo 
           && $current->isFile() 
           && $current->isReadable(); 
        } 
    
        public function key() { 
         return parent::current()->getRealpath(); 
        } 
    
        public function current() { 
         return file_get_contents($this->key()); 
        }  
    } 
    
    // Reads the file contents of the .php files 
    // the key is the file path, the value is the file contents 
    $files_and_content = new SplFileInfoReader($php_files); 
    

    ステップ4 に読まなければならない

    ステップ3 今、私たちは、各項目(ファイルの内容)に当社のコールバックを適用し、何とか結果を残したいです。やはり、戦略パターンを利用しようとすると、私は不必要なコンストラクタ引数を取り除いてしまった。 $preserveKeysまたは類似したので、ここで私は私が通じ供給$callbackの結果を返します、私の基本的なイテレータを持っているが、私は、あまりにもキーが保持されますバージョンを作成するのではなく、ためにそれを拡張しました

    /** 
    * Applies $callback to each element, and only accepts values that have children 
    */ 
    class ArrayCallbackFilterIterator extends FilterIterator implements RecursiveIterator { 
    
        public function __construct(Iterator $it, $callback) { 
         if (!is_callable($callback)) { 
          throw new InvalidArgumentException('$callback is not callable'); 
         } 
    
         $this->callback = $callback; 
         parent::__construct($it); 
        } 
    
        public function accept() { 
         return $this->hasChildren(); 
        } 
    
        public function hasChildren() { 
         $this->results = call_user_func($this->callback, $this->current()); 
         return is_array($this->results) && !empty($this->results); 
        } 
    
        public function getChildren() { 
         return new RecursiveArrayIterator($this->results); 
        } 
    } 
    
    
    /** 
    * Overrides ArrayCallbackFilterIterator to allow a fixed $key to be returned 
    */ 
    class FixedKeyArrayCallbackFilterIterator extends ArrayCallbackFilterIterator { 
        public function getChildren() { 
         return new RecursiveFixedKeyArrayIterator($this->key(), $this->results); 
        } 
    } 
    
    
    /** 
    * Extends RecursiveArrayIterator to allow a fixed $key to be set 
    */ 
    class RecursiveFixedKeyArrayIterator extends RecursiveArrayIterator { 
    
        public function __construct($key, $array) { 
         $this->key = $key; 
         parent::__construct($array); 
        } 
    
        public function key() { 
         return $this->key; 
        } 
    } 
    

    そのためのコンストラクタ引数を使用します。

    こうして私たちは、この持っている:

    // Returns a RecursiveIterator 
    // key: file path 
    // value: class name 
    $class_filter = new FixedKeyArrayCallbackFilterIterator($files_and_content, 'getDefinedClasses'); 
    

    ステップ5 を今、私たちは、適切な様式にそれをフォーマットする必要があります。私は値にファイルパスを望む、とキーは、クラス名である(すなわち、それは、オートローダのために見つけることができているファイルにクラスの直接マッピングを提供するために)

    // Reduce the multi-dimensional iterator into a single dimension 
    $files_and_classes = new RecursiveIteratorIterator($class_filter); 
    
    // Flip it around, so the class names are keys 
    $classes_and_files = new FlipIterator($files_and_classes); 
    

    そして、今、$classes_and_filesを繰り返して、定義されているファイルとともに$ dirの下に定義されているすべてのクラスのリストを取得できます。これを行うために使用されたコードのほとんどすべてが他のコンテキストでも再利用可能です。私はこのタスクを達成するために定義されたイテレータで何かをハードコーディングしていないし、イテレータの外で余分な処理もしていない。

    0

    私はあなたが何をしたいのかと思う、多かれ少なかれPhpFileAndContentから返されたキーと値を逆にすることです。このクラスはfilepath => sourceのリストを返します。最初にマッピングを逆にしてsource => filepathにしてから、sourceに定義されている各クラスに対してsourceを展開すると、class1 => filepath, class2 => filepathになります。

    getChildren()にあるように、$this->key()にアクセスして、実行中のソースの現在のファイルパスを取得することができます。をgetDefinedClasses($path, $source)と書くことができ、すべてのクラスのインデックス付き配列を返す代わりに、現在のインデックス配列の各値が辞書のキーであり、その値がそのクラスが定義されたファイルパスである辞書を返します。

    あなたが望むようにそれが出てきます。

    他のオプションは、RecursiveArrayIteratorの使用を削除し、代わりに

    return new FilePathMapperIterator($this->key,getDefinedClasses($this->current())); 
    

    として(getChildrenに)初期化され、独自のイテレータを書き、その後FilePathMapperIteratorclass => filepathマッピングIにgetDefinedClassesからクラスの配列を変換することです配列を繰り返し処理し、現在のクラスをkey()に戻し、指定されたファイルパスを常にcurrent()に戻すことで説明しています。

    後者はよりクールですが、確かにコードが多いので、私のニーズに合わせてgetDefinedClasses()を適合させることができれば、そうしたことは起こりそうにないと思います。

    関連する問題