PHP 提供 parse_url() 解析 URL 組件、parse_str() 解析查詢字符串,配合使用可輕鬆將 https://example.com/path?a=1&b=2 轉為結構化數組。本文詳解兩大核心函數、自定義封裝、邊界情況處理與現代框架實踐。
| 函數 | 作用 | 返回值 | 關鍵參數 |
|---|---|---|---|
| parse_url($url, $component) | 解析 URL 為組件 | 關聯數組 / 指定組件字符串 | $component: PHP_URL_SCHEME/HOST/PATH/QUERY/FRAGMENT 等 |
| parse_str($str, $result) | 解析查詢字符串填充數組 | void (引用修改 $result) | $result 必須傳變量引用 |
<?php
$url = "https://user:pass@example.com:8080/path/to/page?name=value&foo=bar#section";
// 1. parse_url 解析整體結構
$parts = parse_url($url);
/*
Array(
[scheme] => https
[host] => example.com
[port] => 8080
[user] => user
[pass] => pass
[path] => /path/to/page
[query] => name=value&foo=bar
[fragment] => section
)
*/
// 2. 提取 query 部分
$queryString = $parts['query'] ?? '';
// 3. parse_str 解析查詢字符串為數組
$params = [];
parse_str($queryString, $params);
/*
Array(
[name] => value
[foo] => bar
)
*/
// 4. 直接獲取特定組件 (更高效)
$queryOnly = parse_url($url, PHP_URL_QUERY); // 返回 "name=value&foo=bar"
parse_str($queryOnly, $params);
?>
<?php
/**
* 解析 URL 獲取所有查詢參數
* @param string $url 完整 URL 或僅查詢字符串
* @return array 參數鍵值對數組
*/
function getUrlParams(string $url): array {
// 若傳入完整 URL,提取 query 部分
if (strpos($url, '?') !== false || strpos($url, '://') !== false) {
$query = parse_url($url, PHP_URL_QUERY);
if ($query === null) return [];
} else {
$query = $url; // 假設直接傳入查詢字符串
}
$params = [];
parse_str($query, $params);
return $params;
}
/**
* 獲取單個參數值 (不存在返回默認值)
*/
function getUrlParam(string $url, string $name, $default = null) {
$params = getUrlParams($url);
return $params[$name] ?? $default;
}
// === 使用 ===
$url = "https://shop.com/list?category=electronics&page=2&sort=price_desc&tags[]=new&tags[]=hot";
$params = getUrlParams($url);
/*
[
'category' => 'electronics',
'page' => '2',
'sort' => 'price_desc',
'tags' => ['new', 'hot'] // parse_str 自動處理 [] 為數組
]
*/
echo getUrlParam($url, 'page', 1); // '2' (字符串)
echo getUrlParam($url, 'limit', 20); // 20 (默認值)
?>
key[]=val<?php
parse_str('tags[]=php&tags[]=js&tags[]=vue', $arr);
// $arr['tags'] = ['php', 'js', 'vue']
?>
<?php
parse_str('name=%E5%BC%A0%E4%B8%89&city=%E5%8C%97%E4%BA%AC', $arr);
// $arr['name'] = '張三', $arr['city'] = '北京'
?>
<?php
parse_str('id=1&id=2&id=3', $arr);
// $arr['id'] = '3' // 非數組模式下後蓋前
// 若需保留所有值:id[]=1&id[]=2&id[]=3
?>
<?php
parse_str('user.name=john&user age=25', $arr);
// $arr['user_name'] = 'john', $arr['user_age'] = '25'
// 原因:PHP 變量名不能含點、空格,parse_str 自動轉換
?>
<?php
/**
* 自定義查詢字符串解析 (保留原始鍵名、不自動解碼、保留重複鍵)
* @param string $query
* @param bool $keepDuplicates 是否保留重複鍵為數組
* @return array
*/
function parseQueryCustom(string $query, bool $keepDuplicates = true): array {
$params = [];
$pairs = explode('&', $query);
foreach ($pairs as $pair) {
if ($pair === '') continue;
[$key, $value] = array_pad(explode('=', $pair, 2), 2, '');
// 不自動 urldecode,保留原始編碼
// 不轉換鍵名特殊字符
if ($keepDuplicates && isset($params[$key])) {
if (!is_array($params[$key])) $params[$key] = [$params[$key]];
$params[$key][] = $value;
} else {
$params[$key] = $value;
}
}
return $params;
}
// 對比
$query = 'id=1&id=2&user.name=john';
print_r(parse_str($query, $arr) && $arr); // parse_str 行為
print_r(parseQueryCustom($query)); // 自定義行為
?>
<?php
// PSR-7 ServerRequest (所有現代框架基礎)
$request = ServerRequestFactory::fromGlobals(); // 或框架注入的 $request
$queryParams = $request->getQueryParams(); // 已解析好的關聯數組
$page = $request->getQueryParams()['page'] ?? 1;
// Laravel Request
$page = $request->input('page', 1);
$allQuery = $request->query()->all(); // 只獲取 query 參數,不含 body
// Symfony Request
$page = $request->query->get('page', 1);
?>
<?php
// 1. http_build_query (標準構建 query 字符串)
$params = ['page' => 2, 'sort' => 'desc', 'tags' => ['a', 'b']];
$query = http_build_query($params); // page=2&sort=desc&tags%5B0%5D=a&tags%5B1%5D=b
// 2. 完整 URL 構建 (PECL pecl_http 擴展提供 http_build_url)
// 手動拼接通用版
function buildUrl(string $base, array $params = []): string {
$url = $base;
if ($params) {
$separator = strpos($base, '?') === false ? '?' : '&';
$url .= $separator . http_build_query($params);
}
return $url;
}
echo buildUrl('https://api.com/users', ['page' => 3, 'per_page' => 20]);
// https://api.com/users?page=3&per_page=20
?>
| 情況 | parse_url/parse_str 行為 | 處理建議 |
|---|---|---|
| 無 query 部分 | parse_url 返回 null/無 query 鍵 | 判斷 null 返回空數組 |
| 空 query (?) | parse_str 不報錯,返回空數組 | 正常處理 |
| 片段標譤 # | parse_url 正確分離 fragment | 忽略 fragment,不參與參數解析 |
| 用戶信息 user:pass@ | parse_url 解析 user/pass | 敏感信息,勿記錄日志 |
| 非標準端口 | parse_url 解析 port | 構建 URL 時決定是否顯式帶端口 |
| 相對路徑 URL (/path?a=1) | parse_url 正常解析 path/query | 補全 base URL 後再解析 |
結語:parse_url() + parse_str() 是 PHP 解析 URL 參數的標準組合拳,掌握其自動數組化、自動解碼、鍵名轉換等特性與邊界行為,並封裝通用工具函數,能高效應對從簡單參數提取到複雜 URL 構建解析的各類需求。