根本原因是输出流与接收端编码协商失败;PHP需确保文件UTF-8无BOM,CLI终端设UTF-8编码,Web端header+meta双声明,服务器禁用gzip等缓冲,避免误用转码函数。
echo 或 print 实时输出中文变问号或方块根本原因不是 PHP 本身,而是输出流(stdout)和接收端(浏览器 / 终端 / curl)之间的编码协商没对上。PHP 脚本里用 header('Content-Type: text/html; charset=utf-8') 只影响 HTTP 响应头,对 CLI 模式或 ob_flush()+flush() 实时推送无效。
常见现象:echo "你好"; 在浏览器里显示为 ,在终端里是空格或乱码;用 curl -N http://localhost/test.php 看到的是 UTF-8 字节但没解码。
chcp 65001,PowerShell 则要设 $OutputEncoding = [System.Text.UTF8Encoding]::new()
header(),还要确保 HTML 的 存在,且放在 最前面ob_flush() + flush() 实时输出中文失败这两个函数只是把 PHP 输出缓冲区内容推给 Web 服务器(如 Apache/Nginx),但服务器自身还有缓冲层,且可能做字符转码。中文乱码往往卡在这一步之后。
mod_deflate 未启用(它会压缩并破坏流式响应),或在 .htaccess 加 SetEnv no-gzip 1
gzip 和 fastcgi_buffering,配置段加 gzip off; fastcgi_buffering off;
ob_start() 前加 mb_internal_encoding('UTF-8');,避免多字节函数误判编码usleep(10000);(10ms),防止 TCP 包过小被合并,导致浏览器收不到完整 UTF-8 序列iconv() 或 mb_convert_encoding() 强制转码反而更乱这类函数只改字符串内容,不改传输协议层面的声明。如果原始字符串已经是 UTF-8,再用 iconv('UTF-8', 'UTF-8', $str) 不仅多余,还可能因参数错误触发静默失败。
mb_detect_encoding($str, ['UTF-8', 'GB2312', 'GBK'], true) 查原始编码,别凭感觉猜mb_convert_encoding($content, 'UTF-8', 'GBK')
$_GET/$_POST 直接转码——现代 PHP(7.2+)已默认按 default_charset 解析,改 php.ini 中的 default_charset = "UTF-8" 更安全说明响应头和实际字节不一致。打开 Network 面板,点开请求 → Headers → Response Headers,确认 Content-Type 是 text/html; charset=utf-8(注意分号后有空格)。如果看到 charset=gbk 或压根没这个 header,问题就在这里。
header() 必须在任何输出前调用,包括空格、BOM、echo ""
headers_sent($file, $line) 检查是否已发 header,
header() 会警告,应改用框架提供的响应构造方式curl -i http://localhost/test.php 看原始响应头和 body 字节,再用 xxd 或 hexdump -C 检查中文是否为合法 UTF-8 编码(如“你好”应为 e4-bd-a0 e5-a5-bd)。只要字节对了,问题一定出在接收端解码环节。