filter_var() 是php最稳的基础类型验证方案,它通过预设语义规则校验邮箱、url、整数等,比正则更安全省心,且自动处理空格与编码边界;需区分验证与转义,键名也须白名单校验。

用 filter_var() 做基础类型验证最稳
PHP 自带的 filter_var() 是处理用户输入的第一道防线,不是靠正则硬刚,而是用预设规则做语义校验。比如邮箱、URL、整数这些常见类型,它比手写 preg_match() 更安全、更省心,还自动处理空格和编码边界问题。
常见错误现象:$_GET['id'] 看似是数字,但实际可能是 "123abc" 或 " 456 ",直接转 (int) 会静默截断成 123 或 456,丢失非法后缀却没报错。
- 验证整数用
filter_var($input, FILTER_VALIDATE_INT),它拒绝"123abc",也拒绝"12.3" - 验证邮箱用
filter_var($input, FILTER_VALIDATE_EMAIL),它不保证邮箱真实存在,但能筛掉明显格式错误(如缺少 @、双点号) - 验证 URL 要加
FILTER_FLAG_PATH_REQUIRED参数才要求有路径,否则"http://example.com"和"http://"都过 - 注意:默认不 trim,
" test@example.com "会验证失败,得先trim()
过滤输出前必须区分「验证」和「转义」
验证(validate)是判断输入合不合规则;转义(escape)是让数据在特定上下文里安全渲染。很多人把两者混为一谈,结果该拦的没拦住,该转的又漏了。
使用场景:用户提交昵称,要存进数据库、显示在 HTML 页面、再放进 JS 字符串里 —— 这三处需要三种不同处理。
立即学习“PHP免费学习笔记(深入)”;
新版本程序更新主要体现在:完美整合BBS论坛程序,用户只须注册一个帐号,即可全站通用!采用目前流行的Flash滚动切换广告 变换形式多样,受人喜爱!在原有提供的5种在线支付基础上增加北京云网支付!对留言本重新进行编排,加入留言验证码,后台有留言审核开关对购物系统的前台进行了一处安全更新。在原有文字友情链接基础上,增加LOGO友情链接功能强大的6种在线支付方式可选,自由切换。对新闻列表进行了调整,
- 入库前用
filter_var($name, FILTER_SANITIZE_STRING)已废弃,改用filter_var($name, FILTER_SANITIZE_FULL_SPECIAL_CHARS)(等价于htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE)) - HTML 输出时不能只靠入库时的过滤,必须在
echo前再过一遍htmlspecialchars(),因为同一变量可能被多次输出到不同位置 - JS 上下文中不要拼接 PHP 变量,改用
json_encode($value, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG)输出到<script></script>标签内 - 数据库查询务必用 PDO 预处理,别用
filter_var()“过滤完就放心拼 SQL”
$_POST 和 $_GET 的键名本身也要检查
攻击者可能伪造任意字段名,比如传入 user[admin]=1 触发数组覆盖,或用 GLOBALS[db_host]=xxx 尝试污染超全局变量(虽在现代 PHP 中已禁用,但旧配置仍可能风险)。
容易踩的坑:只校验值,忽略键名合法性。尤其当代码用 foreach($_POST as $k => $v) 批量赋值对象属性时,危险指数拉满。
- 白名单优先:明确列出允许接收的键名,如
$allowed_keys = ['username', 'email', 'age'];,然后用array_intersect_key($_POST, array_flip($allowed_keys)) - 拒绝点号、方括号、大写字母等非常规字符出现在键名中,可用
preg_match('/^[a-z_][a-z0-9_]*$/', $key)简单筛查 - 如果用了框架(如 Laravel),它的
request()->validate()默认只校验值,键名校验仍需手动加逻辑或中间件 - PHP 配置项
magic_quotes_gpc已移除,不用再写兼容代码,但老项目迁移时要确认是否残留相关清理逻辑
自定义验证逻辑别绕开 filter_var() 的底层机制
遇到手机号、身份证、银行卡号这类业务规则,有人直接上正则,但正则难维护、难覆盖边界(比如新版身份证末位 X 大小写、19 位银行卡号支持情况),不如在 filter_var() 基础上叠加校验。
性能影响:纯正则匹配比 filter_var() 快一点,但差异在微秒级,远不如一次 DB 查询或网络请求耗时,别为这点速度放弃可读性和健壮性。
- 手机号先用
filter_var($phone, FILTER_VALIDATE_INT)排除非数字,再用 strlen() 判长度,比一长串正则更易懂 - 身份证号用
ctype_alnum()确保只有字母数字,再单独校验最后一位,避免正则里写满 18 个[0-9Xx] - 所有自定义函数必须返回布尔值,且对空字符串、
null、0等边缘值有明确定义,别让empty()成为验证依据 - 别在验证函数里做数据库查重(如“用户名是否已存在”),那是业务逻辑层的事,验证层只管格式和基本约束
真正麻烦的是嵌套结构:比如 $_POST['profile']['address']['city'],深层键名 + 混合类型 + 可选字段。这时候光靠 filter_var() 不够,得组合 filter_input() 加递归校验,或者用现成的 egulias/EmailValidator 这类专注单一职责的库 —— 但前提是,你清楚它没在底层偷偷执行 eval() 或动态包含文件。










