根本原因是tp5.1中'database.php'的'charset'=>'utf8mb4'配置在手动定义dsn时被忽略,必须显式写入dsn:'dsn'=>'mysql:host=127.0.0.1;dbname=test;charset=utf8mb4';同时需执行set names utf8mb4、升级表/字段至utf8mb4,并统一php层mbstring编码。

ThinkPHP 5.1 中把 database.php 的 'charset' => 'utf8mb4' 配置好,却仍出现中文或 emoji 乱码,根本原因不是“配错了”,而是这个配置在多数情况下**根本没生效**——TP5.1 的数据库连接机制会绕过它,尤其当你手动写了 dsn 字段时,charset 项直接被忽略。
DSN 里没写 charset,配置就白设
TP5.1 使用 PDO 连接 MySQL 时,字符集必须在 DSN(数据源名称)中显式声明,否则 PDO 默认按 latin1 或服务端默认值建立连接。即使你写了 'charset' => 'utf8mb4',只要 DSN 字段存在且不含 charset=,该配置就会被跳过。
- 错误写法(charset 配置无效):
'dsn' => 'mysql:host=127.0.0.1;dbname=test;' - 正确写法(强制指定):
'dsn' => 'mysql:host=127.0.0.1;dbname=test;charset=utf8mb4;'
务必删掉 database.php 中所有 'charset' => ... 行,避免干扰;只靠 DSN 控制最可靠。
连接建好后没执行 SET NAMES,中间层还是 latin1
即使 DSN 带了 charset=utf8mb4,某些旧版 mysqlnd 驱动或服务器配置下,PDO 初始化后仍可能让 character_set_client、collation_connection 等会话变量保持为 latin1。这时插入中文,MySQL 会按 latin1 解释字节,导致存储乱码。
- 验证当前会话字符集:
SELECT @@character_set_client, @@collation_connection, @@character_set_results;
三者都必须是utf8mb4才算真正生效。 - 补救方法:在连接成功后立即执行
Db::connect()->execute('SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci');
可放在app/common.php或数据库连接回调中。
表和字段没升级到 utf8mb4,存不进四字节字符
MySQL 服务端、连接层都配对了,但若表结构仍是 DEFAULT CHARSET=utf8(即 utf8mb3),那 emoji、生僻汉字、韩文等四字节字符会被截断或报错,查出来就是 或问号。
- 检查建表语句:
SHOW CREATE TABLE your_table;
确认末尾是DEFAULT CHARSET=utf8mb4,且关键字段如VARCHAR显式带CHARACTER SET utf8mb4。 - 升级已有表:
ALTER TABLE your_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
注意:如果字段定义里硬写了CHARACTER SET latin1,CONVERT TO不会覆盖它,需单独改列:ALTER TABLE your_table MODIFY COLUMN content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
PHP 层 mbstring 没对齐,字符串操作自己搞乱
TP5.1 自身未全面使用 mb_* 函数,项目中若混用 substr()、strlen() 等原生函数处理中文,会导致截断、长度误判,进而影响分页、验证、JSON 输出等环节,表面像数据库乱码,实则是 PHP 内部处理出错。
- 入口文件开头加:
mb_internal_encoding('UTF-8'); - 禁用已废弃的重载:
在php.ini中注释掉mbstring.func_overload,并重启 PHP。 - 全局搜索替换:
substr(→mb_substr(…, 'UTF-8'),strlen(→mb_strlen(…, 'UTF-8'),
所有涉及中文字符串操作的地方都要显式传编码参数。











