(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");
しかし、これでも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が置いてあるディレクトリパスを指定。
←php.iniをブラウザから閲覧させないようにする。 deny from all
コントロールパネルの【お客様情報】から、【プログラムのパスとサーバの情報】をクリックし、使用する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に変換される件
○ファーストサーバの場合
後者の方法となります。
つまり、.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 にしています。)
method="post" action="test.php" >
type="hidden" name="enc" value="あ">
type="text" name="test" value="" >
type="submit" value="送信" name="submit">
test.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);
}
}
/**
* エンコードのコア部分
* @param unknown_type $value
* @param string $default_enc
* @param string $enc
*/
function EncodeCore( &$value , $default_enc , $enc){
//配列の場合は再帰処理
foreach ($value as &$value2) {
EncodeCore($value2 , $default_enc , $enc);
}
}else if( $enc != $default_enc){
//文字コード変換
$value = mb_convert_encoding( $value , $default_enc , $enc ) ;
}
}
?>
POST配列内に、さらに配列で来ることも予想されるので(チェックボックス使った時等)、再帰処理で変換します。
参考:
今回は、同じスクリプトを複数のレンタルサーバに配置して行う必要があったので、後者の mb_convert_encodingでエンコーディング変換する方法をとりました。
(PHP)レンタルサーバで文字コード固定されている時の文字化け回避法 でも書いたページの先頭で呼び出す、Init関数に、上記の FormEncode 関数も入れておくといいかもしれません。