PHP 7.2老项目报“Server sent charset unknown”或中文乱码,根因是MySQL 8.0默认utf8mb4与caching_sha2_password认证、老版mysqli扩展及未对齐的五层字符集(client/connection/results/table/column)脱节;须立即执行mysqli_set_charset($conn, 'utf8mb4')、修正my.cnf、升级表结构、清理PHP文件UTF-8无BOM编码,并启用mbstring。

PHP 7.2 老项目运行时突然报错“Server sent charset unknown to the client”或页面中文显示为问号、方框、乱码,不是代码逻辑问题,而是MySQL连接层与字符集声明彻底脱节——尤其在升级过MySQL 8.0或迁移服务器后,老配置会直接失效。
确认错误类型和当前字符集状态
先用命令行快速定位问题根源:在PHP脚本开头插入以下调试代码并访问页面:
var_dump(mysqli_get_charset($conn));
echo "MySQL server version: " . mysqli_get_server_info($conn);
?>
如果输出为bool(false)或显示charset => latin1,说明连接根本没成功协商utf8mb4;若报“unknown charset”,则证明MySQL服务端发来的字符集ID PHP不认识——这通常发生在MySQL 8.0默认用caching_sha2_password插件+老版mysqli扩展未更新的组合下。
立即学习“PHP免费学习笔记(深入)”;
修复MySQL连接层字符集声明
老项目常用mysqli面向过程写法,必须在建立连接后、执行任何SQL前立即设置字符集:
方法一:使用mysqli_set_charset()(推荐)
mysqli_set_charset($conn, 'utf8mb4');
方法二:执行SET NAMES语句(兼容性更强)
mysqli_query($conn, "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci");
注意:这两步不能放在mysql_connect()之后就立刻执行——必须等$conn变量真正初始化成功再调用,否则会静默失败。
方法三:改用PDO(适合有重构条件的模块)
$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8mb4", $user, $pass);
同步修正数据库对象层的字符集配置
仅改连接不够,老项目常在建表SQL里写死utf8,而MySQL 5.7+已将utf8视为别名,实际映射为utf8mb3(不支持emoji)。必须逐个检查并升级:
第一步:导出原表结构
mysqldump -d -u root -p your_db > schema.sql
第二步:全局替换utf8为utf8mb4(注意保留utf8mb4本身不替换)
sed -i 's/CHARSET=utf8/CHARSET=utf8mb4/g' schema.sql
sed -i 's/DEFAULT CHARSET=utf8/DEFAULT CHARSET=utf8mb4/g' schema.sql
第三步:执行修正后的建表语句前,先确保MySQL服务端配置已启用utf8mb4:
在my.cnf中确认存在:
[client]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
第四步:对已有数据表执行ALTER升级(关键!很多老项目漏掉这步):
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
清理PHP文件自身编码污染
老项目常混入GBK保存的PHP文件,导致解析时字节流错乱。用file命令批量扫描:
find . -name "*.php" -exec file -i {} \; | grep -v "charset=utf-8"
发现含gbk/iso-8859-1的文件,用iconv转换:
iconv -f GBK -t UTF-8 input.php > output.php && mv output.php input.php
特别注意:所有PHP文件必须保存为UTF-8无BOM格式——VS Code右下角点编码→“Save with Encoding”→选UTF-8(不含BOM),否则header()会直接报“Headers already sent”。
强制统一PHP运行时内部编码
老项目往往没启用mbstring,导致substr、strlen等函数按字节切分中文,引发截断乱码。在入口文件index.php最顶部加入:
if (function_exists('mb_internal_encoding')) {
mb_internal_encoding('UTF-8');
}
同时检查php.ini中是否启用扩展:
extension=mbstring
mbstring.internal_encoding = UTF-8
mbstring.http_output = UTF-8
修改后重启PHP-FPM或Apache服务,否则配置不生效。



















