首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP URL 解析完全指南:parse_url 與 parse_str 深度解析

     編輯:PHP 相關知識

前言

PHP 提供了兩個強大的內建函數處理 URL 解析:parse_url() 用於分解 URL 結構,parse_str() 用於解析查詢字串。本文深度解析兩個函數的參數、返回值、邊界情況,並展示實戰應用場景(含知名專案 php-resque 的原始碼分析)。

parse_url():URL 結構分解

函數簽名

mixed parse_url(string $url, int $component = -1)

參數說明

  • $url:要解析的 URL,無效字元會被替換為底線
  • $component:可選,指定返回特定組件(常數):
    • PHP_URL_SCHEME - 協定(如 http)
    • PHP_URL_HOST - 主機名
    • PHP_URL_PORT - 埠號(返回 integer)
    • PHP_URL_USER - 用戶名
    • PHP_URL_PASS - 密碼
    • PHP_URL_PATH - 路徑
    • PHP_URL_QUERY - 查詢字串(? 之後)
    • PHP_URL_FRAGMENT - 錨點(# 之後)

返回值

  • 省略 $component:返回關聯陣列,包含存在的組件
  • 指定 $component:返回字串(PORT 除外返回整數)
  • 嚴重格式錯誤:返回 false
  • 指定組件不存在:返回 null

基本範例

<?php
$url = 'https://username:password@hostname/path?arg=value#anchor';

print_r(parse_url($url));
/* 輸出:
Array
(
    [scheme] => http
    [host] => hostname
    [user] => username
    [pass] => password
    [path] => /path
    [query] => arg=value
    [fragment] => anchor
)
*/

echo parse_url($url, PHP_URL_PATH); // /path
?>

邊界情況處理

<?php
// 不完整 URL 也能盡力解析
parse_url('https://example.com');           // 正常
parse_url('example.com/path');             // host 會被歸類為 path
parse_url('//example.com/path');           // 正常解析 host
parse_url('mailto:user@example.com');      // scheme=mailto, path=user@example.com

// 無效 URL
parse_url('https:///path');                 // 可能返回 false
?>

parse_str():查詢字串解析

函數簽名

void parse_str(string $str, array &$arr = null)

參數說明

  • $str:查詢字串(如 a=1&b=2)
  • $arr:可選引用參數,若提供則將解析結果存入此陣列,否則直接註冊到當前符號表(不推薦)

基本範例

<?php
$str = "first=value&arr[]=foo+bar&arr[]=baz";

// 方式一:直接註冊變數(污染符號表,不推薦)
parse_str($str);
echo $first;    // value
echo $arr[0];   // foo bar
echo $arr[1];   // baz

// 方式二:存入陣列(推薦,安全)
$output = array();
parse_str($str, $output);
echo $output['first'];   // value
echo $output['arr'][0];  // foo bar
echo $output['arr'][1];  // baz
?>

特殊處理規則

  • 空格轉為 + 或 %20
  • 方括號 [] 表示陣列結構
  • 點號 . 和空格在變數名中會轉為底線 _
  • 重複鍵名後者覆蓋前者(除非使用 [])

實戰應用:Redis 連線字串解析(php-resque 源碼分析)

知名佇列專案 php-resque 巧妙運用這兩個函數解析 Redis DSN:

Redis DSN 格式

redis://user:pass@host:port/db?option1=val1&option2=val2
tcp://user:pass@host:port/db?option1=val1&option2=val2
host:port  (簡寫格式)

解析實現源碼

<?php
/**
 * 解析 Redis DSN 字串
 * @param string $dsn DSN 字串
 * @return array [host, port, database, user, pass, options]
 */
public static function parseDsn($dsn) {
    // 空 DSN 使用預設值
    if ($dsn == '') {
        $dsn = 'redis://' . self::DEFAULT_HOST;
    }
    
    // 1. 使用 parse_url 分解主要結構
    $parts = parse_url($dsn);
    
    // 2. 驗證協定
    $validSchemes = array('redis', 'tcp');
    if (isset($parts['scheme']) && !in_array($parts['scheme'], $validSchemes)) {
        throw new \InvalidArgumentException("Invalid DSN. Supported schemes: " . implode(', ', $validSchemes));
    }
    
    // 3. 處理簡寫格式:parse_url 會將 'host:port' 歸類為 path
    if (!isset($parts['host']) && isset($parts['path'])) {
        $parts['host'] = $parts['path'];
        unset($parts['path']);
    }
    
    // 4. 提取埠號
    $port = isset($parts['port']) ? intval($parts['port']) : self::DEFAULT_PORT;
    
    // 5. 從 path 提取資料庫編號
    $database = false;
    if (isset($parts['path'])) {
        // 移除非數字字元(如開頭的 /)
        $database = intval(preg_replace('/[^0-9]/', '', $parts['path']));
    }
    
    // 6. 提取用戶名密碼
    $user = isset($parts['user']) ? $parts['user'] : false;
    $pass = isset($parts['pass']) ? $parts['pass'] : false;
    
    // 7. 使用 parse_str 解析查詢參數
    $options = array();
    if (isset($parts['query'])) {
        parse_str($parts['query'], $options);
    }
    
    return array($parts['host'], $port, $database, $user, $pass, $options);
}
?>

進階技巧與最佳實踐

1. 取得當前請求的查詢字串

<?php
// 獲取原始查詢字串
$queryString = $_SERVER['QUERY_STRING'];

// 解析為陣列
$params = array();
parse_str($queryString, $params);
?>

2. 重建 URL(修改特定參數)

<?php
function modifyUrlQuery($url, $modifications) {
    $parts = parse_url($url);
    $query = array();
    
    if (isset($parts['query'])) {
        parse_str($parts['query'], $query);
    }
    
    // 應用修改
    foreach ($modifications as $key => $value) {
        if ($value === null) {
            unset($query[$key]);
        } else {
            $query[$key] = $value;
        }
    }
    
    // 重建
    $newQuery = http_build_query($query);
    $newUrl = (isset($parts['scheme']) ? $parts['scheme'] . '://' : '')
            . (isset($parts['host']) ? $parts['host'] : '')
            . (isset($parts['path']) ? $parts['path'] : '')
            . ($newQuery ? '?' . $newQuery : '')
            . (isset($parts['fragment']) ? '#' . $parts['fragment'] : '');
    
    return $newUrl;
}

// 使用
$url = 'https://example.com/path?a=1&b=2';
echo modifyUrlQuery($url, array('b' => 3, 'c' => 4)); 
// https://example.com/path?a=1&b=3&c=4
?>

3. 安全的 URL 解析封裝

<?php
class UrlParser {
    public static function parse($url) {
        $result = parse_url($url);
        
        if ($result === false) {
            throw new InvalidArgumentException("Invalid URL format: $url");
        }
        
        // 確保關鍵組件存在
        $result += array(
            'scheme' => 'http',
            'host' => '',
            'port' => null,
            'path' => '/',
            'query' => '',
            'fragment' => ''
        );
        
        // 解析查詢參數
        $result['query_params'] = array();
        if ($result['query']) {
            parse_str($result['query'], $result['query_params']);
        }
        
        return $result;
    }
}
?>

常見陷阱與注意事項

陷阱說明解決方案
無 scheme 的 URLparse_url 將 host 歸類為 path預檢補全 scheme 或檢查 host 是否為空
parse_str 污染變數未提供 $arr 參數會註冊到符號表永遠傳入第二個參數接收結果
方括號編碼問題[] 在 URL 中需編碼為 %5B%5D使用 http_build_query 自動處理
片段識別符不發送到伺服器#anchor 僅在瀏覽器端使用勿依賴 $_SERVER 獲取 fragment
國際化域名需使用 idn_to_ascii 轉換解析前先轉換 Punycode

總結

parse_url() 與 parse_str() 是 PHP 處理 URL 的黃金搭檔:前者負責結構分解,後者負責查詢參數解析。php-resque 的實現展示了組合使用這兩個函數解析複雜 DSN 格式的經典模式。掌握兩者的邊界行為與最佳實踐,能避免常見陷阱,編寫健壯的 URL 處理程式碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved