PHPでディレクトリを再帰的にループ処理する方法と実例解説

PHPでファイルシステム内のディレクトリを再帰的にループ処理することは、特に大規模なプロジェクトや複数のフォルダ階層を扱う場合に非常に有用です。この技術を使えば、指定されたディレクトリ内のファイルやフォルダを再帰的に探索し、必要な処理を適用することができます。本記事では、PHPでこのような再帰処理を行うための基本的な概念から、実際のコード例、そして応用的な使い方までをわかりやすく解説します。

目次

再帰処理の基本概念

再帰処理とは、ある関数が自分自身を呼び出して処理を繰り返すアルゴリズムの一種です。主に階層構造を扱う際に有効で、ディレクトリ構造やツリー構造のデータを処理する際に広く利用されます。再帰処理では、終了条件を明確に設定することが重要です。終了条件がない場合、無限ループが発生してしまうからです。PHPにおいても、再帰処理を使うことでディレクトリ内のサブディレクトリを効率的に処理することが可能です。

PHPでの再帰処理の実装方法

PHPで再帰処理を実装するには、関数を自分自身で呼び出す必要があります。ディレクトリ構造の探索では、特定のディレクトリを読み込み、その中にさらにディレクトリがあれば同じ処理を繰り返すことで、すべてのサブディレクトリを辿ることができます。以下は、PHPで再帰処理を実装するための基本的な関数の例です。

function recursiveDirectoryLoop($dir) {
    // ディレクトリの内容を取得
    $files = scandir($dir);

    foreach ($files as $file) {
        // 現在のディレクトリと親ディレクトリを除外
        if ($file === '.' || $file === '..') continue;

        // フルパスを作成
        $fullPath = $dir . DIRECTORY_SEPARATOR . $file;

        // ディレクトリの場合、再帰的に関数を呼び出す
        if (is_dir($fullPath)) {
            echo "Directory: " . $fullPath . "\n";
            recursiveDirectoryLoop($fullPath);  // 再帰呼び出し
        } else {
            echo "File: " . $fullPath . "\n";
        }
    }
}

このコードでは、指定されたディレクトリ内のすべてのファイルやサブディレクトリを再帰的に探索し、それぞれの名前を表示します。scandir関数を使ってディレクトリ内のすべてのファイルやフォルダを取得し、is_dir関数を用いてディレクトリかファイルかを判別しています。

`scandir`関数を用いたファイル取得

PHPでディレクトリの内容を取得するためには、scandir関数が非常に便利です。scandir関数は指定したディレクトリ内のすべてのファイルとフォルダを配列として返し、再帰処理と組み合わせることでディレクトリ内の全てのファイルを効率的に探索できます。使用方法は非常にシンプルで、ディレクトリパスを引数として渡すだけです。

`scandir`の基本的な使い方

scandir関数は以下のように使用します。

$files = scandir('/path/to/directory');

このコードは、指定されたディレクトリ内のすべてのファイルとディレクトリを配列として返します。返された配列には、カレントディレクトリ(.)と親ディレクトリ(..)も含まれるため、これらを除外する必要があります。

カレントディレクトリと親ディレクトリを除外

取得した配列からカレントディレクトリと親ディレクトリを除外するためには、if文を使用します。

foreach ($files as $file) {
    if ($file === '.' || $file === '..') continue;
    // その他のファイルやディレクトリの処理
}

このコードにより、無駄なディレクトリの参照を避け、実際のファイルやサブディレクトリの処理を行うことができます。scandirは、簡単なディレクトリのループ処理を行う上で便利な関数であり、再帰的な処理との相性も抜群です。

再帰処理によるサブディレクトリの探索

ディレクトリ内にサブディレクトリが存在する場合、再帰処理を用いてそのサブディレクトリも探索することができます。PHPのscandir関数を使ってディレクトリ内のすべての項目を取得し、is_dir関数を使用してサブディレクトリかどうかを判別することで、再帰的にディレクトリ内を探索できます。

サブディレクトリの判別と再帰呼び出し

