人気ブログランキング | 話題のタグを見る
(PHP)フォームからPOSTで受け取ったデータが文字化けする
レンタルサーバでPHPの内部エンコーディング(mb_internal_encoding)が、作成しているPHPのエンコーディングと異なっていると文字化けになってしまいます。

特に厄介なのが、フォームのデータをPOSTで受け取るときです。

今回レンタルサーバ側のPHPは EUC-JP , 作成中のPHPスクリプト、HTMLは UTF-8 となっています。(UTF8の方が汎用性高いんで。。)

文字化けを防ぐために、PHP側で下記のようにしていました。
header("Content-type: text/html; charset=UTF-8");
mb_language("Japanese");
mb_internal_encoding("UTF-8");

しかし、これでもPOSTデータから受け取った文字列は文字化けしています。
mb_detect_encoding 関数で調べると、やはり EUC-JP でデータが送られてきています。

どうやら強制的に EUC-JP に変換されているようですね。

問題なのは、mbstring.encoding_translation がレンタルサーバ上でONになっていたことです。
これがONになっていると、フォームで送ったデータは、php.ini の mb_internal_encoding で設定されている文字コードに変換してしまうようです。
まったく、なんで encoding_translation が ON 何でしょうか。。。(デフォルトはOFFのはずなんですが。。。 参考:mbstring.encoding_translationはOnにしていいことあるのか)


対策1. php.iniの書き換え
対策としては、php.ini の値が書き換えられるレンタルサーバの場合は、下記のようにしてしまう方法があります。

mbstring.internal_encoding = utf-8
mbstring.encoding_translation = off

レンタルサーバによっては、.htaccess で php_flag , php_value で設定するところもあるようですが、その場合下記のようにします。

php_value default_charset "UTF-8"
php_flag mbstring.encoding_translation off


○CPIの場合
ちなみに、CPIの場合は、前者の php.ini による設定となっていました。
しかし、php.iniファイルがあるディレクトリのPHPスクリプトにしか設定されないので、任意のディレクトリ配下すべてとう場合には下記のようにします。

.htaccess に下記設定を追加

suPHP_ConfigPath /usr/home/x999999/conf/ ←php.iniが置いてあるディレクトリパスを指定。
<Files ~ "\.ini"> ←php.iniをブラウザから閲覧させないようにする。
deny from all
</Files>


コントロールパネルの【お客様情報】から、【プログラムのパスとサーバの情報】をクリックし、使用するPHPと同じバージョンのphp.ini情報をテキストファイルにコピーし、/usr/home/x999999/conf/php.ini を作成する。
php.ini内で、任意の設定。(今回は下記)

php_value default_charset "UTF-8"
php_flag mbstring.encoding_translation off


参考:
CPIサーバでphpの$_POSTデータが勝手にeuc-jpに変換される件
注意事項 for CPIサーバ


○ファーストサーバの場合
後者の方法となります。
つまり、.htaccess で下記を設定します。

php_value default_charset "UTF-8"
php_flag mbstring.encoding_translation off

しかし、この方法を使うにはPHPをDSO版に切り替える必要があります。(つまりCGI版ではダメということです。)

参考:
ファーストサーバ:プログラムの作成・設置方法:文字コードの設定


対策2. mb_convert_encodingでエンコーディング変換
もうひとつは、mb_detect_encoding関数で、フォームの文字コードを取得し、 mb_convert_encoding関数で、文字コードを変更してしまうというものです。

ただし、mb_detect_encoding 関数は判定する文字列によって誤爆するらしいので、確実に文字コード検出可能な文字を、フォーム送信データに強制的に含めておくのが望ましいようです。

こんな感じです。(下記の例では、"あ" を文字コード検出可能な文字として hidden にしています。)

<html><head></head>
<body>
<form method="post" action="test.php" >
<input type="hidden" name="enc" value="あ">
<input type="text" name="test" value="" >
<input type="submit" value="送信" name="submit">
</form>
</body></html>


test.php 側

<?php
FormEncode($_POST); //←これでPOSTデータ内が全てUTF-8化されます
 
/**
* フォームから来たデータをエンコードする
* @param array $post フォームから来たデータ
*/

function FormEncode(&$post){
if ( isset($post['enc']) ){
return;
}
//どのエンコーディングか判定
$enc = mb_detect_encoding($post['enc']);
$default_enc = "UTF-8";
foreach ($post as &$value) {
EncodeCore($value,$default_enc,$enc);
}
unset($value);
}
/**
* エンコードのコア部分
* @param unknown_type $value
* @param string $default_enc
* @param string $enc
*/

function EncodeCore( &$value , $default_enc , $enc){
if( is_array($value)){
//配列の場合は再帰処理
foreach ($value as &$value2) {
EncodeCore($value2 , $default_enc , $enc);
}
}else if( $enc != $default_enc){
//文字コード変換
$value = mb_convert_encoding( $value , $default_enc , $enc ) ;
}
}
?>

POST配列内に、さらに配列で来ることも予想されるので(チェックボックス使った時等)、再帰処理で変換します。

参考:
PHP GET/POSTメソッドでの日本語の文字化け防止
PHP の文字化けについて考える


今回は、同じスクリプトを複数のレンタルサーバに配置して行う必要があったので、後者の mb_convert_encodingでエンコーディング変換する方法をとりました。

(PHP)レンタルサーバで文字コード固定されている時の文字化け回避法 でも書いたページの先頭で呼び出す、Init関数に、上記の FormEncode 関数も入れておくといいかもしれません。
by jehoshaphat | 2011-07-03 00:57 | PHP開発


<< (PHP)Smarty利用時に... (PHP)Smartyでサイニ... >>