どんな症状か
変換前
INSERT INTO wp_posts (post_title) VALUES ('縺薙s縺ォ縺。縺ッ 譌・譛ャ');変換後
INSERT INTO wp_posts (post_title) VALUES ('こんにちは 日本');原因はどこにあるか
WordPress は文字を UTF-8 で保存します。ところが古いサイトには、テーブルが latin1 で作られていたり、latin1 で接続していたりするものがあります。書き込みと読み出しで同じ間違いをしている間は気づきません。新しいサーバーへの移行、phpMyAdmin でのエクスポート、接続文字コードの変更をきっかけに、日本語が一斉に化けます。
wp-config.phpのDB_CHARSETがlatin1になっている、またはテーブルと合っていない。- ダンプを
latin1で書き出しutf8mb4で取り込んだ(またはその逆)。 - プラグインやインポート用スクリプトが、すでに化けたテキストをもう一度変換した。これが二重の文字化けです。
エクスポートを修復する
- 何かを変更する前に、データベースのバックアップを取ります。
- 対象のテーブルを
.sqlファイルでエクスポートします。phpMyAdmin のほか、mysqldump --default-character-set=utf8mb4でも可能です。 - 上のボックスにファイルをドロップします。Mojibuster は壊れた部分だけを直し、変更点をすべて表示します。
- 修復したファイルをダウンロードし、
utf8mb4で新しいテスト用データベースに取り込みます。 - サイトの表示を確認し、問題なければ本番のデータベースに取り込みます。
ダンプはアップロードされません。ダンプにはユーザーアカウント、メールアドレス、コメントが含まれるため、これは重要な点です。
シリアライズされたデータに注意
WordPress は一部の設定を wp_options や wp_postmeta に、s:6:"日本"; のような PHP のシリアライズ形式で保存します。数字は文字数ではなくバイト数です。化けた文字を元に戻すと長さが合わなくなり、WordPress はその値を破棄します。そのため、まず wp_posts、wp_comments、wp_terms などの本文系テーブルを修復してください。オプション類は、長さも更新してくれる WP-CLI の wp search-replace が適しています。
MySQL 側で原因を直す
- テーブルと接続を
utf8mb4に切り替え、wp-config.phpにdefine('DB_CHARSET', 'utf8mb4');を設定します。 latin1の列に UTF-8 のバイト列が入っている場合、そのまま変換してはいけません。もう一度エンコードされてしまいます。一般的には、まずMODIFY … LONGBLOB、次にMODIFY … LONGTEXT CHARACTER SET utf8mb4とバイナリ型を経由します。- すべての変更は、まずデータベースのコピーで試してください。
よくある質問
WordPress のデータベース全体を一度に直せますか?
はい、大きな SQL ダンプでも処理できます。ただし wp_options や wp_postmeta のシリアライズデータには注意が必要で、WP-CLI のほうが向いています。
ダンプ内の正しい文字が変わることはありますか?
ありません。Mojibuster は明らかに壊れた並びだけを修復し、正しい文字はそのまま残します。
utf8 と utf8mb4 の違いは何ですか?
MySQL の utf8 は最大3バイトまでの文字しか保存できず、絵文字などが失われます。utf8mb4 が本来の UTF-8 で、WordPress にはこちらが適切です。
WordPress 以外でも使えますか?
はい。Joomla、Drupal、Magento、MySQL や MariaDB 上の自社システムでも、同じ問題と同じ対処法が当てはまります。