3

PHP 5 で Iterators を効果的に使用する方法を理解しようとしていますが、ネット上に適切な例がたくさんないため、少し難しいことがわかりました。

ディレクトリをループして、その中のすべての (php) ファイルを読み取って、定義されたクラスを検索しようとしています。次にやりたいことは、クラス名をキーとして、ファイル パスを値として返す連想配列を作成することです。

RecursiveDirectoryIterator() を使用することで、ディレクトリを再帰的に処理できます。これを RecursiveIteratorIterator に渡すことで、ディレクトリの内容を 1 次元イテレータとして取得できます。次に、これにフィルターを使用することで、すべてのディレクトリと、考慮したいファイルだけを残す非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 の accept() メソッドをハイジャックしています。これは私が知っている完全に間違った使用法ですが、1 つの関数を呼び出す方法を示すための例としてのみ使用しています。 、マスター配列にマージされた配列を返すようにします。

現時点では、RecursionIterator の 1 つを使用する必要があると考えていますが、これは RecursionIterator の機能のように思われるためですが、2 つの異なるメソッド (hasChildren() と getChildren() を使用するという考えは好きではありません) )) 目標を達成する。

要するに、オブジェクトの 1 次元配列 (?) を渡すために使用 (または拡張) できる Iterator を特定し、結果の配列をマスター配列に結合してそれを返すようにしようとしています。 .

これには他にもいくつかの方法があることに気づきました。たとえば、次のようなものです。

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

しかし、これはイテレータを使用する目的に反しており、ソリューションとしてもあまりエレガントではありません。

とにかく、私はそれを十分に説明したことを願っています。ここまで読んでくれてありがとう、そして事前に答えてくれてありがとう:)

4

2 に答える 2

1

そうです、私は最終的にそれを理解することができました。入力イテレーターは本質的に子の結果を生成するため、再帰イテレーターを使用する必要があり、イテレーターをループする機能を既に持っていたイテレーターイテレーターを拡張しました。

とにかく、これが他の人に役立つ場合に備えて、コード例を次に示します。これは、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
            )
        );
    }
}
于 2009-02-25T23:21:22.533 に答える
0

私はかつて似たようなことをしました。ソースはここにあり、簡単に理解できると思います。問題がある場合は、お知らせください。

主なアイデアは、SplFileInfoを拡張してからRecursiveIteratorIterator::setInfoClass($className);を使用することです。ソースコードに関する情報を取得するため。PHP ファイルのみを解析するためのフィルターは便利かもしれませんが、当時はメイン ループで拡張子によってフィルター処理することにしました。

于 2009-02-23T08:59:19.690 に答える