読者です 読者をやめる 読者になる 読者になる

k-holyのPHPとか諸々メモ

Webで働くk-holyがPHP(スクリプト言語)とか諸々のことをメモしていきます。ソースコードはだいたいWindowsで動かしてます。

RecursiveIteratorIteratorとRecursiveDirectoryIteratorで再帰ファイル処理してみた

PHP SPL

下記のような構造のディレクトリとファイルに対して、RecursiveDirectoryIteratorで走査し、RecursiveIteratorIteratorで処理を行う場合

DIR
    ├──DIR-1
    │        ├──DIR-1-1
    │        │        ├──DIR-1-1-1
    │        │        │        ├──FILE-1-1-1-1.php
    │        │        │        └──FILE-1-1-1-2.txt
    │        │        ├──FILE-1-1-1.php
    │        │        └──FILE-1-1-2.txt
    │        ├──DIR-1-2
    │        │        ├──DIR-1-2-1
    │        │        │        └──FILE-1-2-1-1.php
    │        │        └──FILE-1-2-1.txt
    │        ├──FILE-1-1.php
    │        └──FILE-1-2.txt
    ├──DIR-2
    │        ├──DIR-2-1
    │        │        ├──FILE-2-1-1.php
    │        │        └──FILE-2-1-2.txt
    │        ├──DIR-2-2
    │        │        ├──FILE-2-2-1.php
    │        │        └──FILE-2-2-2.txt
    │        ├──FILE-2-1.txt
    │        └──FILE-2-2.php
    └──DIR-3
              └──DIR-3-1
                        └──DIR-3-1-1
                                  └──DIR-3-1-1-1

ソース

RecursiveIteratorIterator::__construct()の第2引数がどう影響するかを見てみます。


RecursiveIteratorIterator::LEAVES_ONLY (デフォルト値)の場合

string(16) "FILE-1-1-1-1.php"
string(16) "FILE-1-1-1-2.txt"
string(14) "FILE-1-1-1.php"
string(14) "FILE-1-1-2.txt"
string(16) "FILE-1-2-1-1.php"
string(14) "FILE-1-2-1.txt"
string(12) "FILE-1-1.php"
string(12) "FILE-1-2.txt"
string(14) "FILE-2-1-1.php"
string(14) "FILE-2-1-2.txt"
string(14) "FILE-2-2-1.php"
string(14) "FILE-2-2-2.txt"
string(12) "FILE-2-1.txt"
string(12) "FILE-2-2.php"

イテレーションで返されるのはリーフ(子を持たないノード、つまりファイル)のみ。


RecursiveIteratorIterator::SELF_FIRSTの場合

string(5) "DIR-1"
string(7) "DIR-1-1"
string(9) "DIR-1-1-1"
string(16) "FILE-1-1-1-1.php"
string(16) "FILE-1-1-1-2.txt"
string(14) "FILE-1-1-1.php"
string(14) "FILE-1-1-2.txt"
string(7) "DIR-1-2"
string(9) "DIR-1-2-1"
string(16) "FILE-1-2-1-1.php"
string(14) "FILE-1-2-1.txt"
string(12) "FILE-1-1.php"
string(12) "FILE-1-2.txt"
string(5) "DIR-2"
string(7) "DIR-2-1"
string(14) "FILE-2-1-1.php"
string(14) "FILE-2-1-2.txt"
string(7) "DIR-2-2"
string(14) "FILE-2-2-1.php"
string(14) "FILE-2-2-2.txt"
string(12) "FILE-2-1.txt"
string(12) "FILE-2-2.php"
string(5) "DIR-3"
string(7) "DIR-3-1"
string(9) "DIR-3-1-1"
string(11) "DIR-3-1-1-1"

親(ディレクトリ)を優先して返される。


RecursiveIteratorIterator::CHILD_FIRSTの場合

string(16) "FILE-1-1-1-1.php"
string(16) "FILE-1-1-1-2.txt"
string(9) "DIR-1-1-1"
string(14) "FILE-1-1-1.php"
string(14) "FILE-1-1-2.txt"
string(7) "DIR-1-1"
string(16) "FILE-1-2-1-1.php"
string(9) "DIR-1-2-1"
string(14) "FILE-1-2-1.txt"
string(7) "DIR-1-2"
string(12) "FILE-1-1.php"
string(12) "FILE-1-2.txt"
string(5) "DIR-1"
string(14) "FILE-2-1-1.php"
string(14) "FILE-2-1-2.txt"
string(7) "DIR-2-1"
string(14) "FILE-2-2-1.php"
string(14) "FILE-2-2-2.txt"
string(7) "DIR-2-2"
string(12) "FILE-2-1.txt"
string(12) "FILE-2-2.php"
string(5) "DIR-2"
string(11) "DIR-3-1-1-1"
string(9) "DIR-3-1-1"
string(7) "DIR-3-1"
string(5) "DIR-3"

子(ファイル)を優先して返される。
たとえば、ファイルとディレクトリを再帰的に削除する場合、CHILD_FIRSTの方がいいわけですね。


FilterIteratorを継承してフィルタリングを試してみます。ディレクトリのみ取得する例。

結果はこうなる

string(5) "DIR-1"
string(7) "DIR-1-1"
string(9) "DIR-1-1-1"
string(7) "DIR-1-2"
string(9) "DIR-1-2-1"
string(5) "DIR-2"
string(7) "DIR-2-1"
string(7) "DIR-2-2"
string(5) "DIR-3"
string(7) "DIR-3-1"
string(9) "DIR-3-1-1"
string(11) "DIR-3-1-1-1"


GlobIteratorなんかも用意されてるけど、FilesystemIteratorは普通にストリームラッパーに対応してるようなので、RecursiveDirectoryIteratorの第1引数でglob://プロトコルを指定すると同じことができます。
DIR-1から始まるディレクトリのみ走査します。

結果

string(5) "DIR-1"
string(7) "DIR-1-1"
string(9) "DIR-1-1-1"
string(16) "FILE-1-1-1-1.php"
string(16) "FILE-1-1-1-2.txt"
string(14) "FILE-1-1-1.php"
string(14) "FILE-1-1-2.txt"
string(7) "DIR-1-2"
string(9) "DIR-1-2-1"
string(16) "FILE-1-2-1-1.php"
string(14) "FILE-1-2-1.txt"
string(12) "FILE-1-1.php"
string(12) "FILE-1-2.txt"


ストリームラッパーに対応してるなら、他にもいろんな事ができそうです。
試しに同一階層にsilex.pharを設置して、phar://プロトコルで中身を覗いてみます。

SplFileInfo::getPathname()でちゃんとphar://プロトコル形式のパスが返ってきてたので、ソースをハイライト表示できました。


参考記事

[PHP] 追加されたイテレータクラス - FilesystemIteratorの設定と実行 | idocsq.net
http://idocsq.net/page/108

単なるファイル走査なら、SymfonyのFinderコンポーネントの方が多機能かつ簡潔に書けそうですが…。
Find your Files - Fabien Potencier
http://fabien.potencier.org/article/43/find-your-files