発生する条件
php.iniで以下のように設定されている時に、multipart/form-dataでUTF-8の内容をPOSTすると文字化けすることがある。(application/x-www-form-urlencodedでは文字化けしない)
mbstring.encoding_translation = On mbstring.http_input = auto mbstring.internal_encoding = UTF-8
どんな流れで発生しているか?
PHPではPOST時のハンドラをMIMEタイプ毎に登録する仕組みになっているようで、mbstringでは以下の場所でapplication/x-www-form-urlencodedとmultipart/form-dataで別々のハンドラが登録されている。
https://github.com/php/php-src/blob/master/ext/mbstring/mbstring.c#L680-L681
application/x-www-form-urlencodedの場合はmbstring内でリクエストのボディ全体から文字コードを判定するため、期待した通りUTF-8として扱われる
multipart/form-dataの場合は以下の場所で文字コードの判別が行われるが、
https://github.com/php/php-src/blob/master/main/rfc1867.c#L427-L429
判別対象のデータが例えば以下のようなヘッダー行なので、
Content-Disposition: form-data; name="name"
「mbstring.http_input = auto」 => 「ASCII,UTF-8」だとASCIIだと判定されてしまい。その下に続く内容もASCIIとして「ASCII -> UTF-8」で変換が行われてしまう。
回避方法
以下のような方法がある。
- 「mbstring.http_input = UTF-8」にする
- フォーム要素に「multipart/form-data」をつけない
- 全ての項目のname属性にマルチバイトの文字を入れる
以下のようなフォームであれば文字化けしない。
<form enctype="multipart/form-data" method="POST"> <input name="📛" value="天野" /> </form>