日本語圏のZIPファイルは格納された日本語ファイル名がCP932(Shift_JIS)であることは有名だが、最新の(恐らくPHP7.0.8以降の?)PHPでは単にgetNameIndexしたファイル名をmb_convert_encodingしてもファイル名は壊れる。
どうやらZipArchiveがgetNameIndexする際に、勝手に文字コードを判定してUTF-8かCP437(なんで?)と見なして変換を掛けてしまうようで、CP932の生値が取れない。
この場合、CP932の生値が欲しい時はgetNameIndexにZipArchive::FL_ENC_RAWを付けてやると文字コード判定を無効にできる。
つまり
$name = $zip->getNameIndex($idx, ZipArchive::FL_ENC_RAW);
$name = mb_convert_encoding($name, 'UTF-8', 'CP932');
こうすることで正しいファイル名が取れる。
公式ドキュメントにはgetNameIndexのオプションがZipArchive::FL_UNCHANGEDしか書かれておらず、ろくなコメントもなく、そもそもZipArchive::FL_ENC_RAWオプションに言及しているページがググってもほとんど存在せず、非常にはまった。
書き込む時は特にオプションは不要で、ファイル名をCP932にmb_convert_encodingすれば良い。こちらは特に変換などは噛んでいないようだ。
$name = mb_convert_encoding($name, 'CP932', 'UTF-8');
$zip->addFromString($name, $content);
ほんまお前PHP