この手の話題はもううんざりしているし、ある種の技術者にとっては当たり前で自明の理というお話だと思いますが、SATOXが3時間ほど悩んだので解決方法をメモ的に記事にしておきます。
たぶん、もっとスマートなやり方とかエスケープしろとかはあるとは思いますが……。
(ちなみに、PHP 5.2.17を使ってます)
■前提と問題点
目的としてはPHPソースからMySQLのデータベースにアクセスし、情報の一覧をJSON形式で出力しようと思いました。で、日本語文字を出力しようとしたところ、「????」と文字化けしてしまったというあるある系の問題が発生。うんざりです。
ひとまず基本的なお話として、データベースもフィールド文字設定もUTF-8に設定しました。
これで面倒な文字化け問題は起きないと思っていたのですが……。
で、まずはUTF-8で保存したPHPソースから、直に日本語を出力して大丈夫なことを確認。
まぁ以下のようなコードですな。とりあえずJSONの事は忘れます。
<?php header('Content-type: text/plain; charset=UTF-8'); header('Content-Transfer-Encoding: binary'); echo "日本語OK\n"; ?>
乱暴だけど、バイナリで無変換。PHPソースがUTF-8なので大丈夫だった。
以下、実行結果。(Firefox)
日本語OK
■データベースから取得した文字を表示したらダメだった
で、次にデータベースとテーブル、NIHONGOというUTF-8の日本語フィールドを作成して、表示しようとしてみました。ソースはこんな感じ。
<?php $sql = mysql_connect("DBURL", "DBユーザ名", "DBパスワード"); mysql_select_db("DBデータベース名", $sql); $query = "SELECT * FROM SQLテーブル名"; $result = mysql_query($query); header('Content-type: text/plain; charset=UTF-8'); while ($row = mysql_fetch_array($result)) { echo $row["NIHONGO"] . "\n"; } ?>
ところが、文字化けしてしまったんです。
以下、実行結果。
???OK
???????
バイナリにしてもダメでした。DBもUTF-8なのに誰が勝手に変換しやがるのか。
■おまじないクエリを入れたら問題解決
というわけで、「UTF-8を使いますよ」との設定が必要です。データベース接続後にSQLクエリの「SET NAMES」でUTF-8設定をしてみました。
<?php $sql = mysql_connect("DBURL", "DBユーザ名", "DBパスワード"); mysql_select_db("DBデータベース名", $sql); mysql_query('SET NAMES utf8', $sql ); // ←これ $query = "SELECT * FROM SQLテーブル名"; $result = mysql_query($query); header('Content-type: text/plain; charset=UTF-8'); while ($row = mysql_fetch_array($result)) { echo $row["NIHONGO"] . "\n"; } ?>
以下、実行結果。
日本語OK
たけやぶやけた
してやったり。
■SET NAMESに問題あり?
ググってみると、SET NAMES設定には問題があるような記事が見つかります。そのはっきりとした理由はよく理解できませんでしたが、PHP4、PHP5的な変遷を考えると「クエリで解決するのではなく、APIで解決しろ」という事だと勝手に解釈。
PHP 5.2.3以降ではクライアント文字設定を行うmysql_set_charset という関数があるので置き換えてみました。
これでも大丈夫でした。
<?php // mysql_query('SET NAMES utf8', $sql ); mysql_set_charset('utf8'); // ←変更 ?>
さて、JSONの話がすっ飛んでいますが、JSONで出力するなら、コンテントタイプは「application/json」がより正確ですかね。JSONはUTF-8がデフォルトです。
結論。
「mysql_set_charset('utf8');」しなさい、と。