ThinkPHP中$_GET键名被转义或丢失,根本原因是Web服务器(如Nginx/Apache)或PHP CGI层预处理导致QUERY_STRING在进入PHP前已被转义(如_→%5F),需通过var_dump($_SERVER['QUERY_STRING'])与var_dump($_GET)比对定位问题层,并检查Nginx merge_slashes、rewrite规则及Apache AllowEncodedSlashes配置。

ThinkPHP 默认会对 URL 参数中的下划线、点号、斜杠等字符做自动转换(比如 _ → %5F),这不是框架 bug,而是 Apache/Nginx 或 PHP CGI 层预处理导致的;真正要解决,得从请求链路源头入手,而不是在控制器里反复 urldecode()。
为什么 $_GET 里的键名被转义或丢失
常见现象是:前端传 ?user_name=abc,后端 input('user_name') 拿不到,或者 $_GET 里根本没这个 key。这是因为 Web 服务器(尤其是 Nginx)默认禁用 URI 中的某些字符,或 PHP 的 auto_globals_jit + request_order 配置异常,导致原始 QUERY_STRING 在进入 PHP 前就被截断或转义。
排查步骤:
- 先看原始请求串:
var_dump($_SERVER['QUERY_STRING']);—— 如果这里已经是user%5Fname=abc,说明问题出在 Nginx/Apache 层 - 再比对
var_dump($_GET);—— 如果二者不一致,基本可排除 ThinkPHP 自身逻辑 - 检查 Nginx 配置是否含
merge_slashes off;或错误的rewrite规则(尤其带break的) - 确认 Apache 是否启用了
AllowEncodedSlashes On(旧版默认 Off)
分页或搜索参数含中文/emoji 时 rawurlencode() 是刚需
手动拼接分页链接(如 ?q=北京&page=2)时,若直接用 urlencode(),空格会变成 +,而浏览器地址栏和 PHP 的 parse_url() 只认 %20;更糟的是,如果用户搜的是 a+b,urlencode() 输出 a%2Bb,但中间那个 + 在 GET 解析时又会被误认为空格。
立即学习“PHP免费学习笔记(深入)”;
正确做法:
- 所有动态参数值必须单独用
rawurlencode($value)编码,例如:'?q=' . rawurlencode($keyword) . '&page=' . $page - 绝不能对整个 query string 再套一次
rawurlencode(),否则&和=也被编码,链接彻底失效 - 前端 JS 用
encodeURIComponent(),行为与rawurlencode()一致,可混用
order/sort 字段名注入必须白名单校验,不能靠 addslashes()
分页常带 ?order=user_name&sort=desc,如果直接拼进 SQL:ORDER BY ' . input('order'),即使过滤了单引号也没用——攻击者可传 user_name, (SELECT password FROM users LIMIT 1),绕过所有字符串转义。
安全写法只有两条路:
- 严格白名单:
in_array(input('order'), ['user_name', 'created_at', 'status'], true) - 字段名正则限制:
preg_match('/^[a-zA-Z0-9_]{1,64}$/', input('order')),且必须配合数据库元数据校验(不能只信前端传来的字段名) - 绝对不要用
addslashes()、mysql_real_escape_string()或任何针对值的转义函数处理字段名——它们对 SQL 标识符无效
ThinkPHP 模板中 __URL__ 类常量不参与 URL 编码,需手动处理
__URL__、__ACTION__ 这些模板替换变量是纯路径拼接,不会对其中的中文或特殊字符做 rawurlencode()。比如你配置了路由规则支持中文控制器名,模板里写 <a href="__URL__/用户管理">,生成的 HTML 就是裸的中文路径,现代浏览器虽能勉强解析,但服务端收到的 $_SERVER['REQUEST_URI'] 可能已损坏。
稳妥方案:
- 避免在
__URL__后直接拼非 ASCII 路径,改用url()助手函数:{:url('User/管理')} - 若必须手拼,对外部变量部分用
rawurlencode()包裹,例如:<a href="__URL__/<?php echo rawurlencode($tab); ?>"> - Nginx 配置里加
charset utf-8;,并确保location块未启用任何 decode 类指令
最易被忽略的一点:很多开发者以为只要在 ThinkPHP 层调用 input() 就万事大吉,其实从浏览器发出请求到 PHP 接收这中间有至少三层(浏览器编码 → Web 服务器解析 → PHP CGI 解析),任一层出问题都会让特殊字符“消失”。盯住 $_SERVER['QUERY_STRING'],才是定位根源的唯一可靠方式。



















