PHPのZipArchive::numFiles()とファイル数が合わない原因

Code Html Computer Internet It  - RoonZ-nl / Pixabay プログラミング
RoonZ-nl / Pixabay
スポンサーリンク

思いがけず発生して時間を取られたので、備忘録。

解凍されたファイル数とZipArchive::numFiles()が一致しない

1つのファイルを圧縮したzipファイルに対し、PHPでZipArchive::numFiles()を実行すると、1ではなく2が返される場合があります。

以下のようにすれば、zipファイル内の各エントリ名を確認できるので原因がわかります。

$zip = new ZipArchive();
if ($zip->open('hoge.zip') === TRUE) {
    echo "Total entries: " . $zip->numFiles . "\n";
    for ($i = 0; $i < $zip->numFiles; $i++) {
        echo "[$i] " . $zip->getNameIndex($i) . "\n";
    }
    $zip->close();
} else {
    echo "Failed to open zip file.";
}

1つのファイルなのに2になってしまう原因は、以下の2つがあります。

1. ディレクトリが含まれている

archive.zip
└── folder/
    └── data.csv

この場合、ディレクトリも1とカウントするので2になります。

ディレクトリが含まれる場合は除外し、ファイルだけを対象にするには以下のようにすれば良いです。

for ($i = 0; $i < $zip->numFiles; $i++) {
    $name = $zip->getNameIndex($i);
    if (substr($name, -1) !== '/') { // ディレクトリでない(スラッシュで終わっていない)
        echo "file: $name\n";
    }
}

ディレクトリエントリが入る理由

  • 多くの圧縮ソフト(macOS標準、Windowsエクスプローラー、zip コマンドなど)は、フォルダ構造をそのままzipに含めるため、ディレクトリもエントリとして登録される
  • ZipArchive::numFiles() は「すべてのエントリ数」を返すため、「ファイル」以外も含まれる

2. __MACOSX/._ファイル が含まれている

macOSではターミナルやFinderなどから『”hoge.txt”を圧縮』してzip圧縮ファイルを作成できますが、このとき自動的にApple独自の拡張情報である『__MACOSX/._hoge.txt』が生成されて圧縮ファイルに追加されます。

このため、ZipArchive::numFiles() のカウントも増えます。

__MACOSX/._ファイルとは?

  • AppleDouble 形式と呼ばれる、macOS独自のメタデータ保存用ファイル
  • 通常ファイルと対になる .DS_Store._xxxx 形式のファイルは、ファイルのFinderタグやアイコン位置などを保存している
  • Macでは見えなくても、他のOSやZipArchiveなどでは「普通のファイル」として扱われる

__MACOSX ディレクトリおよび . 系ファイルが含まれている場合に除外するには、以下のようにすれば良いです。

for ($i = 0; $i < $zip->numFiles; $i++) {
    $name = $zip->getNameIndex($i);
    
    // __MACOSX ディレクトリおよび ._ 系ファイルを除外
    if (strpos($name, '__MACOSX/') === 0 || strpos(basename($name), '._') === 0) {
        continue;
    }

    echo "Valid file: $name\n";
}

zip作成するときApple拡張を生成しないようにする

macOSでzipを作成するとき、Finderではなくターミナルのzipコマンドを使い、Apple拡張を無効にする『-X』オプションを指定すれば良いです。

zip -r -X archive.zip hoge.txt

-X オプションは “exclude extra file attributes” の意味で、これにより __MACOSX._ ファイルの生成を抑止します。

まとめ

zipファイルにディレクトリやmacOSで生成されるApple拡張が含まれる場合、PHPのZipArchive::numFiles()とファイル数が一致しません。

あまり遭遇しないとは思いますが、何かの参考になれば嬉しいです。

PHPの勉強には、以下の書籍がとても参考になります。

テキストよりも動画で集中して学びたい方はこちら。

Udemyホームページ

タイトルとURLをコピーしました