再帰的にディレクトリを探索する際、is_dirを使ってディレクトリを判別し、もしサブディレクトリであれば再度同じ関数を呼び出すことでサブディレクトリ内も探索するようにします。以下のコードはその例です。

function recursiveDirectoryLoop($dir) {
    // ディレクトリ内の項目を取得
    $files = scandir($dir);

    foreach ($files as $file) {
        // カレントディレクトリと親ディレクトリを除外
        if ($file === '.' || $file === '..') continue;

        // フルパスを生成
        $fullPath = $dir . DIRECTORY_SEPARATOR . $file;

        // ディレクトリであれば再帰的に処理
        if (is_dir($fullPath)) {
            echo "Exploring directory: " . $fullPath . "\n";
            recursiveDirectoryLoop($fullPath);  // 再帰呼び出し
        } else {
            echo "File found: " . $fullPath . "\n";
        }
    }
}

このコードは、ディレクトリ内のすべての項目を探索し、もしディレクトリであれば、その中にさらに再帰的に潜り込んでいきます。この方法で、階層構造がいくつもあるディレクトリでも完全に探索することが可能です。

再帰処理の実行例

例えば、以下のようなディレクトリ構造を持つ場合を考えてみます。

/my-directory
  ├── file1.txt
  ├── file2.txt
  └── sub-directory
      ├── file3.txt
      └── file4.txt

この構造に対して上記の再帰関数を実行すると、以下のような出力が得られます。

File found: /my-directory/file1.txt
File found: /my-directory/file2.txt
Exploring directory: /my-directory/sub-directory
File found: /my-directory/sub-directory/file3.txt
File found: /my-directory/sub-directory/file4.txt

このように、再帰処理を用いることで階層構造を持つディレクトリでもサブディレクトリ内のファイルをすべて探索できるようになります。

ファイルフィルタリングと特定の拡張子の処理

再帰処理でディレクトリ内のファイルを探索する際、すべてのファイルを処理するのではなく、特定の拡張子を持つファイルのみを対象にすることがよくあります。たとえば、テキストファイル(.txt)や画像ファイル(.jpg)のみを処理したい場合、ファイルの拡張子を確認してフィルタリングする必要があります。

拡張子の判別方法

PHPでは、pathinfo関数を使ってファイルパスの情報を解析し、拡張子を取得することができます。pathinfoは配列としてパス情報を返し、その中のextensionキーに拡張子が含まれます。

$fileInfo = pathinfo($filePath);
$extension = $fileInfo['extension'];

これを使って特定の拡張子のファイルだけを処理することができます。以下に、特定の拡張子(例:.txt)を持つファイルのみを処理する例を示します。

特定の拡張子のファイルを処理する例

function recursiveDirectoryLoop($dir) {
    // ディレクトリの内容を取得
    $files = scandir($dir);

    foreach ($files as $file) {
        // カレントディレクトリと親ディレクトリを除外
        if ($file === '.' || $file === '..') continue;

        // フルパスを生成
        $fullPath = $dir . DIRECTORY_SEPARATOR . $file;

        // ディレクトリの場合、再帰的に処理
        if (is_dir($fullPath)) {
            recursiveDirectoryLoop($fullPath);
        } else {
            // ファイルの拡張子を取得
            $fileInfo = pathinfo($fullPath);
            if (isset($fileInfo['extension']) && $fileInfo['extension'] === 'txt') {
                echo "Text file found: " . $fullPath . "\n";
            }
        }
    }
}

このコードでは、pathinfo関数を使ってファイルの拡張子を取得し、'txt'拡張子のファイルだけを処理しています。これにより、無関係なファイルをスキップし、必要なファイルだけに対して処理を行うことができます。

複数の拡張子を処理する方法

もし複数の拡張子を処理したい場合は、配列で指定して判定することができます。

$allowedExtensions = ['txt', 'jpg', 'png'];

if (isset($fileInfo['extension']) && in_array($fileInfo['extension'], $allowedExtensions)) {
    echo "Allowed file found: " . $fullPath . "\n";
}

