iPhoneの絵文字をMySQLにInsertしようとすると文字列がぶった切れる件
半日くらいハマったので、メモしておきます。
PHPとMySQLを使ってWebアプリ開発しているのですが、この環境にiPhoneから絵文字入りのテキストデータをPOSTすると、絵文字を入力した後ろの所からバッサリと文字列が削除されてしまうという現象が起きていました。結論からいうとiPhoneの絵文字がUTF8の4バイト文字なのでMySQLに登録できなかったわけなのですが、その調査作業手順をまとめたメモです。
まずサーバやアプリのログを追ってみたのですが、よくわからず。そもそもサーバまでちゃんとデータが届いているのか調べるためtcpdumpしてみました。
# tcpdump -nX port 80 -s0 -i eth0 -w hogehoge.dump
のようにコマンドを実行すると、パケットを監視して内容をファイルにダンプするようになる。この状態でiPhoneから
abc[絵文字]abc
のようなテスト用テキストをPOSTします。これでTCPのダンプが取れます。続いて、取得したダンプファイルをローカルPCにダウンロードしてWiresharkというソフトを使って内容を確認します。
HTTP POSTリクエストのところをクリックして詳細を見ます。POSTで送信されるデータは「Line-based text data: application/x-www-form-urlencoded」というセクションに書かれているのでココを見てみます。すると投稿データは以下のようになっているのがわかりました。
text=abc%F0%9F%98%A3abc
どうやら絵文字データちゃんとサーバまで届いているようです。HTTPのプロトコル上では「%F0%9F%98%A3」と表現されているのが絵文字の箇所。で、これはURLエンコーディングされているので元のデータは
F09F98A3
という4バイトデータということがわかります。ここで、おや?と感じる。いわゆる携帯(ガラケー)の絵文字はキャリアごとに仕様は異なるのですが、DBに保存するときUTF8の3バイト文字表現に変換して登録していました。UTF8の4バイト文字とはあんまり馴染みがない。
UTF8、4バイト、MySQL。ここいらをキーワードにしてぐぐる先生で検索。
http://www.mysql.gr.jp/mysqlml/mysql/msg/13823
はい。結構古い記事なのですがこれがドンピシャなようです。これによると「...4バイトのUTF-8の文字以降の全ての文字列が削除された上で格納される」らしいです。はい、そうなってました。よし、もうちょっと調べる。
http://nippondanji.blogspot.com/2010/04/mysqlmysql-554.html
によるとutf8mb4という文字コードを指定すれば4バイトまで入れられるっぽい。
とはいえ、そのまま4バイトデータを入れてもこれをガラケー向けに表示するには変換しなければならなかったり、現在動作中のDBの文字コードを途中で変えるのも何が起こるかよくわからんので、ひとまず4バイト文字は全部削除する方向で対応しました。
PHPで書くとこんな感じのコードです
$textwithout4byte = preg_replace('/[\xF0-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF]/', '', $textwith4byte);
これで絵文字は削除されてしまうが、少なくとも絵文字から後ろがごっそり削除されるようなことにはならなくなりました。めでたしめでたし?