Edited at

RecursiveDirectoryIteratorで親ディレクトリまで対象に入る


前書き

ぼく「/var/www/tmp/以下にある全部のディレクトリを削除したいな~」

ぼく「/var/www/以下が全部消えた…」


ダメなコード


dame.php

<?php

$iterator = new RecursiveDirectoryIterator("/var/www/tmp/");
$iterator = new RecursiveIteratorIterator($iterator);

foreach ($file_infos as $file_info) {
//そのファイルがディレクトリなら
if ($file_info->isDir()) {
exec(escapeshellcmd("rm -rf " . $file_info->getRealPath()));
}
}


これだと、...まで削除してしまいます。つまり、rm -rf /var/wwwが実行されてしまいます。


大丈夫なコード


ok.php

<?php

$iterator = new RecursiveDirectoryIterator("/var/www/tmp/",FilesystemIterator::SKIP_DOTS);
$iterator = new RecursiveIteratorIterator($iterator,RecursiveIteratorIterator::SELF_FIRST);
$file_infos = iterator_to_array($iterator);

foreach ($file_infos as $file_info) {
//そのファイルがディレクトリなら
if ($file_info->isDir()) {
exec(escapeshellcmd("rm -rf " . $file_info->getRealPath()));
}
}


FilesystemIterator::SKIP_DOTSを入れることで、...をスキップします。


ハマったこと


ディレクトリを取得できない

/var/www/tmp/hoge/に入った時にも.がスキップされるので、ディレクトリが取得できなくなります(伝われ)

そこで、サブフォルダ内のファイルをまとめて取得するにあるように、RecursiveIteratorIteratorの引数にRecursiveIteratorIterator::SELF_FIRSTを付けると取得できます。


UnexpectedValueExceptionが投げられる

foreachの部分、最初は

foreach ($iterator as $file_info) {

と書いていたのですが、こうするとUnexpectedValueExceptionが出ます。

これはforeachの中でディレクトリを削除しているので、$iteratorが次の値を取るときに削除されていて取れないみたいな感じの感じだと思います。

そこで、stackoverflowにあるように、iterator_to_array()を使ってイテレータを配列にしちゃいます。

メモリ使用量は増えますがまあいいや(適当)


最後に

/var/www/htmlも吹き飛ぶので気を付けようね!