みんな大好きディレクトリ一覧をソートしつつ再帰的に表示する実装

####ディレクトリ一覧（再帰的）を表示する
Symfony2になら便利なファイル操作関数が用意されてるが、プレーンなPHPにはそんなの無いのでつくってみた

見ため的にはWindowsのCMDにあるTREEコマンドのようになる

#####方法
おやくその再帰処理をするだけな簡単な実装

1. パスを指定してファイル一覧を取得
1. 1件毎にディレクトリか調べてディレクトリなら１へ
1. ファイル一覧からディレクトリが無くなればソートを行う
1. すべてのファイルを取得するまで1－3を繰り返す

#####注意

このコードは.(ドット)から始まるファイル名は表示しません。
.(ドット)から始まるファイルを表示させる為には以下のように「GlobIterator」の部分を
「RecursiveDirectoryIterator」クラスを使用するとフィルタされなくなります。
この部分についてはコメント頂いた [RecursiveTreeIteratorでtreeコマンドを実装](http://qiita.com/Hiraku/items/4da9fe08ea83773f9631) を参考にさせていただきました。

```php:サンプルコード
//    $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
<?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

```