3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。(jehupc.exblog.jpから移転中)

(PHP)フォームからPOSTで受け取ったデータが文字化けする

レンタルサーバPHPの内部エンコーディング(mb_internal_encoding)が、作成しているPHPエンコーディングと異なっていると文字化けになってしまいます。

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

今回レンタルサーバ側のPHPEUC-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

レンタルサーバによっては、.htaccessphp_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に変換される件

注意事項 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 にしています。)

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);

}

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 関数も入れておくといいかもしれません。