PHP × MySQL環境で「General error: 1366 Incorrect string value」エラーが出た場合

全然違うアプローチする愚か者の図

最悪や…orz
まる一日ハマってしまったので、ちょっとまとめておきます。

出だしから分かるように原因はしょうもない事でした。

あ、わかりやすく言うと、上図のような状況でした。

やろうとしたこと

Laravel環境で、特定のWebサイトをクロールしてスクレイピングする処理を作ってた。

スクレイピングの内容は単純で、URIと、title、descriptionなど特定の要素やプロパティを抜く程度。

で、集めたデータをMySQL DBに格納すると。

ある程度はすんなりDBに格納できたんだけど、いくつかのページでDBエラーが発生した。

DBエラーの内容

General error: 1366 Incorrect string value: '\xE3\x82' for column ...

環境について詳しく

対象の環境は以下の通り。Laradock使ってます。

  • Windows 10
  • Docker Desktop for Windows(Hyper-V)
  • Laravel 5.7.28
  • PHP 7.3.23
  • MySQL 5.7.31(charset:utf8)

ここで問題です

見る人が見たらすぐ分かるかも。

Q. なぜ上記のようなエラーになってしまったんでしょうか?

ここで問題です

ヒント(確認したこと試したこと)

1. MySQLの文字コードを変更

とりあえずエラーメッセージでググると、MySQLのcharsetいじれ的な記事が多く出てきたので確認してみた。

mysqlでstatusを見ると、Server、Dbのcharctersetはutf8で、Client、Conn.はlatin1。(Laradockのデフォルト)

my.cnfを修正して、すべてのcharsetをutf8mb4に合わせてみた。

→ 結果:解決せず

2. 照合順序を変更

エラーが発生した対象DBのmigrationファイルに以下を追記して照合順序を「utf8_general_ci」に変更してみた。

$table->collation = 'utf8_general_ci';

→ 結果:解決せず

3. そもそものデータをもう少し調べる

実行されるSQLをクライアントツールで動かしてみたら、なぜか登録は出来ていたのでサーバの設定の問題かと思ってたんだけど、よく見ると最後の1文字が文字化けしてた…。

答え合わせ

はい、しゅ~りょ~!

ほんとしょうもない理由でしたが、今回の原因は↓でした。

DB挿入時の処理

// スクレイピング処理後
$title = $page_info->title;
$uri = $page_info->uri;
$content = trim(strip_tags($page_info->content));
$description = substr($content, 0, 120);

// DB格納処理
$this->insertDB($title, $uri, $description, $content);

諸悪の根源はソースでした。どちくしょう。

問題の箇所はこちら。

substr($content, 0, 120);

マルチバイトデータに対してsubstrで部分抽出していた。
最後の文字の文字コード途中で切られていたので、おかしくなってただけだった。

mb_substr($content, 0, 120, 'utf-8');

mb_substrに変更したあげく、文字コードまでつけてやった。

くそう…自分の愚かさがうらめしい…

投稿者: Output48

中学生の時に初めてHTMLに触れてからホームページ制作を独学で始める。 ベンチャー企業の営業、大手企業のPG・SEを経て、独立。 現在はとある企業のCTOと、変な名前の会社の社長をしてる。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください