Shift-jisで『ソ』をDBに保存する際の文字化け

文字コードをshift-jisでformを作成し、そのままMySQLに保存すると文字化けて登録される。

何故文字化けが起きるかというと、PHPの方でマルチバイト文字の誤認識をしたまま変換しているから。

つまりは仕様です。

文字化けの仕組み

PHPは親切にもFormで送るとき、「"」「'」「\」みたいなんを、エスケープ文字『\』つけて「\"」「\'」「\\」として送ってくれる。
で、受取ったら今度は「"」「'」「\」に戻すために、『\』を自動的に消してくれる。

だが
残念ながら『shift-jis』等『マルチバイト文字』を考えてなかった。

『\』をバイナリ化すると『5C』。
『shift-jis』ではこの『5C』が案外使用されているのだ。

1文字を構成するのが1バイトである半角の英字に対し、マルチバイト文字は2バイト以上で作られている。

半角英字は『A』=『41』みたいに1つで現せるから、『5C』は当然『\』だけ。

でも
『あ』=『82 A0』のように2つバイトを持つマルチバイトの場合、『5C』を使っているのは、単純に『\』だけではない。
※1バイト=8ビット。それを16進数であらわすから、1バイト2桁数字で書かれてる。

化け代表として「ソ」(83 5C)があるから、それで例えてみる。

[パ][ー][ソ][ナ][ル] は [83 70][81 5B][83 5C][83 69][83 8B]

PHPで『5C』が消されるから、その分詰められて、
マルチバイトの法則に乗っ取って再結合される。
[83 70][81 5B][83 83][69][83 8B]

結果
『パーャiル』
と表示されてしまうわけだ。

対処法

仕組みは『5C』を1個だけ削るので、文字化ける文字に『5C』つまり『\』を足しておく。
つまり『ソ』なら『ソ\』にしなきゃいけない。

[パ][ー][ソ][\][ナ][ル] = [83 70][81 5B][83 5C][5C][83 69][83 8B]

処理後、1個だけ削られ残った文字でマルチバイト的に結合すると
[83 70][81 5B][83 5C][83 69][83 8B] で [パ][ー][ソ][ナ][ル]
おめでとう!

と、色々書いてみたものの、
こちら、イチイチ該当文字をモチモチやる必要はない。
シッカリとPHP側で設定を用意してある。

magic_quotes_gpcをONにしておく
または変換対象に『addslashes()』をしておくこと

これだけの単純な話なんだけど、せっかく調べたから書きたかった

変換対象に『addslashes()』

addslashes()は、PHP内で、マルチバイト的に上の現象が起こりそうだなぁという文字の後ろに『\』を付けてくれる。
使い方は以下みたいな感じ。

if(isset($_REQUEST['なんつら']))
	$_REQUEST['なんつら'] = addslashes($_REQUEST['なんつら']);

magic_quotes_gpcをONにする

ONにすると$_GET、$_POST、$_REQUEST、$_COOKIEにaddslashes()が自動的にかかる状態になる。
便利だけどFormで受取る度に『/』付け続けるから、繰り返しFormで処理のときには注意が必要。
設定方法は『php.ini』『.htaccess』『php処理ファイル』の3つがある。

『php.ini』にて設定

magic_quotes_gpc = On

『.htaccess』にて設定

php_value 'magic_quotes_gpc' 'on'

『php処理ファイル』に設定

ini_set('magic_quotes_gpc', 'on'); 

を一番先頭に記載。

magic_quotes_gpcがかかっているかわからないとき

サーバ管理権限が自分になく、htaccessも置けない設定されてたとか、そんなとき。
自動でならないなら手動でやります的な処理を付けとく。

スラッシュつけたいとき

if (!get_magic_quotes_gpc()) {
	if(isset($_REQUEST['なんつら']))
		$_REQUEST['なんつら'] = addslashes($_REQUEST['なんつら']);
}

スラッシュ取りたいとき

if(ini_get(magic_quotes_gpc) == 1){
	if(isset($_REQUEST['なんつら']))
		$_REQUEST['なんつら'] = stripcslashes($_REQUEST['なんつら']);
}

『配列』でスラッシュつけたいとき

if (!get_magic_quotes_gpc()) {
	function my_addslashes($val){
		return is_array($val) ?
			array_map('my_addslashes', $val) :
			addslashes($val);
	}
	if(isset($_REQUEST['なんつら']))
		$_REQUEST['なんつら'] = my_addslashes($_REQUEST['なんつら']);
}

『配列』でスラッシュ取りたいとき

if(ini_get(magic_quotes_gpc) == 1){
	function my_stripcslashes($val){
		return is_array($val) ?
			array_map('my_stripcslashes', $val) :
			stripcslashes($val);
	}
	if(isset($_REQUEST['なんつら']))
		$_REQUEST['なんつら'] = my_stripcslashes($_REQUEST['なんつら']);
}

因みに下記はどちらの書き方でも処理は動くのだが

if(ini_get(magic_quotes_gpc) == 1){

↑はNOTICEエラー出るのでエラーログが増えてしまうので

if (get_magic_quotes_gpc()) {

を使う方が無難。

蛇足

全くの蛇足メモ。

昔、上記の変換方法を一切使わず、対象の文字をイチイチ探し変換する状況があったのだけど、下記ではエラーが色々出た。

$str = mb_ereg_replace ("ソ\","ソ", $str);

ところが、下記を.httaccessに書き込むとなくなった。

php_value max_execution_time 0
php_flag display_errors Off
php_flag output_buffering Off
php_flag magic_quotes_gpc Off
php_flag session.use_trans_sid On

php_value error_reporting 7
php_value default_charset Shift_JIS
php_value mbstring.detect_order auto
php_value mbstring.http_input SJIS,EUC-JP,ASCII,JIS,UTF-8
php_value mbstring.http_output SJIS
php_value mbstring.internal_encoding SJIS
php_value mbstring.substitute_character none
php_value mbstring.language Japanese

結局どれがよかったかなんて、もう使わないであろう遺物なので、もう調べることはないだろう…

コメント

タイトルとURLをコピーしました