PHP には先達の開発者のおかげで日本語対応の機能が多くあります。日本語文字コード対応はそんな中の一つで、よくある UTF-8 やいくつかの環境で扱いやすい Shift_JIS やその拡張など様々な文字コードの変換にも対応しています。文字コードの変換は mb_convert_encoding という関数で行います。
PHP: mb_convert_encoding – Manual
この mb_convert_encoding 関数のふるまいが現状 PHP8.0 以前と PHP8.1 以降で異なります(2022-04-20時点ではこの問題に関連する文字コードの話とあわせて今後どうするか php-src で議論中)。サンプルコードとデモは次です。
<?php
function conv($str): string
{
$res = '';
$res .= ' SJIS: ' . mb_convert_encoding($str, 'SJIS', 'UTF-8') . "\n";
$res .= 'SJIS-win: ' . mb_convert_encoding($str, 'SJIS-win', 'UTF-8') . "\n";
return $res;
}
$res = '';
$res = conv('あ');
$res .= conv('~');
$res .= conv('\\');
echo $res;
/*
PHP8.0以前
SJIS: あ
SJIS-win: あ
SJIS: ~
SJIS-win: ~
SJIS: \
SJIS-win: \
*/
/*
PHP8.1以降
SJIS: あ
SJIS-win: あ
SJIS: ~
SJIS-win: ~
SJIS: \
SJIS-win: \
*/
Online PHP editor | output for Dt0Pq
デモは表示と文字コードが合わないので文字化けしますが、実際はコメントの様になっています。無印の SJIS の変換結果が変わっています。それぞれの文字の表す意味は変わらないので表示上の問題は起きにくいですが、何がしかの形式にそぐわなくなる、という問題が起きうると予想できます。例えば次の様に一部記号とひらがなカタカナの入力を許して、半角文字に変換して保存したい、という動作が壊れます。
<?php
function conv($str): string
{
$res = '';
// @see https://www.php.net/manual/ja/function.mb-convert-kana.php
// a 「全角」英数字を「半角」に変換します。
// k 「全角カタカナ」を「半角カタカナ」に変換します。
// s 「全角」スペースを「半角」に変換します(U+3000 -> U+0020)。
// h 「全角ひらがな」を「半角カタカナ」に変換します。
$mode = 'aksh';
$res .= ' SJIS: ' . mb_convert_kana(mb_convert_encoding($str, "SJIS"), $mode, "SJIS"). "\n";
$res .= 'SJIS-win: ' . mb_convert_kana(mb_convert_encoding($str, "SJIS-win"), $mode, "SJIS-win"). "\n";
return $res;
}
$res = '';
// バリデーションを通って来たひらがなと半角記号で構成された文字列
$res .= conv('あいうえお~\\');
echo $res;
/*
PHP8.0以前
SJIS: アイウエオ~\
SJIS-win: アイウエオ~\
*/
/*
PHP8.1以降
SJIS: アイウエオ~\ // 半角文字だけを期待したが全角文字が混じっている
SJIS-win: アイウエオ~\
*/
対策としてはこれまでの例の様に SJIS-win を使うという方法があります。SJIS-win 指定は Windows 版元である Microsoft が Shift_JIS を拡張した文字コードを指定します。こちらならば従来通りの挙動をします。
余談ですが SJIS-win は PHP8.1 で CP932 というまた少し違う文字コードのエイリアスとなり、mb_list_encodings 関数で返される文字コードのリストから消えています。こちらも現在(2022-04-20)今後どうするか議論中です。
PHP: mb_list_encodings – Manual
ちなみに違う部分は次です。
UTF-8の"¥"を SJIS-win ないし CP932 に変換すると
SJIS-win: ¥
CP932: \
UTF-8の"‾"を SJIS-win ないし CP932 に変換すると
SJIS-win:  ̄
CP932: ~
Online PHP editor | output for AaUu7#↑の文字含めて SJIS-win による変換結果が変わっていないことを示すデモ
2022-04-26追記:以下の様にピンポイントで違う文字を置き換えることでこれまで同様の挙動を再現できます。SlackのPHPユーザーズ(日本語)で教えてもらいました。
$str = strtr(mb_convert_encoding($input, 'UTF-8', 'SJIS'), ['¥' => '\\', '‾' => '~']);