よくコード規約やベストプラクティスにソースコードは UTF-8 で統一すること、とあります。これは特にマルチバイト文字をソースコード内に埋め込む時に事故が起こりやすく、それの対策として入っています。また単にエディタや人の環境の違いによってソースコードが文字化けするのを防ぐためでもあります。この記事では Shift_JIS をソースコード内で使った際に起きる問題の例を三つ紹介します。
一つ目は次です。
// 能のバイナリコードの末尾が UTF-8 の \ と同じのため、文字列の終端が認識されずに Parse error 発生 $title = 'xx機能';
Shift_JIS のマルチバイト文字は第1バイトに81-9FとE0-EFの範囲、第2バイトに40-7Eと80-FCの範囲を用いて構成されます。このため \ と同じ 5C を第2バイトに持つ文字が現れる場合があり、それが問題を引き起こします。例では”能”という”\x94\x5C”から成るマルチバイト文字の直後にシングルクォートを配置したため”\'”と解釈されて Parse error になりました。この例では Parse error のため問題が起きていることがわかりましたが、実際には単に謎の文字化けに留まることもあり問題に気づきにくいです。
二つ目は次です。
// ゼの末尾が [ と同じのため正規表現のグループが機能し \x83a がマッチする preg_match("/ゼa-z]/", "\x83a", $matches); var_dump($matches); // ポの末尾が | と同じのため正規表現の or が機能し"ケ"がマッチする preg_match("/^ポケ$/", "ケ", $matches); var_dump($matches); // ポの末尾が | と同じのためポの1バイト目が宙に浮き // Warning: preg_match(): Compilation failed: UTF-8 error: isolated byte with 0x80 bit set at offset 1 // と警告が出力される preg_match("/ポケ/u", "ケ", $matches); var_dump($matches);
正規表現の中では少なくない記号が特別な挙動を表現します。このため正規表現宙に Shift_JIS のマルチバイト文字を混ぜると予期せぬ正規表現の挙動を招きやすくなります。例ではそれぞれ角かっこのグループ化とパイプの or を動かしています。他にも~や{}と被るマルチバイト文字があるため例以上に不思議な挙動になりやすいです。
三つ目は次です。
// チの末尾が ` と同じのため ` が予期せぬタイミングで閉じて、余りの文字で Parse error 発生 $str = `echo チ`;
シェル実行用の演算子である`とチの末尾が同じのため上記の通りになります。シェル実行が予期せぬ挙動をするというのは任意コード実行の脆弱性につながりやすいです。Shift_JIS の1バイト目は2バイト目より厳格に安全な範囲で定義されているため、これといった方法は思いつきませんでしたが、これを起点にした問題は不具合にとどまらないやもしれません。
これら例として挙げた言語構造として特殊な意味合いを持つ文字の範囲であり、Shift_JIS の第2ビットの範囲でもあるのは主に以下です。
下位4ビット | |||||||||||||||||
+0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +A | +B | +C | +D | +E | +F | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
上位4ビット | 50 | P | Q | R | S | T | U | V | W | X | Y | Z | [ | \ | ] | ^ | _ |
60 | ` | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | |
70 | p | q | r | s | t | u | v | w | x | y | z | { | | | } | ~ | DEL |
これと先にリンクとして貼ったシフトJISの文字コード表を見比べるとソースコード内のマルチバイト文字に問題があるかないか目grepできます。正直、人間に制御できるものではないのでおとなしく UTF-8 を使ってソースコードを作る方が安心です。
追記:こういった文字をダメ文字と言い、ダメ文字でググると分かりやすい資料が多く出てきます。