重温 filter_var() 和 FILTER_VALIDATE_URL

不久前,我研究了使用filter_var()FILTER_VALIDATE_URL 标志使用该函数来验证 URL,最近有人指出该函数不仅自初始发布以来发生了变化,而且可以添加许多标志来改变该函数的方式作品。这是可用的标志。

  • FILTER_FLAG_SCHEME_REQUIRED要求URL 中的方案(例如,http://、ftp:// 等)。

  • FILTER_FLAG_HOST_REQUIRED需要 URL 的主机(例如,www.google.com)

  • FILTER_FLAG_PATH_REQUIRED要求在 URL 的主机后面有一个路径。

  • FILTER_FLAG_QUERY_REQUIRED要求在 URL 末尾进行查询(例如,?key=value)

这些标志可以添加到正常filter_var()调用中以更改结果的结果。以 FILTER_FLAG_PATH_REQUIRED 标志为例。

filter_var('http://www.bbc.co.uk', FILTER_VALIDATE_URL); // returns http://www.bbc.co.uk (which means it validates)
filter_var('http://www.bbc.co.uk', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED); // returns false

也可以通过将多个标志添加在一起来使用多个标志。例如,如果您想在 URL 中同时要求路径和查询,那么您可以使用以下代码段。

filter_var('http://www.bbc.co.uk', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED); // returns false

所以我想我会尝试尽可能完整地测试这些 URL 过滤器,并将结果打印在真值表中。为此,我创建了一堆可以测试的 URL。数组中的每一项都包含一个空数组,每个测试的结果都将放入该数组中。

$urls = array(
    'http://www.bbc.co.uk' => array(),
    'http://www.hashbangcode.com' => array(),
    'http://www.hashbangcode.com/blog' => array(),
    'http://www.example.com/index.html#anchor' => array(),
    'http://www.example.com/index.html?q=123' => array(),    
    'example.com' => array(),
    'www.example.com' => array(),
    'www.example.com/blog' => array(),
    'www.example.com/index.html?q=123' => array(),    
    '/index.html?q=123' => array(),     
    'https://www.example.com/' => array(),
    'https://localhost' => array(),    
    'https://localhost/' => array(),
    'https://127.0.0.1/' => array(),    
    'http://.com' => array(),
    'http://...' => array(),
    'http://' => array(),
    'http://i\'me really trying to break this url!!!"£$"%$&*()' => array()
);

然后我创建了一个验证标志列表,包括一个用于测试无标志的 NULL 值和四个标志的不同排列。您可能会问自己,为什么我在这个数组中同时使用字符串和值。原因是标志实际上只是整数常量,所以当我稍后尝试将它们打印出来时,我发现打印的是整数值而不是实际的标志键。因此,为了妥协,我使用标志的字符串表示作为数组键,使用实际标志作为值。

$flags = array(
'Null' => NULL,
'FILTER_FLAG_SCHEME_REQUIRED' => FILTER_FLAG_SCHEME_REQUIRED,
'FILTER_FLAG_HOST_REQUIRED' => FILTER_FLAG_HOST_REQUIRED,
'FILTER_FLAG_PATH_REQUIRED' => FILTER_FLAG_PATH_REQUIRED,
'FILTER_FLAG_QUERY_REQUIRED' => FILTER_FLAG_QUERY_REQUIRED,
'FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED' => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED,
'FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_PATH_REQUIRED' => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_PATH_REQUIRED,
'FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_QUERY_REQUIRED' => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_QUERY_REQUIRED,
'FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED' => FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED,
'FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_QUERY_REQUIRED' => FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_QUERY_REQUIRED,
'FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED' => FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED,
'FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED' => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED,
'FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED' => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED,
'FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED' => FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED,
'FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED' => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED,
);

然后将这些阵列拉到一起并按以下方式进行测试。

// 做过滤
foreach ($urls as $url => $data) {
    foreach ($flags as $textFlag => $flag) {
        $urls[$url][$textFlag] = (filter_var($url, FILTER_VALIDATE_URL, $flag) === FALSE)? 'FALSE' : 'TRUE';
    }
}

然后将这些测试的结果打印在两个不同的表中,第一个使用标志作为标题,第二个使用 URL 作为标题。这是打印出表格的代码。

// 以 URL 为行打印表格
$header = '<tr><th>URL</th><th>';
$header .= implode('</th><th>' , array_keys($flags)) . '</th>';
$header .= '</tr>';
$rows = '';
 
foreach ($urls as $url => $data) {
    $rows .= '<tr><td>' . $url . '</td>';
    foreach ($flags as $textFlag => $flag) {
        $colour = ($urls[$url][$textFlag] == 'TRUE')? 'green':'red';
        $rows .= '<td style="background-color:' . $colour . ';">' . $urls[$url][$textFlag] . '</td>';
    }
    $rows .= '</tr>';
}
 
echo '<table border="1">' . $header . $rows . '</table>';
 
echo '<br />';
 
// 打印带有标志作为行的表
$header = '<tr><th>FLAG</th><th>';
$header .= implode('</th><th>' , array_keys($urls)) . '</th>';
$header .= '</tr>';
$rows = '';
 
foreach ($flags as $textFlag => $flag) {
    $rows .= '<tr><td>' . $textFlag . '</td>';
    foreach ($urls as $url => $data) {
        $colour = ($urls[$url][$textFlag] == 'TRUE')? 'green':'red';
        $rows .= '<td style="background-color:' . $colour . ';">' . $urls[$url][$textFlag] . '</td>';
    }
    $rows .= '</tr>';
}
echo '<table border="1">' . $header . $rows . '</table>';

在撰写本文时,所有这些代码都是使用最新版本的 PHP 运行的,即 5.3.3。有很多数据要塞进这个页面,所以我创建了一个结果页面,将完整显示真值表。

我确实发现的一件事是 FILTER_FLAG_SCHEME_REQUIRED 标志似乎没有做任何事情。我认为它可以强制诸如 http:// 之类的东西出现在 URL 的前面,但是由于该filter_var()函数的默认操作不允许该方案被遗漏,所以这个标志基本上是没有意义的。自从我上次查看此验证功能以来,事情已经取得了很大进展,但它仍然没有按预期工作。