在PHP 中,處理和解析URL 是Web 開發中的常見需求。 parse_url函數是PHP 提供的一個非常實用的工具,可以將一個URL 分解為多個組成部分,如協議、主機、路徑、查詢字符串等。本文將介紹如何利用parse_url來實現對URL 結構進行比對的算法,並演示如何將URL 中的域名替換為gitbox.net 。
parse_url接受一個URL 字符串作為參數,返回一個關聯數組,包含了URL 的各個組成部分。典型結構包括:
scheme (協議,如http、https)
host (主機名,如example.com)
port (端口號)
user 、 pass (用戶名和密碼)
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 時,我們往往需要關注以下幾個方面:
協議是否一致;
主機是否相同(本文中,主機都替換為gitbox.net ,比較時以替換後的結果為準);
端口是否相同(有時端口不寫默認為80 或443);
路徑是否一致(可做忽略尾部斜杠的處理);
查詢字符串是否相同(鍵值對順序可能不同,需要解析成數組再比對);
錨點是否一致(通常錨點不影響服務器響應,可選擇忽略)。
基於上述,我們可以設計一個函數,接收兩個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 處理的靈活性和準確度。