URL 處理是 Web 開發的高頻需求。PHP 提供了一組完整的原生函數覆蓋編碼、解碼、解析、構建、元數據提取等場景。本文系統整理這些函數的用法、差異與最佳實踐。
<?php
$str = 'This is an encoded string';
$encoded = base64_encode($str); // VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==
$decoded = base64_decode($encoded); // This is an encoded string
// URL 安全版 Base64(替換 + / =)
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function base64url_decode($data) {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
<?php
$url = 'https://www.example.com/search?q=中文 test';
// urlencode:空格編碼為 +,符合 application/x-www-form-urlencoded 標準
echo urlencode($url);
// http%3A%2F%2Fwww.example.com%2Fsearch%3Fq%3D%E4%B8%AD%E6%96%87+test
// rawurlencode:空格編碼為 %20,符合 RFC 3986,適用於 URL 路徑部分
echo rawurlencode($url);
// http%3A%2F%2Fwww.example.com%2Fsearch%3Fq%3D%E4%B8%AD%E6%96%87%20test
// 解碼對應
urldecode($encoded); // 對應 urlencode
rawurldecode($encoded); // 對應 rawurlencode
選擇原則:
urlencode/urldecoderawurlencode/rawurldecoderawurlencode,Query 參數用 urlencode<?php
$url = 'https://user:pass@example.com:8080/path/to/page?query=string#fragment';
print_r(parse_url($url));
/*
Array
(
[scheme] => http
[host] => example.com
[user] => user
[pass] => pass
[port] => 8080
[path] => /path/to/page
[query] => query=string
[fragment] => fragment
)
*/
注意: 不驗證 URL 合法性,不完整 URL 也會盡力解析。PHP 8.1+ 對無 Scheme 的 URL 解析行為有變化,建議始終提供完整 URL。
<?php
$data = [
'c' => 'blog',
'a' => 'show',
'id' => 10,
'tags' => ['php', 'url', 'encoding'],
'filter' => ['status' => 1, 'type' => 'article']
];
// 基礎用法
echo http_build_query($data);
// c=blog&a=show&id=10&tags%5B0%5D=php&tags%5B1%5D=url&tags%5B2%5D=encoding&filter%5Bstatus%5D=1&filter%5Btype%5D=article
// 數字索引鍵名加前綴
echo http_build_query(['hello', 'world'], 'item_');
// item_0=hello&item_1=world
// 指定分隔符(PHP 5.4+)
echo http_build_query($data, '', '&'); // XML/HTML 安全
<?php
$query = 'name=John&age=30&hobbies[]=reading&hobbies[]=coding';
// 解析到數組(推薦)
$arr = [];
parse_str($query, $arr);
print_r($arr);
/*
Array
(
[name] => John
[age] => 30
[hobbies] => Array ( [0] => reading [1] => coding )
)
*/
// 直接解析到當前符號表(不推薦,污染命名空間)
parse_str($query);
echo $name; // John
<?php
// 獲取所有頭(數字索引)
$headers = get_headers('https://www.example.com');
/*
Array
(
[0] => HTTP/1.1 200 OK
[1] => Server: nginx
[2] => Date: Mon, 01 Jan 2024 00:00:00 GMT
...
)
*/
// 關聯數組格式(PHP 5.4+)
$headers = get_headers('https://www.example.com', 1);
/*
Array
(
[0] => HTTP/1.1 200 OK
[Server] => nginx
[Date] => Mon, 01 Jan 2024 00:00:00 GMT
[Set-Cookie] => Array ( [0] => ... [1] => ... )
)
*/
// 僅獲取頭部,不下載 Body(流式上下文)
$ctx = stream_context_create(['http' => ['method' => 'HEAD']]);
$headers = get_headers('https://www.example.com', 1, $ctx);
<?php
$meta = get_meta_tags('https://www.example.com');
/*
Array
(
[keywords] => PHP, 教程, 開發
[description] => 專業的 PHP 技術博客
[generator] => WordPress 6.4
[viewport] => width=device-width, initial-scale=1
)
*/
// 注意:只能獲取 和 ,無法獲取 property (og:*) 或 charset
function buildApiUrl(string $baseUrl, string $endpoint, array $params = []): string {
$url = rtrim($baseUrl, '/') . '/' . ltrim($endpoint, '/');
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
return $url;
}
// 使用
$apiUrl = buildApiUrl('https://api.example.com', '/v1/users', [
'page' => 1,
'per_page' => 20,
'filter' => ['active' => true, 'role' => 'admin']
]);
function safeRedirect(string $targetUrl, array $allowedHosts = []): void {
$parsed = parse_url($targetUrl);
if (!$parsed || !isset($parsed['host'])) {
fallbackRedirect(); return;
}
if (!empty($allowedHosts) && !in_array($parsed['host'], $allowedHosts)) {
fallbackRedirect(); return;
}
header('Location: ' . $targetUrl, true, 302);
exit;
}
function normalizeUrl(string $url): string {
$parsed = parse_url($url);
$normalized = $parsed['scheme'] . '://' . $parsed['host'];
if (!empty($parsed['port'])) $normalized .= ':' . $parsed['port'];
$normalized .= $parsed['path'] ?? '/';
if (!empty($parsed['query'])) {
parse_str($parsed['query'], $params);
ksort($params); // 參數排序
$normalized .= '?' . http_build_query($params);
}
// 故意丟棄 fragment
return $normalized;
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 雙重編碼 | 已編碼字符串再次 urlencode | 編碼前檢查,或用 rawurldecode 先解碼 |
| parse_url 解析失敗 | URL 缺少 Scheme 或含非法字符 | 預處理補全 https://,或用 filter_var 驗證 |
| get_headers 超時/失敗 | 目標服務器無響應/證書錯誤 | 設置 stream context timeout、verify_peer=false |
| 中文參數亂碼 | 編碼不一致(GBK vs UTF-8) | 統一項目為 UTF-8,輸入端強制轉換 |
PHP 的 URL 處理函數族覆蓋了從底層編碼到高層語義解析的完整鏈路。核心記住:Query 參數用 urlencode,路徑用 rawurlencode;解析用 parse_url + parse_str;構建用 http_build_query;外部請求頭用 get_headers;SEO 元數據用 get_meta_tags。掌握這組組合拳,URL 相關需求輕鬆應對。