php 两个URL是否相同?忽略参数顺序

9o685dep  于 2022-12-10  发布在  PHP
关注(0)|答案(3)|浏览(139)

我有两个网址,并正在寻找最好的方式来确定他们是否相同。
示例:

$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中,只有sortmsg参数被交换,所以我认为它们是相等的。
我有一个URL列表,需要查找重复的URL,所以代码应该很快,因为它是在循环中运行的。domain/page.php将始终保持不变,它只是通过参数查找URL。)

xjreopfe

xjreopfe1#

也许像这样?

function compare_url($url1, $url2){
  return (parse_url($url1,PHP_URL_QUERY) == parse_url($url2,PHP_URL_QUERY));
}
a64a0gku

a64a0gku2#

找出一个URI是否相同并不像听起来那么容易,特别是在这里考虑查询参数时。
一种常见的方法是使用normalizes the 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;
}

对参数使用数组/散列Map也不错,我只是想展示另一种方法。完整示例:

<?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)
u0njafvf

u0njafvf3#

为了确保两个URL相同,我们需要比较至少4个元素:
1.方案(例如httphttpsftp
1.主机,即URL的域名
1.路径,即请求的“文件”
1.请求的查询参数。
一些注意事项:

  • (1)和(2)不区分大小写,这意味着http://example.orgHTTP://EXAMPLE.ORG相同。
  • (3)可以有前导或尾随斜杠,说明应忽略:example.orgexample.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

*/

相关问题