必须在pdo dsn中显式指定charset=utf8mb4,仅靠pdo::mysql_attr_init_command设set names不够,否则emoji插入失败、中文被截断;同时须启用pdo::errmode_exception以捕获详细错误。

pdo连接时必须显式设charset=utf8mb4在DSN里
只靠PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4'不够,emoji插入失败、中文被截断就是典型表现。MySQL服务端默认字符集是latin1或utf8(非utf8mb4),光靠运行时执行SET NAMES无法改变连接层的初始编码协商。
-
dsn字符串中必须包含charset=utf8mb4,例如:"mysql:host=127.0.0.1;port=3306;dbname=test;charset=utf8mb4" - 如果用环境变量拼接DSN,确保
$charset变量值是utf8mb4,不是utf8或空字符串 - PHP 8.5.5 不会自动补全或降级处理这个参数,漏写就按服务端默认值走,后果静默且难排查
必须开启PDO::ERRMODE_EXCEPTION
不设这个选项,PDO::query()或PDOStatement::execute()出错时只返回false,fetch()返回null或空数组,没有任何提示。你看到“查不到数据”,实际可能是密码错、库不存在、表名拼错,但代码照常往下跑。
- 务必在
new PDO()的第四个参数(options数组)里写:PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION - 不要用
setAttribute()后设,构造时没开,后面设无效 - 配合
try-catch捕获PDOException,错误信息里会带具体SQL状态码(如HY000、42S02),比mysqli_error()更准
MySQLi连接后要立刻调用set_charset()
MySQLi不支持在构造函数里传字符集,new mysqli($host, $user, $pass, $db)完成的是TCP连接和认证,字符集还是服务端默认值。不手动设,一样乱码或截断。
- 连接成功后立即执行:
$mysqli->set_charset('utf8mb4') - 不能写成
$mysqli->set_charset('utf8')——MySQL的utf8只支持3字节,不支持emoji - 如果用面向对象方式,
$mysqli对象必须存在且有效,否则call to a member function set_charset()报错
持久连接(ATTR_PERSISTENT)要慎用
开PDO::ATTR_PERSISTENT => true看起来省资源,但PHP-FPM下容易引发连接状态残留:前一个请求开了事务没提交、建了临时表、锁了行,下一个请求复用该连接就会直接继承这些状态,导致数据异常或死锁。
- 仅在明确需要且能控制连接生命周期的场景用(如CLI脚本、长时守护进程)
- Web请求中优先用短连接,让连接池由数据库代理(如ProxySQL)或连接管理器(如PgBouncer)管
- 若真要用,确保每个请求结束前显式
$pdo = null,并清掉所有prepared statement引用
charset=utf8mb4——它不在配置文件里,也不在框架默认值里,得你亲手敲进去。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











