今回も結構レアなケースだろうから、あんまり役には立たないかもしれないけど、小規模・低予算のサイトの場合、案外ありそうな気もするのと、備忘録として。
前書き
ウェブサイトを構築する場合、最近はほとんどUTFを使う場合が多い。jQueryその他マッシュアップAPI、OSSなどを用いる場合、UTFのほうが便利。(むしろUTF前提で作られてるものが多い)
新規に構築する場合で、MySQL4.1~であれば、何も考えずにHTML,PHP側もUTFにしておけば混乱は少ない。
しかし、MySQL3.xの頃から稼働しているサイトの場合、DBやHTML,PHPがEUCであることも多く、コレをすべてUTFに移行するのは、恐ろしく手間が掛かる。(ハイリスク・ローリターン)
しかし、MySQL3.0,4.0系から、4.1~へアップグレードすると、ほぼ間違いなく文字化けするようになり、この文字化けが回避できず、せっかくMySQLが複数エンコーディングをサポートしているにも関わらず、サーバ側・クライアント側をEUCガチガチに固定して運用してしまうケースも多々あると思う。(恥ずかしながら僕がそうです)
だがそれでは前述のAjaxや最新のOSS(WordPressとか)などUTF8を前提としている機能が使えないというデメリットがある。
1つのホスト内でのVirtualHostで、あるサイトはEUCを、あるサイトはUTF8を使うようにMySQLで設定できないか、というのが今回のテーマである。
テスト環境
下記のようなVirtualHostがあるとする。
host A) eucdb.example.com – 127.0.0.1
host B) utfdb.example.com – 127.0.0.1
eucdb.example.comは、既に稼働しているサイトであり、MySQL4.0系を使っている。
utfdb.example.comは新しく設置したサイトで、今後、UTF8にて運用する予定。
つまり、1つのMySQL[mysqld]にて、EUC,UTF8のデータ利用したい、という訳である。
今回テストしたOSは、OS X10.4だけれど、基本的にLinuxでもWindowsでも関係なく、MySQLとPHP側の設定で解決できます。
my.cnfファイルの記述
[mysqld] default-characterset=utf8 character-set-server=utf8
my.cnfには、とりあえず”utf8″を指定しておく。
skip-character-set-client-handshakeや、init-connectは使わない。[mysql]や[client]に対しての明示的なエンコーディングの指定は行わない。
データベースの作成
■eucdb.example.comで使うデータベースの生成
CREATE DATABASE データベース名 DEFAULT CHARACTER SET eucjpms ;
※MySQL 5以降の場合、ujisではなく、eucjpmsを使うのが良いらしい。MySQL 4.1~の場合は、素直にujisを指定しよう。
■utfdb.example.comで使うデータベースの生成
CREATE DATABASE データベース名 DEFAULT CHARACTER SET utf8; [/codecode] <h3>インポート</h3> [text] % mysql -uXXXXX -pXXXXX テーブル名 --default-character-set=eucjpms < インポートするファイル
MySQL[mysqld]自体は、utf8にしておいて、単純に、CREATE DATABASEするときと、そこにインポートする時に、ちゃんとdefault-character-setで、eucjpms(ujis)を指定すること。
ただし、これだけでは、PHPからDBにアクセスしてデータを取得しても、utf8で返ってきてしまう。なぜなら、mysqlのクライアントに対して、変換の指示をしていないからutf8になる。CREATE DATABASEで指定したエンコーディングを自動的に返せばいいのにね。
※my.cnfの設定により、eucjpms(ujic)固定で値を返すことも可能。ただし、my.cnfで設定してしまうと、今度はutf8を扱えなくなってしまう。
PHP側の処理
CREATE DATABASEでeucjpmsを指定し、合わせてちゃんとimportしてるにも関わらず、PHPからアクセスすると、utfでデータが返ってくる。なぜそうなるのか100%理解してるわけではないが、my.cnfにクライアント側のエンコーディングを指定してないため、デフォルトでもあるutfに勝手に変換されてるのだと思う。
最初からサーバ側とクライアント側が同一エンコーディングであればこの問題は発生しないが、何度も書いているように、euc,utf8のデータベースを使いたいから、その設定は使えない。
そこで、PHP側からはMySQLに対し、明示的にエンコーディングを指示してやることで、指定したエンコーディングでデータを取得できる。
そのもっとも簡単な方法が、’set names XXXX’である。XXXの部分はeucjpmsやujisを入れる。
しかし、set namesにはさまざまな問題があり、PHP 5.2.3~かつ、MySQL 5.0.7 移行であれば、mysql_set_charsetを使うのが推奨されている。
PHP 4.xを使ってる場合は、この際5系に切り替えた方がいいでしょう。
ちなみに、僕はDB_DataObjectをよく使っていて(PEARだし、フレームワークに依存しないし、遅いけど便利と思うんだけどな)、テスト用のサーバはPHPが4.xのため、set namesを使わざるを得ない。その場合、DB_DataObjectの初期化の直後に、
$dbinit = new DB_DataObject;
$dbinit->query('set names eucjpms');
:
という感じで、factoryせずにDB_DataObjectクラスから1度だけqueryで実行してやればOKです。
いずれ、PHP用のActiveRecordとか、Propelに移行しないとダメとは思うけど、既に手に馴染んでるからなぁ…
まとめ
perlだと、
use DBI;
my $dbh = DBI->connect(
'DBI:mysql:database=sandbox;host=localhost;mysql_read_default_file=/etc/mysql/my.cnf',
qw/id password/,
);
という感じで、コネクト時にcnfファイルを指定できるらしい。これは便利。
phpでも、mysqli関数を使えばできるらしいが、これはこれでまた面倒の種が増える…。
正直、MySQL4.1からのエンコーディング周りの処理には、いい印象がなく、文字化けにウンザリしていたのだが、腰を据えてやってみると、案外簡単であった。
サーバを移行しようにも、MySQL3.x → MySQL 4.1~5での文字化けが怖くてできない…けど、EUC固定もできないし!という方はこのエントリが参考になれば幸いです。
参考リンク
SET NAMESは禁止
PHPからSET NAMESを使わない方が良い理由と対策まとめ | twk @ ふらっと
MySQL5開拓団 - 日本語処理の鉄則 / KLab株式会社 ← 重要!
mysql_set_charset関数を使ってみる – PHPプロ!TIPS+
へぼへぼCTO日記 – libmysqlclientを使うプログラムはset namesをutf8であっても使ってはいけない
Popularity: 15% [?]




