[PHP]ディレクトリを再帰的に削除する_SPL版

Read More



PHP5からは、SPL(Standard PHP Library)というライブラリが利用することができる。


大量のファイルが内包されたディレクトリを削除する場合、
何も考えずにscandir()を使うと配列に格納する段階でバッファオーバーフローを起こしてしまうことがある。

しかし、RecursiveIteratorIteratorクラスを使い配列ではなくオブジェクトとして扱うことで
このバッファオーバーフローを防ぐことができる。

このSPLイテレータを使ってディレクトリを再帰的に削除するものを実装する。



■ディレクトリを再帰的に削除する(SPL利用)
function rmdir_r($dir_path=null)
{
  $chk=true;
  
  // check
  if(!strlen($dir_path)) return false;
  if(!file_exists($dir_path)) return true;    // 存在しない場合は削除したものとみなす
  if(!is_dir($dir_path)) return false;
  if(basename($dir_path)==='.' or basename($dir_path)==='..') return false;    // 危険なので禁止
  
  // main
  $files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($dir_path, FilesystemIterator::SKIP_DOTS),
    RecursiveIteratorIterator::CHILD_FIRST
  );
  
  foreach($files as $file)
  {
    if($file->isDir())
    {
      if($chk) $chk = rmdir($file->getPathname());
    }
    else
    {
      if($chk) $chk = unlink($file->getPathname());
    }
  }
  
  if($chk) $chk = rmdir($dir_path);
  
  return $chk;
}


上記は入れ子になっているディレクトリや内包するファイルも消える。



--
なおRecursiveIteratorIteratorクラスを使わずとも、
dir()やopendir()を使ってもバッファオーバーフローの対策を取ったようなものは実装できる。
function rmdir_r($dir_path=null)
{
  if(!strlen($dir_path)) return false;
  if(!file_exists($dir_path)) return true;
  if(is_file($dir_path) or is_link($dir_path)) return @unlink($dir_path);

  $objDir = @dir($dir_path);
  while($fl=$objDir->read())
  {
    if($fl==='.' or $fl==='..') continue;
    if(!rmdir_r($dir_path. '/'. $fl)) return false;
  }
  $objDir->close();

  return @rmdir($dir_path);
}

こんな感じで動くと思う(たぶん)
ただ、この場合ディレクトリの入れ子の数によってメモリが増えていく(たぶん)
未検証。


--
昔と違って使えるメモリ量は多くなっているのでscandir()を使っていても問題になるようなことはあまりないのだけど。


暇があればそれぞれの動作速度をそのうち検証してみたい。