必须将 charset 配置项设为 utf8mb4,不能写 utf8 或留空;需同步确保 mysql 服务端 character_set_server 为 utf8mb4、连接后执行 set names utf8mb4、表结构显式声明 utf8mb4 字符集与 collation。

必须把 charset 配置项设为 utf8mb4,不能写 utf8,也不能留空——否则中文、emoji 一存就变问号或静默截断。
ThinkPHP6 数据库配置里 charset 写 utf8 就会乱码
MySQL 的 utf8 是个历史包袱,实际只支持最多三字节的 UTF-8 字符(即 utf8mb3),而中文生僻字、微信昵称、emoji 等四字节字符会被直接丢弃或替换为 ?。ThinkPHP6 的 database.php 配置中,'charset' => 'utf8' 看似合理,实则无效。
实操建议:
- 把配置里的
'charset' => 'utf8'改成'charset' => 'utf8mb4' - 确认 MySQL 服务端已启用
utf8mb4:执行SHOW VARIABLES LIKE 'character_set_server';,值必须是utf8mb4;没生效就改my.cnf并重启mysqld - TP6 默认不会在连接后自动执行
SET NAMES utf8mb4,所以光改配置不够,还得确保连接建立后立刻生效
连接建立后没执行 SET NAMES utf8mb4 也会乱码
即使配置写了 utf8mb4,如果 TP6 在底层连接初始化时没发这条指令,PDO 或 mysqli 仍可能沿用服务器默认(比如 latin1),导致 INSERT 截断、SELECT 出来是乱码。
实操建议:
- 在
database.php的'deploy' => 0模式下,加'trigger_sql' => true并观察日志,确认有没有SET NAMES执行 - 更可靠的做法:在
app/common.php或数据库连接回调中手动触发,例如:Db::connect()->execute('SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'); - 若用了多库或自定义连接,每个连接实例都得单独设置,别指望全局配置自动透传
迁移文件新增字段仍用旧字符集?因为没显式指定 collation
TP6 的 Schema::table() 或 Schema::create() 不会继承你配的 charset,也不会自动补 COLLATE。如果原表是 utf8,新增字段默认还是 utf8,哪怕你连的是 utf8mb4 连接。
实操建议:
- 建新表时必须链式调用:
->engine('InnoDB')->charset('utf8mb4')->collation('utf8mb4_unicode_ci') - 改已有表字段:不用
Schema::table(),改用原生语句:Db::statement("ALTER TABLE `users` MODIFY `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci") - 整表升级更彻底:
Db::statement("ALTER TABLE `users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"),注意带CONVERT TO,否则 TEXT、索引字段可能不更新
最容易被忽略的是:MySQL 服务端配置、连接层配置、表结构定义这三层必须全部对齐为 utf8mb4 + utf8mb4_unicode_ci,缺一不可。只改 PHP 配置或只改表结构,都会在某个环节掉字节。尤其是生产环境跑过一段时间的老表,别信 SHOW CREATE TABLE 输出的默认值——要逐字段看 CHARACTER SET 和 COLLATE 实际声明。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











