PHP 8.1+乱码主因是mbstring未启用或配置失效、mysqli默认字符集不继承、rawurlencode编码逻辑变化及BOM污染;需启用mbstring、显式设字符集、校验UTF-8编码并清除BOM。

PHP 版本升级(尤其是从 7.x 到 8.0+)后出现乱码,通常不是“版本不兼容”本身导致的,而是新版默认行为更严格、旧写法被废弃或隐式转换失效所致。关键不是降级,而是补全缺失的编码控制点。
PHP 8.1+ 中 mbstring 默认未启用或配置失效
PHP 8.0 起,mbstring 扩展虽仍默认编译进核心,但部分 Linux 发行版包管理器(如 Ubuntu 的 php8.1 包)可能未自动启用它;若 php.ini 中注释了 extension=mbstring 或漏配 mbstring.internal_encoding,所有 mb_* 函数会退化为单字节处理,中文直接截断或转成 。
- 运行
php -m | grep mbstring确认是否加载;没输出就编辑php.ini,取消;extension=mbstring前的分号 - 在脚本开头加
mb_internal_encoding('UTF-8'),不要依赖 php.ini 的全局设置——PHP 8.1+ 对 ini 值的继承更脆弱 - 检查
mbstring.func_overload是否为 0(必须为 0;设为非 0 会覆盖原生strlen等函数,极易引发乱码+崩溃)
mysqli 默认字符集不再继承 mysql.default_charset
PHP 8.1 废弃了 mysql.default_charset 配置项,且 mysqli_connect() 不再隐式调用 SET NAMES。即使数据库是 utf8mb4,连接一建立就是 latin1,查出来的中文全变问号。
- 连接后必须显式执行
mysqli_set_charset($conn, 'utf8mb4')——mysqli_query($conn, "SET NAMES utf8mb4")在 PHP 8.1+ 已不推荐,且不保证生效 - PDO 用户需在 DSN 中强制指定:
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8mb4';,仅靠setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES utf8mb4")不可靠 - 用
mysqli_get_charset($conn)检查实际连接字符集,别信配置文件里的“应该”
rawurlencode() 和 header() 中 filename* 的编码逻辑变化
PHP 8.1 对 rawurlencode() 处理非 ASCII 字符更严格:它现在严格按 UTF-8 字节序列编码,而旧版(尤其 Windows 下)可能误将 GBK 字节当 UTF-8 编。下载中文名文件时,Content-Disposition 头里 filename*=UTF-8''... 部分若原始字符串不是 UTF-8,就会生成非法编码,浏览器直接拒解析。
立即学习“PHP免费学习笔记(深入)”;
- 确保传给
rawurlencode()的文件名变量本身就是 UTF-8 编码——用mb_detect_encoding($name, ['UTF-8', 'GB2312'], true) === 'UTF-8'校验 - 若来源不可控(如 $_GET 参数),先转码:
$name = mb_convert_encoding($name, 'UTF-8', 'auto'),再rawurlencode() - 绝对不要用
urlencode()替代rawurlencode():前者把空格转成+,RFC 5987 明确要求用%20
最易被忽略的是:PHP 8.1+ 不再容忍 BOM。哪怕文件开头只有 \xEF\xBB\xBF 三个字节,header() 就会报 headers already sent,导致后续 Content-Type 失效——此时页面看似“有 UTF-8 meta”,实则 HTTP 响应头仍是 text/html 无 charset,浏览器按默认 latin1 解析,中文全乱。务必用 file -i your.php 检查,而非只看编辑器右下角显示。



