この方法で、テキストファイルや画像ファイルなど、複数の拡張子を持つファイルをフィルタリングしつつ再帰的に処理することができます。ファイルの種類に応じてフィルタリングを行うことで、効率的かつ柔軟にファイルシステムを探索することができます。

再帰的処理のパフォーマンス最適化

再帰処理を用いてディレクトリを探索する場合、大量のファイルや複雑なディレクトリ構造を扱うとパフォーマンスの低下が問題になることがあります。特に、数千から数万ファイルを再帰的に処理する場合、効率的な処理方法や最適化が求められます。ここでは、再帰処理のパフォーマンスを向上させるためのいくつかの方法を紹介します。

メモリ使用量の最適化

再帰処理は大量の関数コールを伴うため、メモリ使用量が増加しがちです。PHPのデフォルト設定では、スタックが深くなりすぎるとメモリ不足に陥る可能性があります。そのため、再帰の深さを抑える工夫や、スタックオーバーフローを防ぐための工夫が必要です。

  • 再帰の深さを制限する:無限に再帰しないように、最大の再帰深度を設定することが有効です。
  • PHPのメモリ制限を確認するmemory_limitの設定を確認し、必要に応じて適切に増やすことも考慮してください。

遅延読み込みを利用する

scandir関数は一度にディレクトリ内のすべてのファイルとフォルダを取得しますが、大量のファイルを一度にメモリに読み込むのは非効率です。これを改善するために、ファイルやディレクトリを必要な時に読み込む「遅延読み込み」を活用する方法があります。DirectoryIteratorRecursiveDirectoryIteratorクラスを利用すると、遅延的にファイルを処理することができます。

$iterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($dir),
    RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
    if ($file->isDir()) {
        echo "Directory: " . $file->getPathname() . "\n";
    } else {
        echo "File: " . $file->getPathname() . "\n";
    }
}

この方法では、ファイルが必要になる時点でのみメモリに読み込まれるため、メモリ効率が向上します。

ファイルシステムアクセスの最適化

ファイルシステムへのアクセスは処理時間の大部分を占めることがあります。特にネットワークドライブやリモートサーバーのディレクトリを探索する際には、遅延が顕著です。この問題を軽減するためには以下の点を考慮できます。

  • キャッシュの活用:一度読み込んだディレクトリやファイル情報をキャッシュすることで、再度同じ場所にアクセスする際の処理時間を削減できます。
  • 非同期処理の導入:並列処理や非同期処理を使うことで、複数のディレクトリを同時に処理し、処理速度を向上させることが可能です。PHPでは非同期処理を扱うために、pcntl_fork()ReactPHPなどのライブラリが使えます。

不必要なファイルの除外

ディレクトリ内の全ファイルを再帰的に処理する場合でも、特定の拡張子やファイル名を事前に除外することがパフォーマンス改善に寄与します。これにより、無駄なファイルアクセスを避け、処理の負荷を軽減できます。

$excludedExtensions = ['tmp', 'log'];
$fileInfo = pathinfo($fullPath);
if (in_array($fileInfo['extension'], $excludedExtensions)) {
    continue;
}

このコードを導入することで、不要なファイルを効率的にスキップし、全体的なパフォーマンスを向上させることが可能です。

パフォーマンス向上の総括

再帰的なディレクトリ探索では、特に大量のファイルを扱う場合、メモリとファイルシステムアクセスの最適化が不可欠です。遅延読み込みやキャッシュの活用、ファイルのフィルタリングを行うことで、処理速度を大幅に向上させることができます。適切な方法で再帰処理を最適化することは、アプリケーション全体のパフォーマンスにも大きく影響します。

エラー処理と例外のキャッチ

再帰処理でディレクトリを探索する際には、さまざまなエラーが発生する可能性があります。存在しないディレクトリへのアクセス、アクセス権限の不足、ファイルシステムの不具合など、さまざまな状況に対処するためには、適切なエラー処理と例外のキャッチが必要です。ここでは、再帰処理で考慮すべきエラー処理の方法と、PHPの例外処理を利用した方法について解説します。

