しばしば何かのために文字コードの変換をすることがあります。そういった時、変換先の文字コードで表現できない文字の存在に気づかないまま文字コードを変換してしまい文字化けが起きることがあります。この文字化けを検知する方法を紹介します。
検知用のコードは次です。
/** * 文字化けが起きるならば true * @param string $txt * @param string $toEncoding * @param string|null $fromEncoding * @return bool */ function isGarbled(string $txt, string $toEncoding, string $fromEncoding = null): bool { // fromEncoding が明示されないならば文字コードを検出する $fromEncoding ??= mb_detect_encoding($txt); // 変換→復元を行い復元後と元テキストで差異がなければ、データが落ちていない、つまり文字化けが起きていないと判断 $encoded = mb_convert_encoding($txt, $toEncoding, $fromEncoding); $decoded = mb_convert_encoding($encoded, $fromEncoding, $toEncoding); return $txt !== $decoded; }
やっていることは文字コードを変換して、復元して、変換前と比べる、というそれだけです。こうすると変換によって文字化けした場合、復元後の文字化け部には文字化け状態を表現した文字(?など)がそのまま入ります。これにより文字化けが起きるか否かを調べられます。これは次の様な流れです。
<?php isGarbled('㈱浜松カンパニー', 'ISO-2022-JP','UTF-8'); function isGarbled(string $txt, string $toEncoding, string $fromEncoding = null): bool { $fromEncoding ??= mb_detect_encoding($txt); $encoded = mb_convert_encoding($txt, $toEncoding, $fromEncoding); var_dump($encoded); // string(21) "?$BIM>>%+%s%Q%K!<(B" $decoded = mb_convert_encoding($encoded, $fromEncoding, $toEncoding); var_dump($decoded); // string(22) "?浜松カンパニー" return $txt !== $decoded; // ㈱と?が違うので true }
ちなみにこの関数はあくまで文字列が文字コードの変換によって意図した文字列と異なる文字列になるか否か、という関数です。セキュリティ的な面での文字化けを調べるのはまた別枠です。セキュリティ的な面では PHP 組み込み関数の mb_check_encoding が役に立ちます。
PHP: mb_check_encoding – Manual
mb_check_encoding は次の様に不正な文字列か否かを調べ、その結果を知らせてくれます。これは攻撃用の文字列や実際の文字コードと想定した文字コードの違いを調べるのに便利です。一方で文字化けについては正常な文字化け用文字として扱うため文字化けを調べることにはつかえません。適材適所で使う必要があります。
<?php $string_bad = "\xc3\x28"; // 不正な2オクテットのUTF-8シーケンス if (mb_check_encoding($string_bad, 'UTF-8')) { echo "This string is valid UTF-8."; } else { echo "This string is not valid UTF-8."; }