请注意,该函数不会验证 "非拉丁" 域名。
if (filter_var('уникум@из.рф', FILTER_VALIDATE_EMAIL)) {
echo 'VALID';
} else {
echo 'NOT VALID';
}
(PHP 5 >= 5.2.0, PHP 7, PHP 8)
filter_var — 使用指定过滤器过滤变量
value
要过滤的值。请注意,标量值在内部被转换为字符串,然后再进行过滤。
filter
要应用的过滤器的 ID。过滤器类型 手册页列出了可用的过滤器。
如果省略,将使用 FILTER_DEFAULT
,它等效于 FILTER_UNSAFE_RAW
。默认情况下,这将不会进行任何过滤。
options
选项的关联数组或标志的按位析取。如果过滤器接受选项,则可以在数组的 "flags" 字段中提供标志。对于 "callback" 过滤器,应传递可调用 类型。回调必须接受一个参数(要过滤的值)并返回过滤/清理后的值。
<?php
// 对于接受选项的过滤器,请使用此格式
$options = array(
'options' => array(
'default' => 3, // 如果过滤器失败,要返回的值
// 其他选项在这里
'min_range' => 0
),
'flags' => FILTER_FLAG_ALLOW_OCTAL,
);
$var = filter_var('0755', FILTER_VALIDATE_INT, $options);
// 对于只接受标志的过滤器,您可以直接传递它们
$var = filter_var('oops', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
// 对于只接受标志的过滤器,您也可以作为数组传递
$var = filter_var('oops', FILTER_VALIDATE_BOOLEAN,
array('flags' => FILTER_NULL_ON_FAILURE));
// 回调验证过滤器
function foo($value)
{
// 预期格式:姓氏, 名字
if (strpos($value, ", ") === false) return false;
list($surname, $givennames) = explode(", ", $value, 2);
$empty = (empty($surname) || empty($givennames));
$notstrings = (!is_string($surname) || !is_string($givennames));
if ($empty || $notstrings) {
return false;
} else {
return $value;
}
}
$var = filter_var('Doe, Jane Sue', FILTER_CALLBACK, array('options' => 'foo'));
?>
返回过滤后的数据,如果过滤器失败,则返回 false
。
示例 #1 一个 filter_var() 示例
<?php
var_dump(filter_var('[email protected]', FILTER_VALIDATE_EMAIL));
var_dump(filter_var('http://example.com', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED));
?>
上面的示例将输出
string(15) "[email protected]" bool(false)
示例 #2 过滤数组示例
<?php
$emails = [
"[email protected]",
"[email protected]",
"invalidemail"
];
var_dump(filter_var($emails, FILTER_VALIDATE_EMAIL, FILTER_REQUIRE_ARRAY));
?>
上面的示例将输出
array(3) { [0]=> string(15) "[email protected]" [1]=> string(18) "[email protected]" [2]=> bool(false) }
请注意,该函数不会验证 "非拉丁" 域名。
if (filter_var('уникум@из.рф', FILTER_VALIDATE_EMAIL)) {
echo 'VALID';
} else {
echo 'NOT VALID';
}
实际上,这对于手册来说不是一个很有用的评论(所以不要点赞),但由于搜索引擎没有找到很多关于错误消息的实例,尤其是没有有用的提示,所以它可能会节省一些人的时间。
如果您收到类似 "filter_var(): Unknown filter with ID 2097152" 或其他数字的错误消息,那么您只是不小心弄错了参数。因此,而不是
<?php
filter_var($ip, FILTER_FLAG_IPV6)
?>
你应该用
<?php
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
?>
来试一下,它会工作的 ;) 我知道,这并不是设计函数最直观的形式,而且很容易想把所有东西都放到一个参数里,就像对常规检查所做的那样,但是,是的,它就是这样。
我发现了一些 FILTER_VALIDATE_EMAIL 拒绝的地址,但 RFC5321 允许这些地址。
<?php
foreach (array(
'[email protected]',
'(comment)[email protected]',
'"this is v@lid!"@example.com',
'"much.more unusual"@example.com',
'postbox@com',
'admin@mailserver1',
'"()<>[]:,;@\\"\\\\!#$%&\'*+-/=?^_`{}| ~.a"@example.org',
'" "@example.org',
) as $address) {
echo "<p>"$address is <b>".(filter_var($address, FILTER_VALIDATE_EMAIL) ? '' : 'not')." valid</b></p>";
}
?>
Results
[email protected] 不是有效的
(comment)[email protected] 不是有效的
"this is v@lid!"@example.com 不是有效的
"much.more unusual"@example.com 不是有效的
postbox@com 不是有效的
admin@mailserver1 不是有效的
"()<>[]:,;@\"\\!#$%&'*+-/=?^_`{}| ~.a"@example.org 不是有效的
" "@example.org 不是有效的
文档中没有说明 FILTER_VALIDATE_EMAIL 应该通过 RFC5321,但是你可以通过这些例子来验证 (尤其是第一个例子)。因此,这是一个注意事项,而不是一个错误报告。
请注意,FILTER_VALIDATE_BOOLEAN 试图变得聪明,它识别像 Yes、No、Off、On 这样的词,以及 true 和 false 的字符串和本机类型,并且在验证字符串时不区分大小写。
<?php
$vals=array('on','On','ON','off','Off','OFF','yes','Yes','YES',
'no','No','NO',0,1,'0','1','true',
'True','TRUE','false','False','FALSE',true,false,'foo','bar');
foreach($vals as $val){
echo var_export($val,true).': '; var_dump(filter_var($val,FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE));
}
?>
输出
'on': bool(true)
'On': bool(true)
'ON': bool(true)
'off': bool(false)
'Off': bool(false)
'OFF': bool(false)
'yes': bool(true)
'Yes': bool(true)
'YES': bool(true)
'no': bool(false)
'No': bool(false)
'NO': bool(false)
0: bool(false)
1: bool(true)
'0': bool(false)
'1': bool(true)
'true': bool(true)
'True': bool(true)
'TRUE': bool(true)
'false': bool(false)
'False': bool(false)
'FALSE': bool(false)
true: bool(true)
false: bool(false)
'foo': NULL
'bar': NULL
"hek" 关于 HTML5 有模式,从而减轻了在 PHP 中过滤的必要性的说明是完全错误的:你仍然必须在服务器端过滤输入。HTML5 表单输入是客户端的,这意味着它们完全在用户的控制之下。只有当你以 PHP 接收数据时,它才是服务器端的,并在你的控制之下。一旦数据在你的控制之下,你必须对它进行适当的过滤/清理。
无论服务器端语言是什么,这都是正确的。我鼓励版主删除 "hek" 的说明,因为它会导致人们产生可怕的后果。
Steve
这也是一个有效的 url
http://example.com/"><script>alert(document.cookie)</script>
请注意
url 中的问号也是有效的
<?php
echo filter_var("http://test???test.com", FILTER_VALIDATE_URL)?"valid":"not valid"; #valid
?>
FILTER_VALIDATE_URL 允许
filter_var('javascript://comment%0Aalert(1)', FILTER_VALIDATE_URL);
其中 %0A (URL 编码的换行符) 在某些情况下会将注释与 JS 代码分隔开。
这可能会导致 XSS 漏洞。
请注意,带有 FILTER_VALIDATE_URL 的 filter_var() 使用的是过时的 RFC2396。这意味着它将一些当前有效的字符 (例如 "_") 视为无效。
在许多情况下,使用 php parse_url() 可能更有益,因为它使用的是当前有效的 RFC3986。
我无法确认 yactouat 所说的话。截至 PHP 7.3,0 不会被 FILTER_VALIDATE_INT 过滤掉。它正确地返回 0,而不是 false。当然,你必须使用恒等运算符来检查返回值。否则,你无法区分 0 和 false。
请注意,FILTER_FLAG_PATH_REQUIRED 对单个斜杠 (/) 很满意,所以
<?php
$options = array('flags' => FILTER_FLAG_PATH_REQUIRED);
filter_var('http://example.com', FILTER_VALIDATE_URL, $options); // 返回 false
filter_var('http://example.com/', FILTER_VALIDATE_URL, $options); // 返回 'http://example.com/'
?>
我编写了一个与 PHP 的 filter_var() 实现完全兼容的 JavaScript 邮箱验证器。
mpyw/FILTER_VALIDATE_EMAIL.js: 与 PHP 的 filter_var($value, FILTER_VALIDATE_EMAIL) 兼容的邮箱验证
https://github.com/mpyw/FILTER_VALIDATE_EMAIL.js
你实际上很可能想要检测所有保留的范围,而不仅仅是私有 IP,并且还有一个常量用于它们,应该与它进行按位或运算。
<?php
function is_private_ip($ip) {
return !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
}
?>
使用 FILTER_CALLBACK 需要将一个数组作为选项传递。
<?php
function toDash($x){
return str_replace("_","-",$x);
}
echo filter_var("asdf_123",FILTER_CALLBACK,array("options"=>"toDash"));
// 返回 'asdf-123'
?>
我不建议在普通网站上使用此函数来验证邮箱地址。问题是根据 RFC 3696 (检查和转换名称的应用技术),以下邮箱地址将被视为有效
customer/[email protected]
[email protected]
!def!xyz%[email protected]
[email protected]
"Abc@def"@example.com
这很难让人接受在 2020 年的实时 Web 应用中 :-/
在熟悉 filter_var( $var, FILTER_VALIDATE_INT ) 的过程中,我发现很有趣的是 0 会被过滤掉,因此不会被视为 int。希望这对某些人有用,不会被卡住 ;)
注意:如果你需要接受 0,你可以使用 is_int()
以下是如何使用多个标志 (对于那些喜欢通过例子学习的人,就像我一样)
<?php
echo "|asdf".chr(9).chr(128)."_123|";
echo "\n";
// "位运算与" 指的是逻辑或 / 位运算 |
echo filter_var("|asdf".chr(9).chr(128)."_123\n|" ,FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
/*
结果:
|asdf �_123|
|asdf_123|
*/
?>
请注意,FILTER_VALIDATE_URL 会通过以下 URL。
http://example.ee/sdsf"f
注意,仅使用 FILTER_VALIDATE_URL 来验证 URL 输入可能会导致 XSS。
$url = 'javascript://%0Aalert(document.cookie)';
if (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)) {
echo '<a href="' . $url . '">click</a>';
}
您至少应该另外检查实际使用的方案。
以下是一个使用标志的过滤器语法的实际示例,因为似乎没有一个对此的单行代码
'hours' => array('filter'=>FILTER_SANITIZE_NUMBER_FLOAT, 'flags' => FILTER_FLAG_ALLOW_FRACTION, 'options'=> '.')
请注意,即使 URL 不正确,以下代码也会返回 true。因为它只验证域、子域、路径和查询,而不是协议。
<?php
filter_var( 'http://https://example.com', FILTER_VALIDATE_URL );
?>
请阅读更多关于 https://php.net/manual/en/filter.filters.validate.php
您可以使用多个标志来验证 IP 地址
// 验证输入是否为 IPv4 地址
$_FILTERS = array('flags' => FILTER_FLAG_IPV4);
// 验证输入是否为 IPv4 地址,且不是私有 IP。
$_FILTERS = array('flags' => FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE);
// 验证输入是否为 IPv4 且不是保留 IP。
$_FILTERS = array('flags' => FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE);
// 验证输入是否为 IPv4,且不是私有 IP 且不是保留 IP。
$_FILTERS = array('flags' => FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
filter_var($_input, FILTER_VALIDATE_IP, $_FILTERS);
我看到了一些关于 FILTER_VALIDATE_URL 的报告,我也想补充我的,因为在像这样的愚蠢代码中
<?php
$ckOrigin = 'https://forum.myw3host.comhttps://forum.myw3host.comhttps://forum.myw3host.com/viewtopic.php?p=45#p45';
if(filter_var($ckOrigin, FILTER_VALIDATE_URL)){
echo 'ok the URL is valid';
}
?>
因为我确信如果 URL 错误,它会返回 false,我花了很长时间才意识到它反而失败成像上面的字符串,并且它返回 true。
作为 https://php.net/manual/en/function.filter-var.php#128235 的回复
如果您使用 FILTER_FLAG_PATH_REQUIRED,它会正确工作。
var_dump( filter_var('http://test???test.com/path/?t=1', FILTER_VALIDATE_URL) ); // true
var_dump( filter_var('http://test???test.com/path/?t=1', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED) ); // false
"(comment)[email protected]"
根据 RFC5322(附录 A.6.3)是一个无效的电子邮件地址
"此外,地址、日期和消息标识符中的注释和空格都是过时语法的部分。"
请注意,当使用 FILTER_VALIDATE_INT 以及 FILTER_FLAG_ALLOW_HEX 标志时,例如字符串 "2f" 不会成功验证,因为您必须使用 "0x" 前缀,否则它将数据视为十进制。
范围选项也足够智能,可以识别何时在不同的基数中超出边界。
以下是一个示例
<?php
$foo = '256';
$bar = '0x100';
var_dump(validate_int($foo)); // false,太大
var_dump(validate_int($bar)); // false,太大
function validate_int($input)
{
return filter_var(
$input,
FILTER_VALIDATE_INT,
// 我们必须传递一个关联数组
// 以包含范围检查选项。
array(
'flags' => FILTER_FLAG_ALLOW_HEX,
'options' => array('min_range' => 1, 'max_range' => 0xff)
)
);
}
?>
FILTER_VALIDATE_URL 不支持国际化域名 (IDN)。无论有效与否,任何包含 Unicode 字符的域名都不会通过验证。
我们可以通过自制的解决方案来规避这个问题,但是 C 代码就是 C 代码,所以我选择了下面的代码,它基于 filter_var()。
<?php
$res = filter_var ($uri, FILTER_VALIDATE_URL);
if ($res) return $res;
// 检查它是否包含 Unicode 字符。
$l = mb_strlen ($uri);
if ($l !== strlen ($uri)) {
// 用 “X” 替换宽字符。
$s = str_repeat (' ', $l);
for ($i = 0; $i < $l; ++$i) {
$ch = mb_substr ($uri, $i, 1);
$s [$i] = strlen ($ch) > 1 ? 'X' : $ch;
}
// 现在重新检查。
$res = filter_var ($s, FILTER_VALIDATE_URL);
if ($res) { $uri = $res; return 1; }
}
?>
逻辑很简单。非 ASCII 字符的长度超过一个字节。我们将每个这样的字符替换为“X”,然后再次检查。
另一种方法是在调用 filter_var() 之前对 URI 进行 punycode 编码,但是 PHP 缺乏对 punycode 的原生支持。我认为我的方法很有效。如果您有其他想法或发现可以改进的地方,请给我发邮件。
以下是用 FILTER_VALIDATE_URL 进行 filter_var 的简单测试。
(如果您在此之后使用 file_get_contents,您会遇到问题,我使用的是:PHP 5.5.12 (cli))
<?php
$url = 'a://google.com';
$result = filter_var($url, FILTER_VALIDATE_URL);
if($result){
echo 'Valid URL'.PHP_EOL;
}
var_dump($result);
?>
结果是
Valid URL
string(14) "a://google.com"
请记住,FILTER_VALIDATE_EMAIL 会根据标准验证电子邮件地址。
但是,鉴于组织可以自由地限制自己的电子邮件地址形式,仅使用此过滤器会导致很多退信。
gmail、yahoo、hotmail、aol 都有特殊规则
例如
<?php
$email_a = '0hot\'[email protected]';
if (filter_var($email_a, FILTER_VALIDATE_EMAIL)) {
echo "This (email_a) email address is considered valid.";
// 报告为有效
}
// 不能有 "[email protected]"
// 因为 hotmail 会说 "您的电子邮件地址需要以字母开头。请重试。" 即使您删除了 '
?>
关于过滤整数需要记住的一件关键事情是,选项 max_range 的值必须小于或等于 PHP_INT_MAX 的值。
filter_var($someVariable, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => SOME_VALUE_GREATER_THAN_PHP_INT_MAX)));
即使 $someVariable 是预期范围内的有效整数,这也会失败。
当您尝试在 32 位系统上验证无符号 MySQL INT 类型(其最大值为 4294967295)的潜在键时,可能会出现这种情况,其中 PHP_INT_MAX 的值为 2147483647。
一些布尔转换
<?php
var_dump(filter_var('oops', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// NULL
var_dump(filter_var('false', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(false)
var_dump(filter_var('true', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(true)
var_dump(filter_var(0, FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(false)
var_dump(filter_var(1, FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(true)
var_dump(filter_var('TRUE', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(true)
var_dump(filter_var('', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(false)
var_dump(filter_var('FALSE', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(false)
FILTER_SANITIZE_EMAIL 注入测试。
<?php
$InjString = "\r\n|\n|%0A|%0D|bcc:|to:|cc:|Content-Type:|Mime-Type:|";
echo filter_var($InjString, FILTER_SANITIZE_EMAIL);
?>
||%0A|%0D|bcc|to|cc|Content-Type|Mime-Type|
需要注意的是,尽管函数第一个参数的数据类型被声明为“mixed”,但这只是真相的一半。
虽然它接受任何数据类型,但第一个参数将在验证或清理之前始终被转换为字符串。
此函数似乎是专门为用户输入字符串而设计的。例如:来自在线表格。当将其用于除这些之外的任何其他用途时,您可能会遇到问题。因此请仔细阅读文档!
特别是注意,在使用 FILTER_NULL_ON_FAILURE 标志时,存在一个(至今)未解决的问题(#49510),涉及布尔过滤器。请注意,(string) FALSE 和 FALSE 都不会被识别为布尔值,并将返回 NULL(而不是您期望的 FALSE)。
因此我个人建议(至今)使用 filter_var() 函数超越其最初目的(并允许将来扩展和自定义)的最佳方法是将它们封装在您自己的类中。这将允许您解决非字符串输入上的意外行为,并添加自定义检查,或移植将来 PHP 版本中可能添加的过滤器或清理器。
(特别是由于 PHP 目前仍然缺少一些更奇特的 HTML5 输入类型(如“color”)的过滤器和清理器。因此,我们确实有可能在将来的某个时候需要自定义过滤器或移植。)
许多人,包括我自己,都发现 FILTER_VALIDATE_EMAIL 实际上并没有正常工作。
下面是一个我认为可以验证所有合法可路由地址的包装器。
<?php
/*******************************************
*
* 这些是函数
*
* check_username 由 check_email 调用
* - 它补偿了 php
* filter_var 函数中的错误。
* - 返回布尔值
*
* check_email 是要使用的函数。
* 第一个参数是字符串,要检查的地址
* 第二个参数是可选的布尔值,
* 是否使用 DNS 来验证
* 域名。默认为 true
* 返回布尔值
*
*/
function check_username($uname) {
// 只有 UTF-8 地址是合法的
if (iconv('UTF-8', 'UTF-8', $input) != $input) {
return FALSE;
}
// 将所有大于 U+007F 的字符替换为字母 U,以简化检查
$uname = preg_replace('/[\x{007F}-\x{FFFF}]/u', 'U', $uname);
// 移除注释 - 仅在用户名开头或结尾的格式 (comment) 中合法
$s[] = '/^\([^\)]*\)/'; $s[] = '/\([^\)]*\)$/';
$uname = preg_replace($s, '', $uname);
// 确保我们还剩下一些东西
if(strlen(trim($uname)) == 0) {
return FALSE;
}
// 检查点号的使用是否合法
if(substr_count($uname, '..') > 0) {
return FALSE;
}
// 将 \\ 和 \" 转换为 A,以简化操作
$s[] = '/[\\\][\\\]/';
$s[] = '/\\\"/';
$uname = preg_replace($s, 'A', $uname);
// 检查引号的使用是否合法
if(preg_match('/[^.]+"[^.]+/', $uname)) {
return FALSE;
}
// 补偿在引号中合法的字符
$uname = preg_replace_callback('/"(.*)"/', function ($m) {
$s[]="/[ \(\),\:;<>@\[\] ]/";
return preg_replace($s,'Q',$m[1]);
}, $uname);
// 使用 filter_var 检查剩余的内容
return filter_var($uname . '@example.org', FILTER_VALIDATE_EMAIL);
}
function check_email($email, $dns_check=true) {
$array = explode('@', $email);
if(count($array) < 2) {
return FALSE;
}
$domain = end($array);
array_pop($array);
if(function_exists('idn_to_ascii')) {
// php 过滤器无法处理 unicode 字符
$domain = idn_to_ascii($domain);
}
$ipcheck = preg_replace(array('/^\[ipv6\:/i', '/^\[/', '/\]$/'), '', $domain);
if(filter_var($ipcheck, FILTER_VALIDATE_IP)) {
// 这是一个 IP 地址
if(! filter_var($ipcheck, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return FALSE;
}
} else {
// 这是一个域名
// php 错误 - FILTER_VALIDATE_EMAIL 不喜欢裸 TLD
if(! filter_var('user@a.' . $domain, FILTER_VALIDATE_EMAIL)) {
return FALSE;
}
if($dns_check) {
if(! dns_get_record($domain)) {
return FALSE;
}
}
}
// 现在检查合法的用户名
return check_username(implode('@', $array));
}
?>
它分两部分评估地址,首先评估主机,如果合法,则评估用户名。
如果存在 DNS 问题 *并且* 使用了默认的 $dns_check 值 true,则 valid 将失败。如果它是一个国际域名,您必须安装 php-intl 包。
享受。
回复 Andi
这不是一个有效的 URL,因为字符没有被编码
http://example.com/"><script>alert(document.cookie)</script>
这是一个有效的 URL
http://example.com/%22%3E%3Cscript%3Ealert%28document.cookie%29%3C%2Fscript%3E
对于那些正在寻找私有 IP 检查的人,这就是它
<?php
function is_private_ip($ip)
{
return !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE);
}
?>
请注意,如果尝试从 XML 文档(例如通过 xpath)获取字符串,filter_var 结合 FILTER_VALIDATE_EMAIL 将不起作用。
我经常使用 XML 文件作为配置文件,并使用一个通过 xpath 从配置文件返回字符串的函数。虽然这在 5.2.11 之前工作正常,但现在不再有效(也不应该有效,因为它是一个 XML 元素,而不是一个字符串)。
要克服这个问题,可以对 $variable 进行类型转换
<?php
$variable = fancyXmlGetFunction('from');
filter_var((String) $variable, FILTER_VALIDATE_EMAIL);
?>