ディレクトリ一覧(再帰的)を表示する
Symfony2になら便利なファイル操作関数が用意されてるが、プレーンなPHPにはそんなの無いのでつくってみた
見ため的にはWindowsのCMDにあるTREEコマンドのようになる
方法
おやくその再帰処理をするだけな簡単な実装
- パスを指定してファイル一覧を取得
- 1件毎にディレクトリか調べてディレクトリなら1へ
- ファイル一覧からディレクトリが無くなればソートを行う
- すべてのファイルを取得するまで1-3を繰り返す
注意
このコードは.(ドット)から始まるファイル名は表示しません。
.(ドット)から始まるファイルを表示させる為には以下のように「GlobIterator」の部分を
「RecursiveDirectoryIterator」クラスを使用するとフィルタされなくなります。
この部分についてはコメント頂いた RecursiveTreeIteratorでtreeコマンドを実装 を参考にさせていただきました。
サンプルコード
// $targetPath = realpath($path) . DIRECTORY_SEPARATOR . '*';
//
// //ファイル一覧を生成する
// $glob = new GlobIterator($targetPath, FilesystemIterator::CURRENT_AS_FILEINFO);
// $tree = array();
// foreach ($glob as $item) {
//上記の部分を以下に書き換え
//ファイル一覧を生成する
$allFileIterator = new RecursiveDirectoryIterator($path);
$tree = array();
foreach ($allFileIterator as $item) {
クロージャを使用してますが投稿者の趣味です。
クラス/メソッドで実装したほうがスマートな気がします。
<?php
header('Content-Type: text/plain');
$path = dirname(__FILE__);
$directorySearcher = function ($path) use(&$directorySearcher) {
if (is_file($path)) {
//Pathがファイルを指していたらファイルだけのリストを返す
return array('leaf' => new SplFileInfo($path));
}
if (is_dir($path) === false) {
//ディレクトリ以外なら処理終了
return false;
}
$targetPath = realpath($path) . DIRECTORY_SEPARATOR . '*';
//ファイル一覧を生成する
$glob = new GlobIterator($targetPath, FilesystemIterator::CURRENT_AS_FILEINFO);
$tree = array();
foreach ($glob as $item) {
//ファイルシステム操作関数のSplFileInfoクラスを生成
$tree[$item->getRealPath()] = array('leaf' => $item->getFileInfo());
if ($item->isDir()) {
//ディレクトリなら再帰処理
$tree[$item->getRealPath()]['node'] = $directorySearcher($item->getRealPath());
}
}
//末端のノードに達したらノード一覧をソートする
//ソートのルールは動的に変更可能以下はディレクトリファイルの順且つ、ファイルネームの昇順
foreach ($tree as $node => $leaf) {
$sortIndex1st[$node] = $leaf['leaf']->isDir();
$sortIndex2nd[$node] = $leaf['leaf']->getFilename();
}
if (count($tree) > 0) {
array_multisort($sortIndex1st, SORT_DESC, $sortIndex2nd, SORT_ASC, $tree);
}
return $tree;
};
//実装例
$printScript = function($dir, $head = '')use(&$printScript) {
foreach ($dir as $leafItems) {
$leaf = $leafItems['leaf'];
echo $head, ($leaf->isDir()) ? '├' : '│';
echo $leaf->getFilename();
if ($leaf->isFile()) {
echo ':', floor($leaf->getSize() / 1024), '.', ($leaf->getSize() % 1024), 'kb';
}
echo PHP_EOL;
if ($leaf->isDir()) {
$printScript($leafItems['node'], $head . '│');
}
}
};
//実行
$directorys = $directorySearcher($path);
if ($directorys !== false) {
$printScript($directorys);
}
表示例
試しにPEARのテストディレクトリを表示させてみる
├Console_Table
│├tests
│││assoziative_arrays.phpt:0.538kb
│││colors.phpt:0.673kb
│││filters.phpt:0.717kb
│││multibyte.phpt:0.511kb
│││multiline.phpt:1.579kb
│││no_header.phpt:0.364kb
│││no_rows.phpt:0.441kb
│││rules.phpt:1.502kb
├Mail
│├tests
│││13659.phpt:0.651kb
│││9137.phpt:0.1001kb
│││9137_2.phpt:1.202kb
│││rfc822.phpt:2.802kb
│││smtp_error.phpt:0.720kb
│││validateQuotedString.php:0.640kb
├Net_SMTP
│├tests
│││auth.phpt:0.859kb
│││basic.phpt:0.785kb
│││config.php.dist:0.488kb
│││quotedata.phpt:1.771kb
├Structures_Graph
│├tests
││├testCase
││││BasicGraph.php:8.905kb
│││AllTests.php:3.268kb
├XML_Util
│├tests
│││AllTests.php:3.560kb
│││testBasic_apiVersion.phpt:0.449kb
│││testBasic_attributesToString.phpt:3.559kb
│││testBasic_collapseEmptyTags.phpt:1.771kb
│││testBasic_createCDataSection.phpt:0.494kb
│││testBasic_createComment.phpt:0.471kb
│││testBasic_createEndElement.phpt:0.696kb
│││testBasic_createStartElement.phpt:3.701kb
│││testBasic_createTag.phpt:5.796kb
│││testBasic_createTagFromArray.phpt:9.89kb
│││testBasic_getDocTypeDeclaration.phpt:1.492kb
│││testBasic_getXmlDeclaration.phpt:1.143kb
│││testBasic_isValidName.phpt:1.386kb
│││testBasic_raiseError.phpt:0.502kb
│││testBasic_replaceEntities.phpt:3.176kb
│││testBasic_reverseEntities.phpt:3.161kb
│││testBasic_splitQualifiedName.phpt:0.983kb
│││testBug_4950.phpt:0.675kb
│││testBug_5392.phpt:1.73kb