Finderコンポーネント:ファイルを見つける


約2日前、Symfony 2のFinderという新しいコンポーネントがgithubに登場しました。 そして今日、 ファビアンのツイッターこのコンポーネントに関する彼のブログの新しい投稿へのリンクを見ました。 それでは、理解しましょう。 カットの下で、 「ファイル検索」は翻訳を投稿します 。 それでは始めましょう。

PHPを使用してファイルを見つけるためのベストプラクティスは、近年大幅に進化しています。 2004年に戻って、PHPで最初に行ったことの1つは、PerlモジュールFile :: Find :: Rule to PHPを移植することでした。 ファイル::検索::ルールは、作業したいファイルとディレクトリを記述するのに最適な方法です。 組み込みのPHP関数opendir、readdir、closedirを使用しましたが、非常にうまく機能しました。 PHPクラスはsfFinderと呼ばれていましたが、symfonyバージョンでも引き続き使用できます。 クラスがsymfonyにアタッチされている場合でも、symfonyに関連するとは限らない、ほぼすべてのタイプのタスクに使用する人がいることを知っています。

しかし、コードはその年齢を示し始めました。 第一に、それ以来PHPについて多くのことを学んだからです。また、より良いオプションがあるからです。 イテレータが登場しました! PHP 5には、すべての種類の反復および反復を簡素化する反復用のクラスが多数付属しています。 非常に強力なPHPコンストラクトである標準のforeachステートメントを反復処理できます。

PHPのイテレーター


では、PHPイテレータを使用してすべてのファイルとディレクトリを再帰的に取得する方法は? 率直に言って、私は知りません。 使用するクラスとそれらをどのように組み合わせるかは多かれ少なかれ知っていますが、気にしないために、既存のコードブロックを常に使用してこれを正しく行います。 このコードは次のとおりです。
// ... (symlink)
$flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS;
//
$iterator = new \RecursiveDirectoryIterator($dir, $flags);
//
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
//
foreach ($iterator as $file)
{
// - $file ( \SplFileInfo)
}

注:各組み込みクラスの前に派手な\文字がありますか? これは、PHP 5.3名前空間のコンテキストで組み込みのPHPクラスを使用する場合にアクセスする方法です。

ご覧のとおり、複雑なことはありません。 使用するイテレータ、適用できるフラグ、およびそれらを一緒に使用する方法を知る必要があります。 つまり、主要な参入障壁は学習です。 インターネットにはイテレータに関する多くのプレゼンテーションとチュートリアルがありますが、php.netの公式ドキュメントにはおそらく良い例が欠けています。

もう1つの「問題」は、すべてが非常にオブジェクト指向であることです。 そして、イテレータをフィルタリングしたいならすぐに、独自のクラスを作成する必要がありますが、ほとんどの場合、実用的ではありません。 これは、PHPイテレータが非常に強力であり、汎用イテレータとして作成されたためです。

フィルタリングとは? たとえば、.rbで終わるすべてのファイルをイテレータから除外したいとします。 このために、単純な\ FilterIteratorを作成できます。
class ExcludeRubyFilesFilterIterator extends \FilterIterator
{
public function accept() {
$fileinfo = $ this ->getInnerIterator()->current();

if (preg_match( '/\.rb$/' , $fileinfo)) {
return false ;
}

return true ;
}
}


フィルター反復子は、たとえば次を含めることで、以前のコードで使用できます。
$iterator = new ExcludeRubyFilesFilterIterator($iterator);

とても簡単です。 ただし、ファイルやディレクトリを見つける必要がある場合、バージョン管理システム(.svnや.gitディレクトリなど)からファイルを除外したり、名前やサイズでファイルをフィルタリングしたりするなど、1つの特殊なタイプのフィルターが常に必要です。

symfonyのFinderコンポーネント


同じイテレータを何度も書く代わりに、Symfonyコンポーネントとして、つまりFinderコンポーネントとしてまとめました。

SymfonyのFinderコンポーネントには、ファイルとディレクトリを見つけるための多くの特殊なイテレータクラスが付属しています。 彼はまた、日常の使いやすさのためにラッパーを追加します。

