我有两个URL,正在寻找确定它们是否相同的最佳方法。
例:
$url1 = 'http://example.com/page.php?tab=items&msg=3&sort=title';
$url2 = 'http://example.com/page.php?tab=items&sort=title&msg=3';
在两个 URL 中,只有 sort
和 msg
参数被切换,因此我认为它们是相等的。但是,我不能简单地做if ( $url1 == $url2 ) { … }
我有一个 URL 列表,需要查找重复项,因此代码应该很快,因为它在循环中运行。(作为旁注:域/页面.php将始终相同,它只是通过参数查找URL。
也许是这样的?
function compare_url($url1, $url2){
return (parse_url($url1,PHP_URL_QUERY) == parse_url($url2,PHP_URL_QUERY));
}
确定 URI 是否相同并不像听起来那么容易,尤其是当您在此处考虑查询参数时。
执行此操作的一种常见方法是使用一个函数来规范化 URL,然后比较规范化的 URI:
$url1 = 'http://example.com/page.php?tab=items&msg=3&sort=title';
$url2 = 'http://example.com/page.php?tab=items&sort=title&msg=3';
var_dump(url_nornalize($url1) == url_nornalize($url2)); # bool(true)
在这样的规范化函数中,你提出了你的要求。首先,URL 应根据规范进行规范化:
function url_nornalize($url, $separator = '&')
{
// normalize according RFC 3986
$url = new Net_URL2($url);
$url->normalize();
然后,您可以处理其他规范化步骤,例如,对查询的子部分进行排序:
// normalize query if applicable
$query = $url->getQuery();
if (false !== $query) {
$params = explode($separator, $query);
sort($params);
$query = implode($separator, $params);
$url->setQuery($query);
}
可以采取其他步骤,例如删除默认参数或不允许的参数,或重复的参数等等。
最后返回规范化 URL 的字符串
return (string) $url;
}
对参数使用数组/哈希映射也不错,我只是想展示一种替代方法。完整示例:
<?php
/**
* http://stackoverflow.com/questions/27667182/are-two-urls-identical-ignore-the-param-order
*/
require_once 'Net/URL2.php';
function url_nornalize($url, $separator = '&')
{
// normalize according RFC 3986
$url = new Net_URL2($url);
$url->normalize();
// normalize query if applicable
$query = $url->getQuery();
if (false !== $query) {
$params = explode($separator, $query);
// remove empty parameters
$params = array_filter($params, 'strlen');
// sort parameters
sort($params);
$query = implode($separator, $params);
$url->setQuery($query);
}
return (string)$url;
}
$url1 = 'http://EXAMPLE.com/p%61ge.php?tab=items&&&msg=3&sort=title';
$url2 = 'http://example.com:80/page.php?tab=items&sort=title&msg=3';
var_dump(url_nornalize($url1) == url_nornalize($url2)); # bool(true)
为了确保两个 URL 相同,我们需要比较至少 4 个元素:
- 方案(例如
http
、https
、ftp
) - 主机,即 URL 的域名
- 路径,即请求的"文件"
- 请求的查询参数。
一些注意事项:
- (1) 和 (2) 不区分大小写,这意味着
http://example.org
与HTTP://EXAMPLE.ORG
相同。 - (3)可以有前导斜杠或尾随斜杠,应忽略:
example.org
与example.org/
相同 - (4) 可以包含不同顺序的参数。
- 我们可以安全地忽略锚文本或"片段"(
#anchor
查询参数之后),因为它们仅由浏览器解析。 - URL 还可以包含端口号、用户名和密码 - 我认为我们可以忽略这些元素,因为它们很少使用,因此不需要在此处检查它们。
溶液:
这是一个检查所有这些细节的完整函数:
/**
* Check if two urls match while ignoring order of params
*
* @param string $url1
* @param string $url2
* @return bool
*/
function do_urls_match( $url1, $url2 ) {
// Parse urls
$parts1 = parse_url( $url1 );
$parts2 = parse_url( $url2 );
// Scheme and host are case-insensitive.
$scheme1 = strtolower( $parts1[ 'scheme' ] ?? '' );
$scheme2 = strtolower( $parts2[ 'scheme' ] ?? '' );
$host1 = strtolower( $parts1[ 'host' ] ?? '' );
$host2 = strtolower( $parts2[ 'host' ] ?? '' );
if ( $scheme1 !== $scheme2 ) {
// URL scheme mismatch (http <-> https): URLs are not identical.
return false;
}
if ( $host1 !== $host2 ) {
// Different host (domain name): Not identical.
return false;
}
// Remvoe leading/trailing slashes, url-decode special characters.
$path1 = trim( urldecode( $parts1[ 'path' ] ?? '' ), '/' );
$path2 = trim( urldecode( $parts2[ 'path' ] ?? '' ), '/' );
if ( $path1 !== $path2 ) {
// The request-path is different: Different URLs.
return false;
}
// Convert the query-params into arrays.
parse_str( $parts1['query'] ?? '', $query1 );
parse_str( $parts2['query'] ?? '', $query2 );
if ( count( $query1 ) !== count( $query2 ) ) {
// Both URLs have a differnt number of params: They cannot match.
return false;
}
// Only compare the query-arrays when params are present.
if (count( $query1 ) > 0 ) {
ksort( $query1 );
ksort( $query2 );
if ( array_diff( $query1, $query2 ) ) {
// Query arrays have differencs: URLs do not match.
return false;
}
}
// All checks passed, URLs are identical.
return true;
} // End do_urls_match()
<小时 />测试用例:
$base_urls = [
'https://example.org/',
'https://example.org/index.php?sort=asc&field=id&filter=foo',
'http://EXAMPLE.com/p%61ge.php?tab=items&&&msg=3&sort=title',
];
$compare_urls = [
'https://example.org/',
'https://Example.Org',
'https://example.org/index.php?sort=asc&&field=id&filter=foo',
'http://example.org/index.php?sort=asc&field=id&filter=foo',
'https://company.net/page.php?sort=asc&field=id&filter=foo',
'https://example.org/index.php?sort=asc&&&field=id&filter=foo#anchor',
'https://example.org/index.php?field=id&filter=foo&sort=asc',
'http://example.com:80/page.php?tab=items&sort=title&msg=3',
];
foreach ( $base_urls as $url1 ) {
printf( "'n'n%s", $url1 );
foreach ( $compare_urls as $url2 ) {
if (do_urls_match( $url1, $url2 )) {
printf( "'n [MATCHES] %s", $url2 );
}
}
}
/* Output:
https://example.org/
[MATCHES] https://example.org/
[MATCHES] https://Example.Org
https://example.org/index.php?sort=asc&field=id&filter=foo
[MATCHES] https://example.org/index.php?sort=asc&&field=id&filter=foo
[MATCHES] https://example.org/index.php?sort=asc&&&field=id&filter=foo#anchor
[MATCHES] https://example.org/index.php?field=id&filter=foo&sort=asc
http://EXAMPLE.com/p%61ge.php?tab=items&&&msg=3&sort=title
[MATCHES] http://example.com:80/page.php?tab=items&sort=title&msg=3
*/