エラー処理の基本

ディレクトリを探索する際、アクセスしようとしているディレクトリやファイルが存在しない場合や、アクセス権限がない場合にはエラーが発生します。これらのエラーを無視すると、プログラムが途中で停止してしまいます。そのため、まずはis_dir()file_exists()を使用して、ディレクトリやファイルが存在するかどうかを確認することが重要です。

if (!is_dir($dir)) {
    echo "Error: Directory does not exist: $dir\n";
    return;
}

このように、ディレクトリが存在しない場合はエラーメッセージを表示して、処理を中断させることができます。

例外処理を使ったエラー管理

PHPでは、エラーを例外(Exception)として扱うことができ、try-catchブロックを使って処理を安全に行うことができます。例外を使用することで、エラーが発生してもプログラム全体が停止することなく、例外をキャッチして適切に対応できます。

function recursiveDirectoryLoop($dir) {
    try {
        // ディレクトリが存在するかをチェック
        if (!is_dir($dir)) {
            throw new Exception("Directory does not exist: $dir");
        }

        // ディレクトリの内容を取得
        $files = scandir($dir);

        foreach ($files as $file) {
            if ($file === '.' || $file === '..') continue;

            $fullPath = $dir . DIRECTORY_SEPARATOR . $file;

            if (is_dir($fullPath)) {
                recursiveDirectoryLoop($fullPath);  // 再帰的に呼び出す
            } else {
                echo "File found: " . $fullPath . "\n";
            }
        }
    } catch (Exception $e) {
        echo "Error: " . $e->getMessage() . "\n";
    }
}

このコードでは、ディレクトリが存在しない場合に例外が発生し、catchブロックでエラーメッセージが表示されます。これにより、処理全体が停止することなく、エラーを安全に処理できます。

アクセス権限エラーの処理

ディレクトリやファイルにアクセスする際、アクセス権限の問題が発生することがあります。特定のディレクトリへの読み取り権限がない場合は、警告やエラーメッセージを表示して次のディレクトリやファイルに処理を進めるようにすることが一般的です。is_readable()関数を使用して、ファイルやディレクトリが読み取り可能かどうかを事前に確認することができます。

if (!is_readable($dir)) {
    echo "Error: Cannot read directory: $dir\n";
    return;
}

このコードにより、アクセスできないディレクトリに対してはエラーメッセージを出力し、スムーズに次の処理へ移行できます。

エラーログの活用

エラーが発生した場合、画面にメッセージを表示するだけでなく、エラーログに記録することも重要です。これにより、ユーザーには表示されないバックエンドの問題も追跡でき、デバッグや後続のメンテナンスに役立ちます。PHPのerror_log()関数を使用すると、エラーメッセージをファイルに保存することができます。

error_log("Error accessing directory: $dir");

これを使うことで、問題が発生した時に詳細な情報を後から確認することができ、システムの信頼性を向上させることができます。

エラー処理と例外の重要性

再帰的にディレクトリを探索する際には、様々なエラーが発生する可能性があります。適切なエラー処理や例外のキャッチを行うことで、システムの安定性を高め、予期せぬエラーによる処理の中断を防ぐことができます。エラーが発生しても、例外処理やログ出力を通じて効率的に対処することで、再帰処理がスムーズに動作するように設計することが重要です。

実際のコード例と解説

ここでは、PHPを使ってディレクトリ内のファイルとサブディレクトリを再帰的に探索する具体的なコード例を紹介し、各部分について詳しく解説します。この例では、ディレクトリ内のすべてのファイルを再帰的に処理し、ファイルとディレクトリのパスを出力します。また、アクセス権やファイル拡張子のチェックも行い、処理の効率化を図っています。

再帰的ディレクトリ探索の全体コード例

