3

SPL イテレータを介してディレクトリを再帰的にスキャンする標準的な方法は次のとおりです。

$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($path),
    RecursiveIteratorIterator::CHILD_FIRST
);

foreach ($files as $file) {
    print $file->getPathname() . PHP_EOL;
}

構成可能な一連のフィルターを再帰的なファイル検索に適用したいと考えています。を使用しRecursiveDirectoryIteratorてディレクトリ構造をスキャンしています。

ディレクトリ構造に複数のフィルタを適用したいと考えています。

私のセットアップコード:

$filters = new FilterRuleset(
    new RecursiveDirectoryIterator($path)
);
$filters->addFilter(new FilterLapsedDirs);
$filters->addFilter(new IncludeExtension('wav'));
$files = new RecursiveIteratorIterator(
    $filters, RecursiveIteratorIterator::CHILD_FIRST
);

ルール セットを使用して N 個のフィルターを適用できると考えました。

class FilterRuleset extends RecursiveFilterIterator {
    private $filters = array();

    public function addFilter($filter) {
        $this->filters[] = $filter;
    }

    public function accept() {
        $file = $this->current();

        foreach ($this->filters as $filter) {
            if (!$filter->accept($file)) {
                return false;
            }
        }

        return true;
    }
}

設定したフィルタリングが意図したとおりに機能しません。フィルターを確認するFilterRulesetと、最初の呼び出しでフィルターが設定され、その後の呼び出しでは空白になります。内部RecursiveIteratorIteratorで my を再インスタンス化しているかのようFilterRulesetです。

    public function accept() {
        print_r($this->filters);
        $file = $this->current();

        foreach ($this->filters as $filter) {
            if (!$filter->accept($file)) {
                return false;
            }
        }

        return true;
    }

出力:

Array
(
    [0] => FilterLapsedDirs Object
        (
        )

    [1] => IncludeExtension Object
        (
            [ext:private] => wav
        )
)
Array
(
)
Array
(
)
Array
(
)
Array
(
)
Array
(
)
Array
(
)

私は PHP 5.1.6 を使用していますが、5.4.14 でテストしましたが、違いはありません。何か案は?

4

1 に答える 1

6

FilterRuleset のフィルターを確認すると、最初の呼び出しでフィルターが入力され、その後の呼び出しでは空白になります。内部で RecursiveIteratorIterator が FilterRuleset を再インスタンス化しているかのようです。

はい、まさにその通りです。サブディレクトリに移動するたびに、配列は空になります。これは、再帰反復子の規則に従って、再帰フィルター反復子が子反復子を提供する必要があるためです。

したがって、ここには 2 つのオプションがあります。

  1. 平坦化された反復、つまりツリー トラバーサルの後にフィルターを適用します。子ではなく、個々のファイルのみをフィルタリングする必要がある限り、あなたのケースでは実行可能に見えます。
  2. 標準的な方法:フィルターが設定されgetChildren()た構成済みの recursive-filter-iterator オブジェクトを返すように注意してください。FilterRuleset

私は 2 番目のものから始めます。なぜなら、それはすぐに完了し、これを行う通常の方法だからです。

getChildren()親メソッドをクラスに追加して上書きします。次に、親の結果を取得します (これはFilterRuleset子の新しいものであり、プライベート メンバーを設定します。これは、クラスの同じレベルにあるため、PHP で可能です (それがプライベート メンバーであるため、彼が機能することに疑問がある場合)。その後、それを返すだけで完了です:

class FilterRuleset extends RecursiveFilterIterator
{
    private $filters = array();

    ...

    public function getChildren() {
        $children = parent::getChildren();
        $children->filters = $this->filters;
        return $children;
    }
}

もう1つの(最初の)バリアントは、基本的にそれを「フラット」フィルターに「劣化」させることです。これは標準FilterIteratorです。したがって、最初に a を使用して再帰的な反復を行い、RecursiveIteratorIteratorそれをフィルター反復子にラップします。ツリーは以前の反復子によって既にトラバースされているため、この再帰的なものはすべて必要ありません。

したがって、まず最初にそれを the に変換しますFilterIterator

class FilterRuleset extends FilterIterator
{
   ...
}

唯一の変更は、そのクラスで拡張するものからです。そして、少し異なる順序でインスタンス化します:

$path  = __DIR__;
$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
    RecursiveIteratorIterator::CHILD_FIRST
);

$filtered = new FilterRuleset($files);
$filtered->addFilter(Accept::byCallback(function () {
    return true;
}));

foreach ($filtered as $file) {
    echo $file->getPathname(), PHP_EOL;
}

これらの例が明確であることを願っています。これらをいじって問題が発生した場合 (またはそうでない場合でも)、フィードバックはいつでも歓迎します。

ああ、忘れる前に: 上記の例でフィルターを作成するために使用したモックを次に示します。

class Accept
{
    private $callback;

    public function __construct($callback) {
        $this->callback = $callback;
    }

    public function accept($subject) {
        return call_user_func($this->callback, $subject);
    }

    public static function byCallback($callback) {
        return new self($callback);
    }
}
于 2013-05-23T22:17:45.960 に答える