filter_input() 是验证 $_GET 参数最稳妥的方法,它比类型强转更安全,能自动处理空值、null 和缺失键,并推荐使用 FILTER_VALIDATE_INT 等明确语义的过滤器。

用 filter_input() 验证 GET 参数最稳妥
PHP 原生的 filter_input() 是验证 $_GET 参数的首选,它比手写正则或类型强转更安全、语义更清晰,且自动处理空字符串、null 和缺失键的情况。
常见错误是直接用 $_GET['id'] 然后 (int) 强转——这会让 id=abc 变成 0,看似“有值”,实则丢失校验意图。
实操建议:
- 始终指定
FILTER_SANITIZE_NUMBER_INT(清理)或FILTER_VALIDATE_INT(验证),后者失败时返回false,可明确区分“非法”和“未传” - 验证整数时加
options限定范围,例如['options' => ['min_range' => 1, 'max_range' => 99999]] - 验证邮箱用
FILTER_VALIDATE_EMAIL,但注意它不校验 DNS 或 MX 记录,仅格式合法即可 - 对多值参数(如
?tag[]=php&tag[]=web),filter_input(INPUT_GET, 'tag', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY)才能正确提取数组
自定义规则必须先过滤再验证
当业务要求更复杂(比如“用户名只能是 3–16 位字母数字下划线,且不能以数字开头”),filter_input() 不够用,得组合使用过滤与正则。但顺序错了就白忙:先验证再过滤,可能让 user%20name 这类编码值直接进正则,导致匹配失败。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 先用
FILTER_SANITIZE_STRING(PHP 8.1+ 已废弃,改用FILTER_SANITIZE_FULL_SPECIAL_CHARS或手动urldecode()+htmlspecialchars())清理输入 - 再用
preg_match()判断,例如preg_match('/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/', $username) - 避免用
mb_ereg()—— 它在 PHP 8.0+ 已被移除,且性能差于preg_match() - 敏感字段(如
redirect_url)务必用filter_var($url, FILTER_VALIDATE_URL)+ 白名单域名检查,不能只信parse_url()
注意 $_GET 的隐式类型和编码陷阱
$_GET 里所有值都是字符串,哪怕 URL 写成 ?page=1,$_GET['page'] 也是 '1' 而非 1。更麻烦的是,浏览器对空格、中文等会做 URL 编码,PHP 默认解码一次,但双重编码(如 %2520)可能逃逸。
实操建议:
- 不要依赖
is_numeric($_GET['id'])—— 它会把'1e3'、'0x1F'当作合法数字,而业务通常只要十进制整数 - 对疑似编码过度的值,可用
urldecode()循环解码直到无变化,再校验(但需防 DoS,最多 2 层) - 调试时用
var_dump($_GET)看原始值,别只靠浏览器地址栏判断内容 - 如果框架(如 Laravel)已接管路由,别绕过它直读
$_GET,否则可能错过中间件做的统一清洗
错误提示要具体,但别泄露内部信息
验证失败时返回 400 Bad Request 比 500 更准确,但错误消息不能写“id must be integer”,攻击者会立刻知道你用了什么校验逻辑。
实操建议:
- 统一返回
400状态码,响应体用通用提示如{"error": "Invalid request parameters"} - 开发环境可额外记录详细日志:
"GET param 'id' = '{$_GET['id']}' failed FILTER_VALIDATE_INT" - 避免在错误中拼接用户输入,防止 XSS;即使返回 JSON,也要用
json_encode()转义 - 对高频非法请求(如连续 5 次
?id=abc),考虑限流而非仅返回错误
真正难的不是写一条 filter_input(),而是想清楚每个参数的语义边界:它该不该为空?是否允许负数?长度上限是多少?这些业务规则一旦模糊,再严密的代码也拦不住脏数据。











