日常開發中頻繁遇到需從完整 URL 中提取域名、路徑、查詢參數等結構化信息的需求:OAuth 回調處理、API 簽名驗證、URL 重寫路由分發、爬蟲去重判斷等。PHP 內置 parse_url() 與 parse_str() 可覆蓋 90% 場景,但理解其邊界行為並封裝通用工具函數,能大幅提升代碼健壯性。
| 函數 | 作用 | 返回值類型 |
|---|---|---|
parse_url($url, $component = -1) | 解析 URL 為關聯數組 | array / string / false |
parse_str($string, &$array) | 將查詢字符串解析為變量 | void (引用賦值) |
http_build_query($data) | 數組構建查詢字符串 | string |
<?php
$url = 'https://api.example.com/v1/users?status=active&page=2&limit=20&sort=desc';
// 1. 解析 URL 主體結構
$parts = parse_url($url);
/*
輸出結構:
array(
'scheme' => 'https',
'host' => 'api.example.com',
'path' => '/v1/users',
'query' => 'status=active&page=2&limit=20&sort=desc'
)
*/
// 2. 將 query 字符串轉為關聯數組(推薦用 parse_str)
$params = [];
parse_str($parts['query'], $params);
/*
$params = [
'status' => 'active',
'page' => '2',
'limit' => '20',
'sort' => 'desc'
]
*/
// 3. 修改參數後重新構建 URL
$params['page'] = 3; // 翻頁
$params['debug'] = 'true'; // 新增調試參數
$newQuery = http_build_query($params);
$newUrl = $parts['scheme'].'://'.$parts['host'].$parts['path'].'?'.$newQuery;
// 結果:https://api.example.com/v1/users?status=active&page=3&limit=20&sort=desc&debug=true
echo $newUrl;
<?php
/**
* 將查詢字符串安全解析為關聯數組
* @param string $query 不含 ? 的 query 部分
* @return array
*/
function convertUrlQuery(string $query): array {
$params = [];
// 支援 & 和 & 兩種分隔符
$pairs = preg_split('/[&;]/', $query);
foreach ($pairs as $pair) {
if ($pair === '') continue;
// 僅在第一個 = 分割,值中可能包含 =
[$key, $value] = array_pad(explode('=', $pair, 2), 2, '');
$params[urldecode($key)] = urldecode($value);
}
return $params;
}
/**
* 關聯數組編碼為標準查詢字符串
* @param array $params
* @param int $enc_type PHP_QUERY_RFC1738 (空格編碼為 +) 或 PHP_QUERY_RFC3986 (空格編碼為 %20)
* @return string
*/
function getUrlQuery(array $params, int $enc_type = PHP_QUERY_RFC3986): string {
return http_build_query($params, '', '&', $enc_type);
}
// 測試
$raw = 'name=John+Doe&city=Shanghai&tag=php%20code';
$arr = convertUrlQuery($raw);
// ['name'=>'John Doe','city'=>'Shanghai','tag'=>'php code']
$rebuilt = getUrlQuery($arr);
// name=John+Doe&city=Shanghai&tag=php+code (RFC1738) 或 %20 (RFC3986)
?ids[]=1&ids[]=2 用 parse_str 直接得到 $ids = [1,2];手寫解析需自行識別 [] 後綴。htmlspecialchars() 雙重編碼防 XSS;API 簽名時嚴格按 RFC3986 編碼(http_build_query($arr, '', '&', PHP_QUERY_RFC3986))。parse_url('/path?k=v') 返回 ['path'=>'/path','query'=>'k=v'],無 scheme/host,拼接時需判斷補全。parse_url 遇到非法 URL 返回 false,務必先判斷再訪問下標。<?php
class UrlParser {
public static function parse(string $url): array {
$parts = parse_url($url);
if ($parts === false) throw new InvalidArgumentException('Invalid URL');
$parts['query_params'] = isset($parts['query']) ? self::parseQuery($parts['query']) : [];
return $parts;
}
public static function parseQuery(string $query): array {
$params = [];
parse_str($query, $params);
return $params;
}
public static function build(string $baseUrl, array $extraParams = []): string {
$parts = self::parse($baseUrl);
$parts['query_params'] = array_merge($parts['query_params'], $extraParams);
$query = http_build_query($parts['query_params'], '', '&', PHP_QUERY_RFC3986);
return ($parts['scheme']??'').'://'.($parts['host']??'').($parts['path']??'').($query?'?'.$query:'');
}
}
// 用法
$newUrl = UrlParser::build('https://api.example.com/v1/users?status=active', ['page'=>3, 'debug'=>true]);
💡 總結:日常開發首選 parse_url() + parse_str() + http_build_query() 內置三件套,性能最優且處理邊界情況最完善;特殊編碼需求或舊系統兼容時再引入自定義封裝。