CakePHPのUploadPackというアップロード用プラグインを使っている時、MacのFirefoxからアップロードされたファイルのファイル名に「ば」のような濁点がついている場合、文字化けする問題がありました。
Macだとファイル名がUTF-8で扱われているのですが、NFDと呼ばれる「は」と「゛」が分離して表現される表記法で扱われているのだそうです。
紹介マニアどらふと版: Mac OS X におけるファイル名に関するメモ(NFC, NFD等)
普通に「ば」を一文字で表現する表現方法はNFCと呼ばれ、WindowsやLinuxでは通常こちらで扱われます。
UploadPackでファイル名を処理するとき、Inflector::slug というメソッドでサニタイズされるのですが、「゛」を記号として「_」などの安全な文字に変換してしまっているため、この問題が起きていたのでした。
最初、この現象が再現できなかったのですが、実は現行のSafariやChromeでは起こらず、Firefox(30)のみで起きました。
たぶんSafariとChromeではファイルのアップロード時にNFCに変換してから送っているのではないか、と思います。
このNFD→NFCへの変換は、PHPではNormalizerクラスを利用することで行うことが出来ます。
<?php $nfc = Normalizer::normalize($nfd, Normalizer::FORM_C);
また、NormalizerクラスはPHP5.3以降は標準で入っているintl extensionを使えるようにしておく必要があります。
Windowsのxamppではデフォルトでphp_intl.dllはオフになっているので、php.iniを修正して再起動します。
Installing intl extension in XAMPP | Cyn Wong
php.ini
extension=php_intl.dll
UploadPackの修正は、以下のようにInflector::slugへ渡す前にNormalizer::normalizeを掛けるようにします。
UploadPack/Model/Behavior/UploadBehavior.php 123行目あたり
- Inflector::slug(substr($this->toWrite[$field]['name'], 0, strrpos($this->toWrite[$field]['name'], '.'))). // filename + Inflector::slug(Normalizer::normalize(substr($this->toWrite[$field]['name'], 0, strrpos($this->toWrite[$field]['name'], '.')), Normalizer::FORM_C)). // filename