laravel插入四字节unicode字符报错需全链路统一utf8mb4:mysql服务端、laravel连接配置、已有表结构、新建迁移、php及redis均需校验并强制设置为utf8mb4,缺一不可。

当Laravel应用尝试插入䶮、?、?这类四字节生僻汉字或emoji时,数据库报错SQLSTATE[HY000]: General error: 1366 Incorrect string value,说明当前字符集链路中至少有一环仍停留在utf8(即utf8mb3),无法承载4字节Unicode字符,必须逐层校验并强制统一为utf8mb4。
确认MySQL服务端是否真正支持utf8mb4
进MySQL命令行执行:SHOW VARIABLES LIKE 'character_set%';
重点检查character_set_server和character_set_database两项——若其中任一值不是utf8mb4,说明服务端未生效。
修改/etc/mysql/my.cnf(Linux)或C:\my.ini(Windows)的[mysqld]段,追加两行:character-set-server = utf8mb4collation-server = utf8mb4_unicode_ci
【重启MySQL服务后才生效,不重启配置只是纸面设置】
重启后再次执行SHOW VARIABLES验证,两项必须同时为utf8mb4。
强制Laravel连接层使用utf8mb4
打开config/database.php,定位到'mysql'配置块:
确保包含以下三项,缺一不可:
'charset' => 'utf8mb4'
'collation' => 'utf8mb4_unicode_ci'
'options' => [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"]
⚠️注意:.env里的DB_CHARSET=utf8mb4是无效配置,Laravel官方源码根本不读这个变量,删掉免得误导。
这一步做完后,用php artisan tinker执行DB::connection()->getPdo()->getAttribute(PDO::ATTR_CLIENT_VERSION),确认PDO连接已携带utf8mb4上下文。
升级已有表与字段的字符集
即使连接和库都设对了,旧表仍是utf8建的,字段字节长度也不够——必须手动转换:
第一步:执行ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
第二步:单独检查TEXT、VARCHAR字段是否被遗漏,用SHOW FULL COLUMNS FROM users;核对每列的Collation列,非utf8mb4_unicode_ci的要单独改:ALTER TABLE users MODIFY name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
第三步:索引字段长度超限会失败,先在AppServiceProvider::boot()里加Schema::defaultStringLength(191);,再跑迁移。
这三步做完,SELECT LENGTH('?') FROM users;应返回4,而非报错或截断。
验证迁移文件是否生成utf8mb4表结构
Laravel 9.x之前版本的Schema::create()默认忽略全局charset配置,新建表仍用utf8——这是最隐蔽的漏点。
方法一:在迁移文件末尾硬编码执行SQL:DB::statement("ALTER TABLE posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
方法二:显式声明字段字符集:$table->string('title')->charset('utf8mb4');
方法三(推荐):在迁移文件开头加DB::statement("SET NAMES utf8mb4");,确保migration命令行进程也走utf8mb4通道。
运行php artisan migrate:fresh后,用SHOW CREATE TABLE posts;确认建表语句含ENGINE=InnoDB DEFAULT CHARSET=utf8mb4。
排除PHP与Redis的干扰项
如果用了Redis缓存中文,且Redis客户端未指定编码,生僻字可能在序列化时被破坏。在config/database.php的redis配置里加:'options' => ['prefix' => 'laravel_database:', 'serializer' => Redis::SERIALIZER_PHP],
PHP本身需启用mbstring扩展,否则mb_strlen('䶮')会返回1而非1(此处无误,该字确实是单字符),但json_encode等函数可能出错。运行php -m | grep mbstring确认已加载。
最后,在控制器中写一句测试代码:dd(DB::table('users')->insert(['name' => '䶮']));——成功返回true即表示全链路打通。











