database.php中的'charset'配置基本无效,必须在DSN中显式写死charset=utf8mb4;MySQL服务端需设character_set_server=utf8mb4并重启,已有表须手动ALTER CONVERT TO utf8mb4;还需在params中添加PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true防2014错误。

database.php 里的 'charset' 配置基本无效
ThinkPHP 5/6 的 'charset' => 'utf8mb4' 写在 database.php 的连接配置里,**只在 DSN 没显式声明 charset 时才起作用**;一旦你写了 dsn 字段(哪怕为空字符串),这个配置就完全被忽略。更麻烦的是,某些旧版 mysqlnd 驱动压根不识别它。
常见错误现象:Db::query('SELECT @@character_set_client') 返回 utf8 而非 utf8mb4,插入 emoji 报 SQLSTATE[HY000]: General error: 1366。
- 删掉所有形如
'charset' => 'utf8'或'charset' => 'utf8mb4'的行,避免干扰 - 改用
'dsn' => 'mysql:host=127.0.0.1;dbname=test;charset=utf8mb4'显式写死 - 别用
utf8—— MySQL 的utf8是阉割版,最多存 3 字节,\xF0\x9F\x90\xA3这类四字节字符必然变???
DSN 字符集必须写全且位置正确
DSN 不是可选字段,它是 PDO 连接的底层入口;charset=utf8mb4 必须作为 DSN 查询参数的一部分,紧跟在 dbname=xxx 后面,用分号分隔,不能漏、不能错拼、不能放错位置。
使用场景:多环境部署、CI/CD 自动化配置、Docker 容器内连接。
- ✅ 正确:
'dsn' => 'mysql:host=localhost;dbname=myapp;charset=utf8mb4' - ❌ 错误:
'dsn' => 'mysql:host=localhost;dbname=myapp; charset=utf8mb4'(分号后空格) - ❌ 错误:
'dsn' => 'mysql:host=localhost;dbname=myapp#utf8mb4'(用井号是 ThinkPHP 早期 DSL 语法,已废弃) - ❌ 错误:
'dsn' => 'mysql:host=localhost;dbname=myapp;collation=utf8mb4_unicode_ci'(PDO 不认 collation 参数)
MySQL 服务端字符集必须同步配
哪怕 DSN 写对了 charset=utf8mb4,只要 MySQL 的 character_set_server 还是 latin1 或 utf8,新建表/字段默认就会继承错误编码,插入四字节字符直接失败——这不是 PHP 层能绕过去的。
验证命令:SHOW VARIABLES LIKE 'character\_set\_server';,结果必须是 utf8mb4。
- Linux 下修改
/etc/my.cnf,在[mysqld]段加:character-set-server = utf8mb4和collation-server = utf8mb4_unicode_ci - Windows 下改
my.ini,同样加这两行 - 改完必须重启 MySQL 服务,否则不生效
- 已有库和表需单独升级:
ALTER DATABASE myapp CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;,再对每张表执行ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
连接层缓冲未启用会触发 SQLSTATE[HY000] General error: 2014
这个错误常被误认为是字符集问题,其实是 MySQL 原生“未缓冲查询”机制冲突:在一个没取完结果集的查询上又发新查询(比如循环中嵌套查表),PDO 默认不缓存结果,服务端还拿着前一个游标,就报错。
性能影响:开启缓冲会略微增加内存占用,但换来的是稳定性和兼容性,值得。
- 在
database.php的连接配置'params'数组里加:PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true - ⚠️ 注意:键名必须是常量全名,不是字符串,写成
'MYSQL_ATTR_USE_BUFFERED_QUERY'会静默失效 - 如果已设
PDO::ATTR_EMULATE_PREPARES => true,它也能间接缓解,但不如直接开缓冲可靠 -
fetchAll()只是临时补救,不能替代连接层配置
真正要命的点不在 PHP 配置里,而在 MySQL 服务端默认值和建表语句里——哪怕 DSN 和 PHP 层全对了,CREATE TABLE 时没指定 CHARACTER SET utf8mb4,这张表就还是 utf8 编码。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