function recursiveDirectoryLoop($dir) {
    // ディレクトリの存在確認
    if (!is_dir($dir)) {
        echo "Error: Directory does not exist: $dir\n";
        return;
    }

    // ディレクトリが読み取り可能か確認
    if (!is_readable($dir)) {
        echo "Error: Cannot read directory: $dir\n";
        return;
    }

    // ディレクトリ内の項目を取得
    $files = scandir($dir);

    foreach ($files as $file) {
        // カレントディレクトリと親ディレクトリをスキップ
        if ($file === '.' || $file === '..') continue;

        // フルパスを作成
        $fullPath = $dir . DIRECTORY_SEPARATOR . $file;

        // ディレクトリの場合、再帰的に処理
        if (is_dir($fullPath)) {
            echo "Directory: " . $fullPath . "\n";
            recursiveDirectoryLoop($fullPath);  // 再帰呼び出し
        } else {
            // 拡張子のチェック(例: .txtファイルのみ処理)
            $fileInfo = pathinfo($fullPath);
            if (isset($fileInfo['extension']) && $fileInfo['extension'] === 'txt') {
                echo "Text file: " . $fullPath . "\n";
            } else {
                echo "Other file: " . $fullPath . "\n";
            }
        }
    }
}

コードの解説

  1. ディレクトリの存在確認
  • is_dir()関数を使って、指定されたパスがディレクトリかどうかを確認しています。ディレクトリが存在しない場合はエラーメッセージを出力し、処理を中断します。
   if (!is_dir($dir)) {
       echo "Error: Directory does not exist: $dir\n";
       return;
   }
  1. ディレクトリの読み取り権限確認
  • is_readable()関数を使い、ディレクトリが読み取り可能かどうかを確認します。読み取り権限がない場合はエラーメッセージを出力し、他のディレクトリやファイルの処理に進むようにします。
   if (!is_readable($dir)) {
       echo "Error: Cannot read directory: $dir\n";
       return;
   }
  1. scandirによるディレクトリ内の項目取得
  • scandir()関数を使ってディレクトリ内のすべてのファイルやサブディレクトリを取得します。結果は配列として返されます。
   $files = scandir($dir);
  1. カレントディレクトリと親ディレクトリのスキップ
  • .および..はそれぞれカレントディレクトリと親ディレクトリを表しているため、これらをスキップして処理の無駄を避けます。
   if ($file === '.' || $file === '..') continue;
  1. 再帰的処理の実行
  • is_dir()を使用して、取得した項目がディレクトリかどうかを確認します。ディレクトリであれば、再帰的に同じ関数を呼び出して、その中に含まれる項目を処理します。
   if (is_dir($fullPath)) {
       echo "Directory: " . $fullPath . "\n";
       recursiveDirectoryLoop($fullPath);  // 再帰呼び出し
   }
  1. ファイルの拡張子チェック
  • pathinfo()関数を使ってファイルの拡張子を取得し、ここでは.txtファイルだけを処理しています。他のファイルについては、拡張子を確認した上で出力しています。
   $fileInfo = pathinfo($fullPath);
   if (isset($fileInfo['extension']) && $fileInfo['extension'] === 'txt') {
       echo "Text file: " . $fullPath . "\n";
   } else {
       echo "Other file: " . $fullPath . "\n";
   }

実行結果例

たとえば、次のようなディレクトリ構造がある場合:

/my-directory
  ├── file1.txt
  ├── file2.log
  └── sub-directory
      ├── file3.txt
      └── image.jpg

このコードを実行すると、以下のような出力が得られます。

Text file: /my-directory/file1.txt
Other file: /my-directory/file2.log
Directory: /my-directory/sub-directory
Text file: /my-directory/sub-directory/file3.txt
Other file: /my-directory/sub-directory/image.jpg

このように、再帰的にディレクトリとその中にあるファイルを探索し、特定の条件に基づいてファイルを処理することが可能です。このコードは、さまざまな状況に応じて柔軟に再利用することができます。

応用: ディレクトリ内ファイルサイズの合計を計算

