thinkphp 5.1+ 不自动转码,$_get/$_post 为原始字节流,需在 app/common.php 中用 request::bind() 预先转码 gbk 请求为 utf-8,再统一处理验证、数据库等环节。

ThinkPHP 5.1+ 默认不自动转码,$_GET 和 $_POST 是原始字节流
ThinkPHP 自身不干预 PHP 的原始输入,$_GET、$_POST、input() 返回的值就是 PHP 接收到的原始字节——它不会帮你把 GBK 请求转成 UTF-8。如果你的前端表单是 charset=gbk,而后端没处理,input('name') 拿到的就是乱码字节串,后续 JSON 输出、数据库写入、模板渲染全会出问题。
常见错误现象:input('title') 显示为 “æä¸ªæ±å” 或 “????”,但 mb_detect_encoding(input('title')) 返回 false 或 ASCII;用 iconv('GBK', 'UTF-8//IGNORE', input('title')) 却报“illegal character”。
- 必须先确认请求的真实编码:查
$_SERVER['CONTENT_TYPE']是否含charset=gbk,或用mb_detect_encoding(file_get_contents('php://input'), ['GBK', 'UTF-8'], true)粗略试探(仅限 POST body) - 不要在全局中间件里无差别
iconv('GBK', 'UTF-8', $str)—— UTF-8 请求会被毁掉 - ThinkPHP 的
Request::instance()->charset是只读配置项,不反映真实请求编码,别信它
用 Request::bind() 替换原始 $_POST 和 $_GET,实现按需转码
ThinkPHP 允许你在应用初始化阶段重绑请求数据源。这是最干净的方式:让框架从一开始就读取已转码后的数据,后续所有 input()、param()、验证器、模型赋值都自动生效,不用每个地方手动 iconv。
操作路径:app/common.php 或 app/provider.php 中添加:
// 示例:只对来自特定 Referer 或 User-Agent 的 GBK 请求转码
if (isset($_SERVER['HTTP_ACCEPT_CHARSET']) && false !== stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'gbk')
|| isset($_SERVER['HTTP_REFERER']) && preg_match('/\.old-system\.com$/i', $_SERVER['HTTP_REFERER'])) {
$_GET = array_map(function($v) {
return is_string($v) ? iconv('GBK', 'UTF-8//IGNORE', $v) : $v;
}, $_GET);
$_POST = array_map(function($v) {
return is_string($v) ? iconv('GBK', 'UTF-8//IGNORE', $v) : $v;
}, $_POST);
\think\Request::bind(['get' => $_GET, 'post' => $_POST]);
}
-
iconv('GBK', 'UTF-8//IGNORE')中的//IGNORE很关键,否则遇到非法 GBK 字节直接抛 Warning - 不要用
mb_convert_encoding($str, 'UTF-8', 'GBK')—— 它对混合编码更敏感,容错差 - 如果接口同时接收 JSON(UTF-8)和 form-data(GBK),需单独解析
php://input并判断 Content-Type
validate() 前必须确保数据已转码,否则规则匹配和中文提示全失效
ThinkPHP 验证器默认按 UTF-8 解析正则、长度、中文字符计数。若传入的是 GBK 字节串,rule: 'chs' 会判失败,max:10 可能把一个汉字算成 2 个字符,regex:/^[\x{4e00}-\x{9fa5}]+$/u 直接不匹配。
- 验证器不能“事后补救”——
$validate->check($data)的$data必须是 UTF-8 字符串 - 不要在验证器的
scene或filter里做转码:时机太晚,且filter不作用于嵌套数组字段 - 若使用
Validate::make()独立实例,务必确认传入数据已是 UTF-8,别依赖input()原始返回值
数据库写入前不再需要二次转码,但要注意 MySQL 连接层的 charset
只要请求参数已在 Request::bind() 阶段转为 UTF-8,且数据库连接配置中 'charset' => 'utf8mb4' 正确设置,ThinkPHP 的 ORM 和 QueryBuilder 就不会再动编码。但容易被忽略的是:MySQL 驱动层可能仍按 latin1 解析包。
- 检查
config/database.php中'dsn' => 'mysql:host=...;charset=utf8mb4;'——charset必须显式写在 DSN 里,光靠'charset'配置项不够 - 执行
SHOW VARIABLES LIKE 'character_set%',确认character_set_client、character_set_connection、character_set_results全是utf8mb4 - 如果旧系统用
mysql_query("SET NAMES gbk"),必须删掉,它会覆盖 PDO 的 DSN 设置
真正麻烦的从来不是 ThinkPHP 怎么写,而是你没法控制上游发来的是什么编码,以及中间代理(Nginx、CDN、WAF)有没有悄悄改过 Content-Type 或丢掉 charset 参数。这些地方一漏,前面所有转码都白做。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










