PHP 8.1以降のmb_convert_encodingなどでSJISに変換するとバグる文字がある
TOP > てきとうにこらむ > ゲーム作りとプログラミング日記 > PHP 8.1以降のmb_convert_encodingなどでSJISに変換するとバグる文字がある
PHP 8.1以降のmb_convert_encodingなどでSJISに変換するとバグる文字がある
バグ報です。mb_convert_encoding
などを使ってSJISへ変換しようとする際に、チルダとバックスラッシュを変換しようとすると全角へと変換されます。PHP 8.1以降で再現します。php-srcには報告済みです。
mb_convert_encoding("~", "SJIS", "UTF-8");
https://github.com/php/php-src/issues/8281 このチケットがclosedされたバージョンが公開されたらこの記事のとおりにならなくなるかもしれないことに注意してください(推奨される対策は有効だと思う)。
推奨される対策
じゃあどうすればいいの?から見たい方のために先に対策を述べておくと、「SJIS」はもう使わずに「CP932」にしておきましょう(SJIS-winでもいいのですが、CP932のエイリアスという位置づけになってしまっているためCP932が正式名称となっています)。
mb_convert_encoding("~", "CP932", "UTF-8");
どうしてこうなったのか
php-srcでは現在、major overhaul of mbstringという、mbstringのextension、特に内蔵しているlibmbflに対して大規模な改修を行っています。時代遅れなコードだったり、テクニックの大半が実は意味のないものということが判明してのことと認識しておりますが、その中のpart 2にてこの変更が入っていたようです。
Major overhaul of mbstring (part 2)
該当のコミットを読むとちょうどバックスラッシュの0x5Cとチルダの0x7Eにこんなメッセージが入っており、たしかにそのような変更が加わっています。
Convert Shift-JIS 0x7E to Unicode 0x203E (overline) as recommended by the Unicode Consortium, and as iconv does.
Convert Unicode 0x5C (backslash) to Shift-JIS 0x815F (reverse solidus).
ここで全角に変換されてしまうということになってしまったんですね。
どうしてこんなものを見つけたのか
単に興味本位です。本番環境とかじゃなくて良かったです。なんか、Major overhaul of mbstringの重複したコードを削除している部分を見て「これって大丈夫かなあ…」って思って調べたら見つかりました。変な匂いがしたんですかね。
major overhaul of mbstringの重複したコードが削除されて同じ文字エンコーディングにされてるのを見て、本当にこれは大丈夫だろうか…って思いながら確かに重複コードだなって見てる
— てきめん (@youkidearitai) March 30, 2022
PHP8.1でこの重複が消えてるが果たしてって感じ(多分大丈夫というかまさかあるはずないだろって感じだが)
バグ報告4つ目になりました
これほど「ヒッ」って思うようなヒヤヒヤするやつは1つ目と同じくらいか、それ以上でしょうか。変換ミスという、エラーとして出力されないものかつ、ユースケースとして考えられるのはCSVの変換ですよね。これに引っかかったらマジで怖いし、何より「C言語で」「文字エンコーディングのプログラミング」を行っていることこそが恐怖の和集合のように思えて仕方がありません。
治ることを祈ってる(直そうとしたがテストコードに引っかかってできそうにない&プレッシャーを感じるのでロールはバグ報告にとどまろうと思う)
php-srcほどのバグ報告となると、急病人を救助するのと同じくらいの心理的な動揺はあるかもですね。
— てきめん (@youkidearitai) March 31, 2022