🕌

【PHP】SJIS-macに変換したはずなのにSJIS-winになる

9 min read

SJIS-macに変換したはずなのにSJIS-winになる

StackOverflowにmb_detect_encoding does not detect SJIS-mac?という投稿がありました。

ということらしいので、UTF-8からSJIS-macに変換してみましょう。

$utf8Str = "❶❷❸❹❺";

$sjisStr = mb_convert_encoding($utf8Str, 'SJIS-mac');

echo(mb_detect_encoding($sjisStr, ['UTF-8','SJIS-mac', 'SJIS-win', 'SJIS'])); // SJIS-win ←

SJIS-macに変換したはずなのに、何故かSJIS-winと判定されてしまいます。

そもそもSJIS-macってなんだよって話ですが、単にMacJapaneseのエイリアスです。
従ってMacJapaneseと書いても同じく、正しく誤判定されます。

そしてコメント欄This is a bug in PHP's mbstring extension『mbstringエクステンションのバグじゃよ』という人が現れています。
間違ったコードを書いたときに自分のせいではなく言語・ライブラリのせいにする人はよくいますが、この人の場合は適当言ってるわけではなく本当にバグです。

https://github.com/php/php-src/pull/6052

コメント主のalexdowadは先日、PHPソースにプルリクを送り付けました。
中身はmbstringエクステンションに関するもので、バグ修正、不要なコードの削除、トリッキーなコードのリファクタリング、高速化、テストカバレッジ上昇など多くの改善が含まれています。

これがなかなか大規模で、91コミット5万行以上のごつい変更となっています。
さすがに大半はテスト用の変換テーブルみたいですが、それでも実コードの修正も数千行は入っています。

mbstringなので日本語に影響するかもしれないということで少し覗いてみました。

https://github.com/php/php-src/pull/6052#issue-475811327

このプルリクはmbstringの改善を目的としていいる。

・不要なコードを削除。
・曖昧なコードを読みやすくする。
・残ってしまったトリッキーなコードにはコメントを追加する。
・全てを高速化する。
・バグの修正。
・テストカバレッジの向上。
・一貫性を向上させるため、セマンティクスを調整した。

もしかしたらパフォーマンスのためにあえてそのように書かれているのではないかと考えていた部分もあったが、実際はそうでもなかった。
リファクタリングすることは、パフォーマンスのためにも良いことだった。
性能チェックのために591のベンチマークを走らせた。
幾つか性能劣化が見つかったが、それも修正した。
最適化の余地はまだまだ大量に残されているので、いずれそれらも改善する予定である。

https://github.com/php/php-src/pull/6052/commits/0880fc00094f363dcaac6762f2ec94a02e380baf

mb_check_encodingを最適化したよ
これによってmb_check_encodingが50%高速化されます。

https://github.com/php/php-src/pull/6052/commits/6197b8e7a2ec81e63e1079812741dccdab0b6417

HTMLの数値エンティティ変換を最適化したよ。
これによってmb_encode_numericentityが10%高速化されます。

https://github.com/php/php-src/pull/6052/commits/e3d5a05a97c5ef8d60489ca3491930bf75d79647

mb_str_splitの本体をmbfl_str_splitに移したよ。あとバグも修正した。

https://github.com/php/php-src/pull/6052/commits/4d9999674ec9fc298e3fe26e7445f24c50575792

ISO-8859-xエンコーディングの変換テーブルとテストスイートを追加したよ。

ちなみにISO-8859-3(Latin-3)ISO-8859-6 (Latin/Arabic)ISO-8859-7 (Latin/Greek)ISO-8859-8 (Latin/Hebrew)ISO-8859-16 (Latin-10)が追加されてる。

https://github.com/php/php-src/pull/6052/commits/322bf79b92ab98d54fde1b30c9ebb633b71d8391

MBFL_CHP_{CTL,DIGIT,UALPHA,LALPHA,MSPECIAL}使ってないから削除したよ。

https://github.com/php/php-src/pull/6052/commits/5b0984a5a513cdc7b7a76986c7dc391c90725d3f

意味のないポインタ参照を削除したよ。

	(const char *(*)[])&mbfl_language_uni_aliases,

というか何これ。

https://github.com/php/php-src/pull/6052/commits/136a03103e8566fa89189c8dc65cb6bffe5c92a4

IMAPのために、UTF-7の改良版Modified UTF-7を追加したよ。

https://github.com/php/php-src/pull/6052/commits/fb98c004cb71fe0707ce35bc46c9cfb8465b9891

Shift-JIS-2004を追加したよ。

https://github.com/php/php-src/pull/6052/commits/2621a78593d8bc1ef5131521b56ca1d98a86776e

ISO-2022-JP-2004の識別を厳密にしたよ。

https://github.com/php/php-src/pull/6052/commits/26204445a7e8b08ee5a0dbbbe247e970e75df8dc

mb_convert_kanaが存在しない変換オプションに例外を出すようにしたよ。

https://github.com/php/php-src/pull/6052

動作検証のためにテストコードを追加したら、既存コードに多くのバグを発見してしまったよ。

https://github.com/php/php-src/pull/6052#issuecomment-695835716

mb_strposに面白い問題を発見したよ。
mb_strposには$needleの長さ > $haystackの長さだったら即falseにするって処理が入ってるんだけど、同じ文字を複数の形式で表すことができるエンコーディングもあって、その場合$needleの長さ > $haystackの長さになることがあるんだよね。

他にも大量のコミットが存在します。

Nikita

レビュアーとしてNikitaが現れる
いや本当、何処に行ってもこの人がいるな。

mbstringは元々libmbflというライブラリを使ってたんだけど、これはもう長いことメンテされていない。
なのでPHP7.3/7.4でこのあたりに大幅なメスを入れたんだけど、まだ概念的には別のライブラリとして扱っている。
このプルリクではlibmbflを捨て、直接Zend APIとして使用するようにしてるけど、それによるデメリットはないので今後はそうする方向がいいと思う。

あとヘッダ部分の書式、何処から来てるのかはわからないけど3スペースのインデントが使われているけど、これはそのままのほうがいいだろう。

ものすごい勢いで突っ込み・コミットを入れているものの、全体的には反対ではなさそうです。
おそらくこの変更はそのうちマージされそうな雰囲気です。

感想

いやあ、この人やばいね。
日本人でも全然わかってない人も多い(私とか)Shift_JIS-2004やらISO-2022-JP-2004やらも的確に対応して大量のテストを追加してさらに最適化・高速化まで成し遂げてしまう。
すごすぎる熱量と能力です。

さらにこのプルリク、(part 1)とか書いてありますからね。
パート2では今回あまり手の入っていないmb_convert_encodingあたりにも最適化が入りそうですね。

しかし、mbstringの主戦場である日本や中国からのコントリビュートが全くないのが気になります。
極端な話英語圏ではmbstringがなくてもなんとかなりますから、切り捨てられないためにも日本からも有識者が参加して存在感を出してもらいたいところですね。

Discussion

ログインするとコメントできます