PHPでディレクトリにあるファイルを再帰的に探す処理

再帰的というのは、「鏡の中の鏡」、「GNU is Not Unix」のように自分自身を呼び出すことで、永遠と同じ処理を回すことができます。 下のプログラムの場合、recursiveOpenDirというfunctionの中で自分自身、つまりrecursiveOpenDirを呼び出しています。こうすることで永遠にディレクトリを探し続けることができます。 このプログラムはsitemapやRSSを作成する時などに応用することができます。また、拡張子の判定をjpgやpngに変えれば、画像処理も一括で行うことができます。

<?php
$rootDir = "/somewheredir/";

recursiveOpenDir($rootDir);

function recursiveOpenDir($dir)
{
    $od = opendir($dir);
    if (!$od) {return;}

    while (false !== ($file = readdir($od))) {
        $array = explode('.', $file);
        if (count($array) == 1) { // $fileがディレクトリである
            recursiveOpenDir($dir . $file . "/");
            continue;
        }
        if ($array[1] == "html") {
            echo $dir . $file . "\n";
        }
    }
}

このままでも十分ですが、せっかくですのでfunctionをclass化にすることでオブジェクトとして扱えるようにしたいと思いました。今までのPHPプログラミングでは、オブジェクティブ思考で考えてきませんでした。その場のやっつけ仕事でなんとかなってきたとも言えますが、Objective-Cやswift、kotlinなどでアプリケーション開発していると、PHPもオブジェクティブ思考で考えたくなります。その方が管理が楽ですし、資産化できると思いました。 上記のファンクションは、次のように書くことができました。

<?php
class RecursiveOpenObject
{
    public $path = "";
    public $filename = "";
    public $dist = "";
}

class RecursiveOpen
{
    protected $baseDir;
    protected $extension;
    protected $filePaths;
    protected $excludes;

    public function __construct(string $baseDir, string $extension, array $excludes)
    {
        $this->baseDir = $baseDir;
        $this->extension = $extension;
        $this->filePaths = array();
        $this->excludes = $excludes;
        $this->openDir($this->baseDir);
    }
    public function files()
    {
        return $this->filePaths;
    }

    private function openDir($dir)
    {
        $od = opendir($dir);
        if (!$od) {return;}

        while (false !== ($file = readdir($od))) {
            $array = explode('.', $file);
            if (in_array($array[0], $this->excludes)) {
                continue;
            }
            if (count($array) == 1) { // $fileがディレクトリである
                $this->openDir($dir . $file . "/");
                continue;
            }
            if ($array[1] == $this->extension) {
                $obj = new RecursiveOpenObject();
                $obj->path = $dir . $file;
                $obj->filename = $array[0];
                $obj->dist = preg_replace("{" . $this->baseDir . "}", "", $dir);

                array_push($this->filePaths, $obj);
            }
        }

    }

}

使い方は次のようになります。コンストラクターの第一引数に、検索対象となるルートディレクトリを絶対パスで指定します。第二引数には探したいファイルの拡張子を指定してあげます。第三引数には配列で、除外したいファイルを指定できるようにしてみました。インスタンス生成後、filesメソッドを呼び出すことで、RecursiveOpenObject配列を返してくれます。ちなみにRecursiveOpenObjectのdistというのは、最初に渡したルートディレクトリ以降のディレクトリを返してくれます。

$rootDir = "/somewheredir/";
$recursive = new RecursiveOpen($rootDir, "md", array("sample"));
foreach ($recursive->files() as $obj) {
    echo $obj->path.PHP_EOL;
    echo $obj->filename.PHP_EOL;
    echo $obj->dist.PHP_EOL;
}