Menu

PHP filter_var():验证邮箱、整数、URL 等

filter_var($value, FILTER_VALIDATE_EMAIL) 在值有效时返回该值,无效时返回 false;FILTER_VALIDATE_INT、FILTER_VALIDATE_URL、FILTER_VALIDATE_BOOL 和 FILTER_VALIDATE_IP 的用法相同。内容包括选项(min_range、default)、标志、净化过滤器,以及 false 与 0 的陷阱。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

当值是有效的邮箱地址时,filter_var($value, FILTER_VALIDATE_EMAIL) 返回该值,否则返回 false。换一个过滤器就能检查其他格式:FILTER_VALIDATE_INT、FILTER_VALIDATE_FLOAT、FILTER_VALIDATE_URL、FILTER_VALIDATE_BOOL、FILTER_VALIDATE_IP。结果始终要用 === false 比较。

有两个结果常让人意外。ada@example 被拒绝,是因为域名里没有点;' ada@example.com' 被拒绝,是因为开头有空格,所以验证之前要先用 trim() 处理输入。

验证表单中的邮箱

把同样的检查放到表单后面。运行它,输入一个地址并点击 Check;这段代码会带着你在 $_POST 中的输入再运行一次。

FILTER_VALIDATE_EMAIL 只检查格式。nobody@example.com 能通过,域名里有拼写错误的地址也能通过。非 ASCII 字符会被拒绝:adä@example.com 只有加上 FILTER_FLAG_EMAIL_UNICODE 才能通过,这个标志允许 @ 前面出现这类字符;而 exämple.com 这样的域名即使加了标志也会失败,除非你先把它转换成 xn-- 形式。要知道一个地址能不能用,唯一的办法是给它发送确认链接。

验证整数,以及 min_range 和 max_range

FILTER_VALIDATE_INT 把有效的字符串转换成 int,拒绝其他所有值。选项数组可以加上范围和默认值:

首尾的空白和开头的 + 是允许的,开头的零('042')则不允许;'42' 的结果是整数 42,而不是字符串。超出范围得到 false;设置了 default 选项时,你会得到默认值而不是 false,这对 ?page= 这类参数很方便(见 GET和_GET 和 _POST)。加上 FILTER_FLAG_ALLOW_HEX 或 FILTER_FLAG_ALLOW_OCTAL 就能接受 0x1A 或 0755。

0 与 false 的陷阱

有效的 '0' 返回整数 0,而 0 是假值。普通的 if 会把它当成失败:

FILTER_VALIDATE_FLOAT('0.0')和 FILTER_VALIDATE_BOOL 也一样,对后者来说 false 本身就是有效的答案。FILTER_NULL_ON_FAILURE 正是为此而设的。

布尔值、浮点数、URL 和 IP 地址

FILTER_VALIDATE_BOOL 是 PHP 8 中的名称;FILTER_VALIDATE_BOOLEAN 是同一个过滤器。不加 FILTER_NULL_ON_FAILURE 的话,'maybe' 会返回 false,与 'off' 无法区分。

有效的 URL 不等于安全的链接

FILTER_VALIDATE_URL 只检查字符串的形式是否像 URL,任何协议都可以。这其中包括你绝不希望出现在 href 中的协议:

javascript://comment%0Aalert(1) 能通过 FILTER_VALIDATE_URL,而点击这个链接时浏览器会把它当作 JavaScript 运行,因为 %0A 结束了 //comment。把用户提供的 URL 输出到页面之前,先用 parse_url() 检查协议。你还可以用 FILTER_FLAG_PATH_REQUIRED 和 FILTER_FLAG_QUERY_REQUIRED 要求 URL 必须包含某些部分。

净化过滤器

净化过滤器不回答“是”或“否”,而是移除字符。它们适合规范化输入,但不能让输入变得可以安全输出:

看第一行:Order #12-345 (x2) 变成了 12-3452,一个输入中从未出现过的数字;第三行则是一个没有人输入过的地址。如果做了净化,之后还要验证结果。对于输出到 HTML 中的任何内容,正确的工具是在输出时使用 htmlspecialchars()(FILTER_SANITIZE_FULL_SPECIAL_CHARS 做的是同样的转义)。FILTER_SANITIZE_STRING 从 PHP 8.1 起已被弃用;替代方案见 htmlspecialchars。

用 filter_var_array 验证整个表单

filter_var_array() 对每个键应用一个过滤器,返回的数组包含你要求的每个键:过滤后的值、验证失败时的 false,或者键不存在时的 null。

role 不见了:没有列出的键会被丢弃,这样访问者就无法往表单里加上 role=admin 并让它被保存下来。country 是 null,因为它没有被提交。在 Web 服务器上,filter_input_array(INPUT_POST, $rules) 做同样的事,只是直接从请求中读取。

常见问题

PHP 中怎么验证邮箱地址?

用 filter_var($email, FILTER_VALIDATE_EMAIL),邮箱有效时返回邮箱字符串,无效时返回 false:if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { /* invalid */ }。它只检查格式;要知道地址是否真实存在,得发一封确认邮件。

PHP 中怎么判断一个字符串是不是整数?

filter_var($s, FILTER_VALIDATE_INT) 对 "42"、"-7" 和 " 42 " 返回整数,对 "4.2"、"42abc" 或 "042" 返回 false。要用 !== false 比较,因为有效的 "0" 会返回 0,而它是假值。

为什么 filter_var 对 0 返回 false?

其实没有:filter_var('0', FILTER_VALIDATE_INT) 返回的是整数 0。bug 出在判断上,if (filter_var(...)) 把 0 当成了 false。应该写 if (filter_var($v, FILTER_VALIDATE_INT) !== false)。

PHP 8.1 中用什么替代 FILTER_SANITIZE_STRING?

没有直接的替代品;它被弃用,是因为它的名字承诺了它做不到的安全性。验证你期望的输入,按原样存储,输出时再用 htmlspecialchars() 转义。如果需要去掉标签,用 strip_tags()。

filter_var 和 filter_input 有什么区别?

filter_var() 过滤你传入的任何值。filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT) 直接从原始请求中读取,所以参数不存在时返回 null,并且会忽略你的代码对 $_GET 做的任何修改。两者接受相同的过滤器和选项。

Coddy 编程语言插图

和 Coddy 一起学编程

开始