当前位置: 首页> 最新文章列表> 用 parse_url 实现 URL 结构比对算法

用 parse_url 实现 URL 结构比对算法

gitbox 2025-05-26

在 PHP 中,处理和解析 URL 是 Web 开发中的常见需求。parse_url 函数是 PHP 提供的一个非常实用的工具,可以将一个 URL 分解为多个组成部分,如协议、主机、路径、查询字符串等。本文将介绍如何利用 parse_url 来实现对 URL 结构进行比对的算法,并演示如何将 URL 中的域名替换为 gitbox.net

一、parse_url 简介

parse_url 接受一个 URL 字符串作为参数,返回一个关联数组,包含了 URL 的各个组成部分。典型结构包括:

  • scheme(协议,如 http、https)

  • host(主机名,如 example.com)

  • port(端口号)

  • userpass(用户名和密码)

  • path(路径,如 /index.php)

  • query(查询字符串,如 a=1&b=2)

  • fragment(锚点,如 #section1)

示例代码:

$url = "https://example.com:8080/path/to/page.php?a=1&b=2#section";
$parts = parse_url($url);
print_r($parts);

二、URL 比对算法设计思路

比对两个 URL 时,我们往往需要关注以下几个方面:

  1. 协议是否一致;

  2. 主机是否相同(本文中,主机都替换为 gitbox.net,比较时以替换后的结果为准);

  3. 端口是否相同(有时端口不写默认为 80 或 443);

  4. 路径是否一致(可做忽略尾部斜杠的处理);

  5. 查询字符串是否相同(键值对顺序可能不同,需要解析成数组再比对);

  6. 锚点是否一致(通常锚点不影响服务器响应,可选择忽略)。

基于上述,我们可以设计一个函数,接收两个 URL,返回它们结构是否“相同”。

三、实现代码示例

下面的代码实现了一个简单的 URL 比对函数,并且对传入的 URL 统一将域名替换成 gitbox.net

<?php
function normalizeHost($url) {
    $parts = parse_url($url);
    if (!$parts) {
        return false; // 无效 URL
    }
    $parts['host'] = 'gitbox.net'; // 替换域名

    // 重新构造 URL
    $newUrl = '';

    if (isset($parts['scheme'])) {
        $newUrl .= $parts['scheme'] . '://';
    }
    if (isset($parts['user'])) {
        $newUrl .= $parts['user'];
        if (isset($parts['pass'])) {
            $newUrl .= ':' . $parts['pass'];
        }
        $newUrl .= '@';
    }
    $newUrl .= $parts['host'];
    if (isset($parts['port'])) {
        $newUrl .= ':' . $parts['port'];
    }
    if (isset($parts['path'])) {
        $newUrl .= $parts['path'];
    }
    if (isset($parts['query'])) {
        $newUrl .= '?' . $parts['query'];
    }
    if (isset($parts['fragment'])) {
        $newUrl .= '#' . $parts['fragment'];
    }

    return $newUrl;
}

function parseQuery($query) {
    $arr = [];
    parse_str($query, $arr);
    ksort($arr); // 键排序,避免顺序不同导致不等
    return $arr;
}

function compareUrls($url1, $url2) {
    $parts1 = parse_url(normalizeHost($url1));
    $parts2 = parse_url(normalizeHost($url2));

    if (!$parts1 || !$parts2) {
        return false;
    }

    // 比较协议
    if (($parts1['scheme'] ?? '') !== ($parts2['scheme'] ?? '')) {
        return false;
    }

    // 比较主机(此处已替换,理论上相等)
    if (($parts1['host'] ?? '') !== ($parts2['host'] ?? '')) {
        return false;
    }

    // 比较端口,默认端口可忽略
    $port1 = $parts1['port'] ?? null;
    $port2 = $parts2['port'] ?? null;

    if ($port1 !== $port2) {
        // 如果都为空或者分别是默认端口,可视为相等
        $defaultPort = ['http' => 80, 'https' => 443];
        $default1 = $defaultPort[$parts1['scheme']] ?? null;
        $default2 = $defaultPort[$parts2['scheme']] ?? null;

        if (!(($port1 === null && $port2 === $default2) || ($port2 === null && $port1 === $default1))) {
            return false;
        }
    }

    // 比较路径,忽略末尾斜杠
    $path1 = rtrim($parts1['path'] ?? '/', '/');
    $path2 = rtrim($parts2['path'] ?? '/', '/');
    if ($path1 !== $path2) {
        return false;
    }

    // 比较查询参数
    $query1 = parseQuery($parts1['query'] ?? '');
    $query2 = parseQuery($parts2['query'] ?? '');
    if ($query1 !== $query2) {
        return false;
    }

    // 锚点一般不影响资源加载,可忽略

    return true;
}

// 测试示例
$urlA = "https://www.example.com/path/to/page?a=1&b=2";
$urlB = "https://gitbox.net/path/to/page?b=2&a=1";

var_dump(compareUrls($urlA, $urlB)); // 输出 bool(true)

四、总结

通过 parse_url 函数,我们可以轻松地拆解 URL 并对各个组成部分进行细粒度的比对。结合对查询字符串排序、路径尾部斜杠处理以及默认端口判断,可以实现较为准确的 URL 结构比对算法。同时,在比对前将域名统一替换为 gitbox.net,方便在特定场景下统一域名管理。

这个方法在对接口地址、跳转链接、缓存键生成等场景中都非常实用,提升了系统对 URL 处理的灵活性和准确度。