再帰処理を用いたディレクトリ探索の応用例として、ディレクトリ内のすべてのファイルのサイズを再帰的に合計する方法を紹介します。この機能は、ディスク使用量の分析やディレクトリ全体の容量を計測する際に非常に便利です。以下では、再帰処理を使用してサブディレクトリ内のファイルサイズも含めた合計を計算するコードを解説します。

ファイルサイズの取得

PHPでは、filesize()関数を使用して、指定したファイルのサイズ(バイト単位)を取得することができます。この関数を使って、再帰的にディレクトリ内のすべてのファイルサイズを合計します。

再帰処理によるファイルサイズの合計コード

function getDirectorySize($dir) {
    $totalSize = 0;

    // ディレクトリの存在と読み取り権限を確認
    if (!is_dir($dir) || !is_readable($dir)) {
        echo "Error: Cannot access directory: $dir\n";
        return 0;
    }

    // ディレクトリ内の項目を取得
    $files = scandir($dir);

    foreach ($files as $file) {
        // カレントディレクトリと親ディレクトリをスキップ
        if ($file === '.' || $file === '..') continue;

        // フルパスを作成
        $fullPath = $dir . DIRECTORY_SEPARATOR . $file;

        // ディレクトリの場合、再帰的にサイズを取得
        if (is_dir($fullPath)) {
            $totalSize += getDirectorySize($fullPath);  // 再帰呼び出し
        } else {
            // ファイルサイズを取得し合計に加算
            $totalSize += filesize($fullPath);
        }
    }

    return $totalSize;
}

このコードでは、ディレクトリ内のすべてのファイルサイズを合計し、その結果を返します。ディレクトリが存在しない場合や、読み取り権限がない場合はエラーが表示されます。scandirで取得した各項目がディレクトリかファイルかをチェックし、ファイルの場合はfilesize()関数でそのサイズを取得し、合計に加算します。ディレクトリの場合は再帰的に処理します。

実行例

たとえば、次のようなディレクトリ構造を持つ場合:

/my-directory
  ├── file1.txt (2KB)
  ├── file2.log (3KB)
  └── sub-directory
      ├── file3.txt (4KB)
      └── image.jpg (5KB)

このコードを実行すると、以下のような出力が得られます。

$totalSize = getDirectorySize('/my-directory');
echo "Total size: " . $totalSize . " bytes\n";

出力結果:

Total size: 14000 bytes

このように、ディレクトリ内のすべてのファイルのサイズを再帰的に合計して出力することができます。

人間にわかりやすいサイズ表示

バイト単位の出力は直感的でない場合があるため、人間にわかりやすい単位(KB、MB、GB)で表示するための関数を追加できます。

function formatSize($size) {
    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $unitIndex = 0;

    while ($size >= 1024 && $unitIndex < count($units) - 1) {
        $size /= 1024;
        $unitIndex++;
    }

    return round($size, 2) . ' ' . $units[$unitIndex];
}

// 合計サイズを人間にわかりやすい形式で表示
echo "Total size: " . formatSize($totalSize) . "\n";

出力例:

Total size: 13.67 KB

このように、再帰的にディレクトリ内のすべてのファイルサイズを合計し、人間にわかりやすい形で表示することができます。この応用例は、ディスク使用量のレポートやデータ管理ツールなど、多くの場面で活用できるでしょう。

ユニットテストの導入

再帰的ディレクトリ探索の機能を実装した後は、その機能が期待通りに動作するかどうかを確認するために、ユニットテストを導入することが重要です。特に再帰処理を含むコードは、深いディレクトリ構造やエラーケースを含む複雑な状況に対応するため、しっかりとテストする必要があります。PHPでは、ユニットテストのためにPHPUnitというライブラリを使用するのが一般的です。

PHPUnitのセットアップ

PHPUnitを使うためには、Composerを利用してインストールします。次のコマンドでプロジェクトにPHPUnitを追加できます。

composer require --dev phpunit/phpunit

インストールが完了したら、テストケースを作成して再帰的なディレクトリ探索機能の検証を行います。

ユニットテストの作成

