PHP TIPS:ITproを見ていたら「PHP TIPS – 71. PHPの便利な関数 glob:ITpro」という記事がありまして。多くのレンタルサーバでMySQLが使えて、WordPressで手軽にブログを立ち上げることのできるこの時代に、今更PHPでファイル操作をしてカウンターや掲示板を設置することもないだろうと思いましたが、知っといて損もないだろうと思い取り上げてみました。
PHPでファイルの一覧表示方法っていっぱいあるんですね
glob関数だけかと思いきや、結構いろいろな方法があるのですよ。
- globを使う
- scandirを使う
- opendirとreaddirを使う
- DirectoryIteratorを使う
準備
今回のために次のようなフォルダとファイルを用意しました。
Googleの画像検索で適当にファイルをダウンロードしてフォルダにいれました。後の実験のためにjpg、png、gifを複数用意。なんでラーメンなのかというと、お腹が減っていたからです(真顔
各方法でファイル名の一覧表示
「ra-men」フォルダ以下のファイル名の一覧をそれぞれの方法で表示させてみます。
1 2 3 4 5 6 | <?php foreach(glob("ra-men/*.*") as $file) { $result[] = $file; } print_r($result); |
無事表示されます。
その他の方法も試してみます。
scandir
1 2 3 4 5 | <?php $path = "c:/xampp/php/ra-men"; $result = scandir($path); print_r($result); |
これでもいけました。
1 2 3 4 5 | <?php $path = "ra-men"; $result = scandir($path); print_r($result); |
opendirとreaddir
1 2 3 4 5 6 7 8 9 10 11 | <?php $path = "ra-men"; if ($dh = opendir($path)) { while(($file = readdir($dh)) !== false) { $result[] = $file; } closedir($dh); } print_r($result); |
DirectoryIterator
1 2 3 4 5 6 7 8 | <?php $path = "ra-men"; foreach(new DirectoryIterator($path) as $file) { $result[] = $file->getPathname(); } print_r($result); |
速度比較
各方法で実行速度にどれくらい差がでるのか比べてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <?php function list_glob($path) { foreach(glob($path) as $file) { $result[] = $file; } return $result; } function list_scandir($path) { $result = scandir($path); return $result; } function list_opendir_and_readdir($path) { if ($dh = opendir($path)) { while(($file = readdir($dh)) !== false) { $result[] = $file; } closedir($dh); } return $result; } function list_DirectoryIterator($path) { foreach(new DirectoryIterator($path) as $file) { $result[] = $file->getPathname(); } return $result; } $start1 = microtime(true); for ($i=0; $i < 10000; $i++) { $files1 = list_glob("ra-men/*.*"); } $time1 = round((microtime(true) - $start1) * 1000, 5); $start2 = microtime(true); for ($i=0; $i < 10000; $i++) { $files2 = list_scandir("ra-men"); } $time2 = round((microtime(true) - $start2) * 1000, 5); $start3 = microtime(true); for ($i=0; $i < 10000; $i++) { $files3 = list_opendir_and_readdir("ra-men"); } $time3 = round((microtime(true) - $start3) * 1000, 5); $start4 = microtime(true); for ($i=0; $i < 10000; $i++) { $files4 = list_DirectoryIterator("ra-men"); } $time4 = round((microtime(true) - $start4) * 1000, 5); echo "glob:" . $time1 . "\n"; echo "scandir:" . $time2 . "\n"; echo "opendir&readdir:" . $time3 . "\n"; echo "DirectoryIterator:" . $time4 . "\n"; |
結果はこうなりました。環境はインテルi7@3.40GHz、メモリ8GB、64ビットのWindows7です。
scandir:3081.177
opendir&readdir:3076.17593
DirectoryIterator:3372.19214
単位は「ミリ秒」です。globだけ8秒かかっており、倍以上の時間です(;´Д`)
ではどのような場合にglob関数を使うか?
ファイルの一覧表示のにおいてはスピードで劣るglob関数ですが、メリットは「パターンにマッチするパス名をリスト化して返してくれる」ことです。先ほどのフォルダでjpgファイルだけを抜き出すならば、
1 | glob("ra-men/*.jpg"); |
でできます。例えばこれをopendirとreaddirでやろうとすると、
1 2 3 4 5 6 7 8 | if ($dh = opendir("ra-men")) { while(($file = readdir($dh)) !== false) { if (jpgファイルであるか判定) { $result[] = $file; } } closedir($dh); } |
としなければならず、ソースコードの可読性は落ちます。
実行速度の面ではどうでしょうか。先ほどのソースコードをこちらの記事「PHP – ファイル名から拡張子を取得する関数、ベンチマークもあるよ – たきゃはしです」を参考に拡張子によりファイルを抜き出す処理を付け足してみます。拡張子の判別方法は最速である「’.’を検索し、それ以降の文字を取得する」方法でいきたいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | <?php function judge_extension($filename) { return substr($filename, strrpos($filename, '.') + 1); } function list_glob($path) { foreach(glob($path) as $file) { $result[] = $file; } return $result; } function list_scandir($path) { $files = scandir($path); foreach ($files as $file) { if (judge_extension($file) == "jpg") { $result[] = $file; } } return $result; } function list_opendir_and_readdir($path) { if ($dh = opendir($path)) { while(($file = readdir($dh)) !== false) { if (judge_extension($file) == "jpg") { $result[] = $file; } } closedir($dh); } return $result; } function list_DirectoryIterator($path) { foreach(new DirectoryIterator($path) as $file) { if (judge_extension($file->getPathname()) == "jpg") { $result[] = $file->getPathname(); } } return $result; } $start1 = microtime(true); for ($i=0; $i < 10000; $i++) { $files1 = list_glob("ra-men/*.*"); } $time1 = round((microtime(true) - $start1) * 1000, 5); $start2 = microtime(true); for ($i=0; $i < 10000; $i++) { $files2 = list_scandir("ra-men"); } $time2 = round((microtime(true) - $start2) * 1000, 5); $start3 = microtime(true); for ($i=0; $i < 10000; $i++) { $files3 = list_opendir_and_readdir("ra-men"); } $time3 = round((microtime(true) - $start3) * 1000, 5); $start4 = microtime(true); for ($i=0; $i < 10000; $i++) { $files4 = list_DirectoryIterator("ra-men"); } $time4 = round((microtime(true) - $start4) * 1000, 5); echo "glob:" . $time1 . "\n"; echo "scandir:" . $time2 . "\n"; echo "opendir&readdir:" . $time3 . "\n"; echo "DirectoryIterator:" . $time4 . "\n"; |
結果
scandir:3256.18601
opendir&readdir:3237.185
DirectoryIterator:3554.20303
(;^ω^)
関数の呼び出しコストなのか、glob関数以外の方法で約0.2秒ずつ処理時間が伸びましたが、結局globだけが倍以上の時間がかかることに変わりはなく。
結論
glob関数はソースコードの可読性を上げることには役に立つものの、処理速度の面ではその他の方法に比べて2倍以上の処理時間がかかることは間違いないようです。ただ、ファイル名の取得処理はそれほど頻繁に行うような処理でもないので、処理速度にそれほど気を配る必要もないし、複数のフォルダにまたがったファイル検索もglob関数の一文で表現できることから、やはり便利な関数であると思います。
なんだか、glob関数をディスっただけで終わってしまったような。
Comment