レンタルサーバで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 関数も入れておくといいかもしれません。