すべてのSymfonyコンポーネントと同様に、最初は、Symfony UniversalClassLoaderクラスなどのPHP 5.3互換性標準に準拠したクラスをロードできるクラスローダーでスクリプトをロードする必要があります。
require_once '/path/to/src/Symfony/Foundation/UniversalClassLoader.php' ;

use Symfony\Foundation\UniversalClassLoader;

$classLoader = new UniversalClassLoader();
$classLoader->registerNamespace( 'Symfony' , '/path/to/src' );
$classLoader->register();


次に、メインコンポーネントクラスであるFinderクラスの使用方法を見てみましょう。
use Symfony\Components\Finder\Finder;

$finder = new Finder();
$iterator = $finder->files()-> in (__DIR__);

foreach ($iterator as $file)
{
print $file->getRealpath(). "\n" ;
}


上記のコードは、現在のディレクトリ内のすべてのファイルの名前を再帰的に出力します。 FinderクラスはFluentインターフェイスを使用することに注意してください(メソッド呼び出しをチェーンできる場合)。つまり、すべてのメソッドがFinderクラスのインスタンスを返します。 唯一の例外はin()メソッドで、指定されたカテゴリまたはディレクトリの配列のイテレータを構築して返します:
$iterator = $finder->files()-> in (array( '/path1' , '/path2' ));

注:iterator_to_array()メソッドを使用してイテレーターを配列に変換し、iterator_count()を使用して要素の数を取得できます。

現在のディレクトリ内のPHPファイルのみを返すようにイテレータを制限する場合は、name()およびmaxDepth()メソッドを使用します。
$iterator = $finder
->files()
->name( '*.php' )
->maxDepth(0)
-> in (__DIR__);

name()メソッドは、グロブ、文字列、または正規表現をサポートしています。
$finder
->files()
->name( '/\.php$/' );


ファイルを名前で除外したり、コンテンツディレクトリ全体を一致から除外する方法もあります。
$finder
->files()
->name( 'test.*' )
->notName( '*.rb' )
->exclude( 'ruby' );


結果には拡張子がtestのファイルが含まれますが、拡張子が.rbでない場合のみ(test.rbは除外されます)、反復子はrubyディレクトリ内のファイルと一致しません(たとえば、ruby / foo / test.phpは一致しません) 。

リンクをたどる場合は、followLinks()メソッドを使用します。
$finder
->files()
->followLinks();


サイズによってファイルを制限することもできます。
$finder
->files()
->name( '/\.php$/' )
->size( '< 1.5K' );


ほとんどのメソッドは累積的です。 つまり、サイズが1〜2 KのPHPおよびPythonのすべてのファイルを取得する場合、同様のコードを使用できます。
$finder
->files()
->name( '*.php' )
->name( '*.py/' )
->size( '>= 1K' )
->size( '<= 2K' );


注:デフォルトでは、反復子は一般的なバージョン管理システムファイルを無視します。 これはignoreVCS()メソッドを使用して変更できます。

in()メソッドは\ Iteratorのインスタンスを返すため、特殊なイテレーターでラップできます。 ただし、クラスを作成する代わりに、filter()メソッドを使用することもできます。
$filter = function (\SplFileInfo $fileinfo)
{
if (strlen($fileinfo) > 10) {
return false ;
}
};

$finder
->files()
->name( '*.php' )
->filter($filter);


この例では、ファイルの長さが10文字を超えるすべてのファイルを除外します。

結果を名前で並べ替える場合は、sortByName()メソッドを使用します。
$finder
->files()
->name( '*.php' )
->sortByName();


sort *メソッドは、ジョブを実行するためにすべての一致する要素を渡す必要があることに注意してください。 大きなイテレータの場合、これは非常に遅くなる可能性があります。

カーテンの後ろで、Finderクラスは特殊なイテレータークラスを使用します。
コードを見て、これらのイテレーターとその機能について詳しく学んでください。

Source: https://habr.com/ru/post/J91689/


All Articles