ラボ > PHP:文字関連、MySQL

php,mysql 絵文字が文字化け / utf8mb4、utf8mb3、binary

絵文字をDBにつっこんだら「????」と文字化けする

作成日:2023-07-20, 更新日:2023-08-10

mySQLの「utf8」

utf8は基本、4バイト。でもmySQLでは3バイト。
mySQLの4バイトのutf8は「utf8mb4」ってヤツ。

mySQLでの型

  • utf8 - 3バイト(utf8mb3と同じ)
  • utf8mb4 - 4バイト
  • utf8mb3 - utf8と同じ(3バイト)

「utf8」→「utf8mb4」

いろいろと調べると…MySQLの文字コードを「utf8」から「utf8mb4」に変更すればいいとのコト

パターンとしては下記の3種類かと。

  • DB自体を「utf8mb4」にする
  • 対象テーブルのみ「utf8mb4」にする
  • 対象カラムのみ「utf8mb4」にする

特別な理由が無ければDBの文字コードを「utf8mb4」にするのが良さげ

文字化けするとき

「utf8mb4」にしても状況によっては文字化けすることがあるらしい。
そのときはinsert前にutf8mb4に追加してやれば良いとのトコ

▼PHP側でinsertする前に下記実行

set names utf8mb4

「utf8」→「binary」

対象カラムのみ「binary」にしてみたが…よくわからんが…ダメだった
DBをutf8mb4にすればいけるかもしれないし、特定の絵文字がダメだったかもしれない。未調査

「utf8」のままで保存

絵文字…というか文字をunicodeに変換してDBに保存する…という方針

一文字だけ

以下は、「絵文字一文字だけ」でもunicodeにすると2文字になる場合があるので、実質使えない…

$emoji = '😢';
$unicode_emoji = base_convert(bin2hex(mb_convert_encoding($emoji, 'UTF-32BE', 'UTF-8')), 16, 16);

// ▼絵文字に戻す
// echo mb_convert_encoding(hex2bin(str_repeat('0', 8 - strlen($unicode_emoji)).$unicode_emoji), 'UTF-8', 'UTF-32BE');
// echo '&#x' . $unicode_emoji . ';' // 絵文字に戻すならコッチでも良いような気がする

文字列

$emoji = '😢だよ';
$length = mb_strlen($emoji);
$unicode_many = [];
$conv_emoji = mb_convert_encoding($emoji, 'UTF-32BE', 'UTF-8');
for ($i = 0; $i < $length; ++$i) {
  $tmp_unicode = mb_substr(conv_emoji, $i, 1, 'UTF-32BE');
  $unicode_many[$i] = base_convert(bin2hex($tmp_unicode), 16, 16);
}

unicodeを文字列に戻す

文字に戻すなら上記の「$unicode_many」をループで回してやればOK

▼ループ内で一個ずつ文字に戻す

foreach ($unicode_many as $row_unicode) {
  echo mb_convert_encoding(hex2bin(str_repeat('0', 8 - strlen($row_unicode)).$row_unicode), 'UTF-8', 'UTF-32BE');
}

DBに保存

上記の「$unicode_many」をjsonにして保存すればOK
ただunicodeにしているので文字数的に…カラムの型を気にしなくちゃいけないかもしれない

関数化

// unicodeを文字(1文字)へ変換
function convert_toChar($unicode) {
  return mb_convert_encoding(hex2bin(str_repeat('0', 8 - strlen($unicode)).$unicode), 'UTF-8', 'UTF-32BE');
}

// unicodeを格納した配列を文字列へ変換
function convert_toString($unicode_many) {
  $string = '';
  foreach ($unicode_many as $row_unicode) {
    $string .= convert_toChar($row_unicode);
  }
  return $string;
}

// 文字(1文字)をunicodeへ変換
function convert_toUnicode($char) {
  return base_convert(bin2hex(mb_convert_encoding($char, 'UTF-32BE', 'UTF-8')), 16, 16);
}

// 文字列をunicodeへ変換
function convertString_toUnicode($string) {
  $length = mb_strlen($string);

  $ret = [];
  $s = mb_convert_encoding($string, 'UTF-32BE', 'UTF-8');
  for ($i = 0; $i < $length; ++$i) {
    $tmp_unicode = mb_substr($s, $i, 1, 'UTF-32BE');
    $ret[$i] = base_convert(bin2hex($tmp_unicode), 16, 16);
  }
  return $ret;
}

絵文字について

「utf8mb4」にすれば絵文字は基本、文字化けしないけど、特定の絵文字は文字化けする
さらにブラウザによって表示される・されない…ってのもある

▼一部だけ

  • 「🥹 涙をこらえている顔」の絵文字はFirefoxだと表示されるが、Chrome、Edgeは文字化け
  • 「😘 投げキッス」がFirefox、Chrome、Edgeで表示されるけど、utf8mb4にしたカラムに保存すると文字化け