ExtendedFileManager使用時に読み取り権限のないディレクトリがあるとFatal Errorになる件

とあるシステムのコンテンツ管理画面にWYSIWYGエディタのxinhaを使っており、コンテンツ(PDFやWordファイルなど)のアップロード用にExtendedFileManagerプラグインを有効にしています。
このプラグインで、突然Fatal Errorが出るようになったので調べたところ、コンテンツアップロードディレクトリ内に、とあるコンテンツのバックアップディレクトリがありました。このディレクトリはサーバー管理者が作成したのですが、ディレクトリのパーミッションApacheから読み取れない権限となっていました。

ExtendedFileManagerは、ある起点ディレクトリ以下のディレクトリツリーを再帰的に読み込んで表示するのですが、その読み込み処理で、上記のような「読み取り権限のない」ディレクトリがあるとFatal Errorになってしまいます。


ディレクトリのパーミッションを変更すれば済む話でもありますが、汎用的なWYSIWYGエディタであるxinha側でこのようなケースが想定されていないのも困るので、ソースを確認してみました。

xinha>plugins>ExtendedFileManager

Fatal Errorになるのは、ExtendedFileManager.phpの123行目でした。

<?php
113	    function _dirs($base, $path)
114	    {
115	        $base = Files::fixPath($base);
116	        $dirs = array();
117	
118	        if($this->isValidBase() == false)
119	            return $dirs;
120	
121	        $d = @dir($base);
122	       
123	        while (false !== ($entry = $d->read()))
124	        {
125	            //If it is a directory, and it doesn't start with
126	            // a dot, and if is it not the thumbnail directory
127	            if(is_dir($base.$entry)
128	                && substr($entry,0,1) != '.'
129	                && $this->isThumbDir($entry) == false)
130	            {
131	                $relative = Files::fixPath($path.$entry);
132	                $fullpath = Files::fixPath($base.$entry);
133	                $dirs[$relative] = $fullpath;
134	                $dirs = array_merge($dirs, $this->_dirs($fullpath, $relative));
135	            }
136	        }
137	        $d->close();
138	
139	        Return $dirs;
140	    }

この_dirs()メソッドで起点配下のディレクトリ一覧を再帰的に取得しているのですが、「読み取り権限のないディレクトリ」の場合、121行目のdir()関数の戻り値がfalseになるようで、その場合に123行目の$d->read()部分でFatal Errorになってしまいます。

ですので、121行目を次のように変更すればOKです。

<?php
if (false === ($d = @dir($base))) {
    return $dirs;
}

dir()のマニュアルページでは・・・

PHPドキュメントのdirのページを見てみると、特に「falseが返ってくる」という記述がありませんでした。

ユーザーが投稿したコメントを見ると、戻り値をifで検証しているものもあるようですが、オフィシャルのサンプルコードが以下のようにfalseを検証しないものとなっており、xinhaのExtendedFileManagerのコードもほぼこれをコピペして使用しているように見えます。

<?php
$d = dir("/etc/php5");
echo "Handle: " . $d->handle . "\n";
echo "Path: " . $d->path . "\n";
while (false !== ($entry = $d->read())) {
   echo $entry."\n";
}
$d->close();
?>

結論:コピペはキケン

ですよね。