1. 導入
Webシステム開発において、ユーザーが作成したデータやログファイルを一括ダウンロードさせるために、ディレクトリ構造を維持したままZip圧縮する機能は非常に需要があります。しかし、PHP標準のZipArchiveクラスは単一ファイルの追加には適していますが、ディレクトリ内のサブディレクトリまで含めた「再帰的な圧縮」を正しく実装するには、イテレータを用いた適切なディレクトリ走査が不可欠です。本記事では、実務でも即戦力となる再帰的な圧縮ロジックを解説します。
2. 基礎知識
ディレクトリ圧縮を行う上で重要なのが「RecursiveIteratorIterator」です。これは、複雑なファイルシステムを再帰的にトラバース(巡回)するための強力なクラスです。
ZipArchive::addFile を使用する際、第2引数に「Zipファイル内でのパス」を指定する必要があります。このパスを計算する際、元のディレクトリ構造の基準点(ベースパス)を正しく算出することが、構造を崩さないための鍵となります。
3. 実装/解決策
実装のポイントは以下の通りです。
・FilesystemIterator::SKIP_DOTS を指定して、カレントディレクトリ(.)や親ディレクトリ(..)を走査対象から除外する。
・RecursiveIteratorIterator::SELF_FIRST を指定することで、ディレクトリを処理してからその配下のファイルを処理する順序を保証する。
・各ファイル・ディレクトリの相対パスを算出し、Zipアーカイブ内にマッピングする。
4. サンプルプログラム
以下のコードは、指定したディレクトリを構造を維持したままZipファイルとして出力する関数です。
/
function zipDirectory(string $sourceDir, string $zipFilePath, string $rootName = “”) {
$zip = new ZipArchive();
// ZipArchive::OVERWRITEを指定すると既存のファイルを上書きします
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
return false;
}
// ディレクトリ走査用イテレータの作成
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($sourceDir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
$sourceDir = realpath($sourceDir);
$baseLen = strlen($sourceDir);
foreach ($iterator as $file) {
$filePath = $file->getRealPath();
// Zip内のパスを作成
$relativePath = ($rootName !== “” ? $rootName . DIRECTORY_SEPARATOR : “”) . substr($filePath, $baseLen + 1);
if ($file->isDir()) {
// ディレクトリの場合は空ディレクトリを追加
$zip->addEmptyDir($relativePath);
} else {
// ファイルの場合はアーカイブに追加
$zip->addFile($filePath, $relativePath);
}
}
return $zip->close();
}
// 使用例
zipDirectory(__DIR__ . ‘/data’, ‘./backup.zip’, ‘my_backup_folder’);
?>
5. 応用・注意点
実務で運用する際は、以下の点に注意してください。
・メモリ制限と実行時間: 圧縮対象が巨大な場合、PHPのメモリ上限(memory_limit)や実行時間制限(max_execution_time)に達する可能性があります。その場合は、一度に全て処理せず、ジョブキューなどを用いた非同期処理を検討してください。
・権限管理: 圧縮対象のディレクトリに対する読み取り権限(read permission)がない場合、ZipArchiveがエラーを返します。事前に is_readable() 等でチェックを行うのが安全です。
・文字化け対策: Windows環境とLinux環境でファイル名が混在する場合、ファイル名のエンコーディング(UTF-8/CP932)問題が発生することがあります。必要に応じてファイル名を変換する処理を挟んでください。