次に、ディレクトリ探索機能のユニットテストを作成します。テスト対象は、再帰処理を通じて正しいディレクトリとファイルが処理されるか、エラーが適切にキャッチされるか、ファイルサイズが正確に計算されるか、といった点です。

use PHPUnit\Framework\TestCase;

class DirectoryTest extends TestCase {

    // テスト用のディレクトリ構造を作成
    protected function setUp(): void {
        mkdir('test-dir');
        file_put_contents('test-dir/file1.txt', str_repeat('A', 1024)); // 1KBファイル
        mkdir('test-dir/sub-dir');
        file_put_contents('test-dir/sub-dir/file2.txt', str_repeat('B', 2048)); // 2KBファイル
    }

    // テスト後にディレクトリ構造を削除
    protected function tearDown(): void {
        unlink('test-dir/sub-dir/file2.txt');
        rmdir('test-dir/sub-dir');
        unlink('test-dir/file1.txt');
        rmdir('test-dir');
    }

    // 再帰的ディレクトリ探索のテスト
    public function testDirectoryLoop() {
        $totalSize = getDirectorySize('test-dir');
        $this->assertEquals(3072, $totalSize, "The total size should be 3KB");
    }

    // 存在しないディレクトリのテスト
    public function testNonExistentDirectory() {
        $totalSize = getDirectorySize('non-existent-dir');
        $this->assertEquals(0, $totalSize, "The size should be 0 for non-existent directory");
    }

    // アクセス不可のディレクトリのテスト
    public function testUnreadableDirectory() {
        mkdir('unreadable-dir', 0000);  // 読み取り不可ディレクトリを作成
        $totalSize = getDirectorySize('unreadable-dir');
        $this->assertEquals(0, $totalSize, "The size should be 0 for unreadable directory");
        rmdir('unreadable-dir');
    }
}

テストケースの説明

  1. セットアップとクリーンアップ
  • setUp()メソッドで、テスト用のディレクトリ構造を動的に作成しています。テストが終了した後、tearDown()メソッドで作成したファイルとディレクトリを削除し、テスト環境を初期状態に戻します。
  1. 再帰的ディレクトリ探索のテスト
  • testDirectoryLoop()では、getDirectorySize()関数が正しくファイルサイズを計算するかを検証しています。ここでは、ディレクトリ内に作成したファイルの合計サイズ(3KB)が正しく計算されるかどうかを確認します。
  1. 存在しないディレクトリのテスト
  • testNonExistentDirectory()では、存在しないディレクトリを引数に渡した場合、getDirectorySize()が正しく0を返すかどうかをテストします。エラーハンドリングが正常に行われていることを確認します。
  1. アクセス不可のディレクトリのテスト
  • testUnreadableDirectory()では、読み取り権限がないディレクトリに対する処理をテストします。読み取り不可のディレクトリの場合、0が返されるかどうかを確認します。テスト後はディレクトリを削除してクリーンアップします。

テストの実行

テストは次のコマンドで実行できます。

vendor/bin/phpunit DirectoryTest.php

実行すると、すべてのテストが通過したかどうかが確認できます。エラーがあった場合は、どのテストが失敗したかが詳細に表示されます。

ユニットテストの重要性

再帰的なディレクトリ探索機能は、多くのファイルやディレクトリを扱うため、さまざまなエッジケース(存在しないディレクトリ、アクセス権限の問題、空のディレクトリなど)に対応する必要があります。ユニットテストを実装することで、これらのエッジケースに対してコードが正しく動作するかどうかを確認し、バグや問題を未然に防ぐことができます。

まとめ

本記事では、PHPでの再帰的ディレクトリ探索の実装方法について解説しました。scandir関数を用いた基本的なディレクトリ探索から、ファイルサイズの合計を計算する応用例、そしてエラー処理やユニットテストの導入まで、再帰処理に関する幅広い知識を身につけることができたと思います。これにより、大規模なファイルシステムを効率的に管理・処理するための基礎を習得できたはずです。

コメント

コメントする

目次