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

PHP 獲取當前頁面完整 URL 完整指南與 $_SERVER 變數深度解析

     編輯:PHP 相關知識

核心變數:$_SERVER 超全域陣列

PHP 透過 $_SERVER 提供伺服器與執行環境資訊,關鍵 URL 相關鍵值如下:

鍵名說明範例值
REQUEST_URI請求 URI(含查詢字串),不含域名/article/123?page=2&sort=desc
HTTP_HOST客戶端請求的 Host 標頭(含端口若非標準)example.com:8080
SERVER_NAME伺服器設定的主機名(VirtualHost ServerName)example.com
SERVER_PORT伺服器監聽埠80, 443, 8080
HTTPSHTTPS 請求時為 'on',否則未設定on
SCRIPT_NAME當前執行腳本路徑/index.php
PHP_SELF同 SCRIPT_NAME(CGI 模式下可能不同)/index.php
QUERY_STRING查詢字串(不含 ?)page=2&sort=desc
DOCUMENT_ROOT網站根目錄絕對路徑/var/www/html

獲取完整 URL 的標準函數

<?php
/**
 * 獲取當前頁面完整 URL
 * @param bool $withQuery 是否包含查詢字串
 * @return string 完整 URL
 */
function getCurrentUrl($withQuery = true) {
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
    $uri = $_SERVER['REQUEST_URI'] ?? '';

    if (!$withQuery) {
        $uri = strtok($uri, '?');
    }

    // 處理非標準埠口(HTTP:80, HTTPS:443 為標準,不顯示)
    $port = $_SERVER['SERVER_PORT'] ?? 80;
    if (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) {
        $host .= ':' . $port;
    }

    return "{$protocol}://{$host}{$uri}";
}

// 使用範例
echo getCurrentUrl();        // https://example.com/article/123?page=2&sort=desc
echo getCurrentUrl(false);   // https://example.com/article/123
?>

進階變體:不同需求的 URL 組合

<?php
// 1. 僅取得基礎 URL(無查詢字串)
function getBaseUrl() {
    return getCurrentUrl(false);
}

// 2. 取得網站根目錄 URL
function getSiteRootUrl() {
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
    $port = $_SERVER['SERVER_PORT'] ?? 80;
    if (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) {
        $host .= ':' . $port;
    }
    // 移除腳本路徑
    $scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
    $rootPath = rtrim(dirname($scriptName), '/\\');
    return "{$protocol}://{$host}{$rootPath}";
}

// 3. 取得當前目錄 URL
function getCurrentDirUrl() {
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
    $port = $_SERVER['SERVER_PORT'] ?? 80;
    if (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) {
        $host .= ':' . $port;
    }
    $scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
    $dirPath = rtrim(dirname($scriptName), '/\\');
    $requestUri = $_SERVER['REQUEST_URI'] ?? '';
    $requestDir = rtrim(dirname(parse_url($requestUri, PHP_URL_PATH)), '/\\');
    return "{$protocol}://{$host}{$requestDir}";
}

// 4. 重建不含特定參數的 URL(分頁、排序保留,移除搜尋關鍵字等)
function rebuildUrlWithoutParams(array $excludeParams = array()) {
    $query = $_SERVER['QUERY_STRING'] ?? '';
    parse_str($query, $params);
    foreach ($excludeParams as $param) {
        unset($params[$param]);
    }
    $newQuery = http_build_query($params);
    $uri = $_SERVER['REQUEST_URI'] ?? '';
    $baseUri = strtok($uri, '?');
    return $baseUri . ($newQuery ? '?' . $newQuery : '');
}
?>

$_SERVER 變數深度解析與陷阱

1. HTTP_HOST vs SERVER_NAME

  • HTTP_HOST:來自客戶端請求標頭,用戶可控,可能被偽造
  • SERVER_NAME:來自伺服器設定,受信任,但需正確配置 VirtualHost
  • 建議:優先使用 HTTP_HOST(支援多域名、非標準埠口),安全敏感場景驗證 Host 白名單

2. REQUEST_URI vs SCRIPT_NAME vs PHP_SELF

  • REQUEST_URI:原始請求路徑+查詢字串,最完整
  • SCRIPT_NAME:當前執行腳本路徑,不含查詢字串
  • PHP_SELF:歷史相容變數,CGI/Apache 模組下行為可能不同
  • 安全警示:PHP_SELF 易遭 XSS 攻擊(form action=""),請改用 SCRIPT_NAME 或留空 action=""

3. 反向代理/負載均衡環境下的陷阱

<?php
// 代理環境下,真實協定可能在 X-Forwarded-Proto
function getRealProtocol() {
    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
        $protos = explode(',', $_SERVER['HTTP_X_FORWARDED_PROTO']);
        return trim($protos[0]); // 取第一層代理協定
    }
    return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
}

// 真實 Host 可能在 X-Forwarded-Host
function getRealHost() {
    if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
        $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
        return trim($hosts[0]);
    }
    return $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
}
?>

路徑處理輔助函數

<?php
// 取得目錄部分
$path = '/var/www/html/article/123';
echo dirname($path); // /var/www/html/article

// 取得檔名部分
echo basename($path); // 123
echo basename($path, '.php'); // 移除副檔名

// 取得絕對規範路徑(解析 ..、符號連結)
echo realpath('/var/www/html/../html/article'); // /var/www/html/article

// 組合 URL 路徑(避免雙斜線)
function joinPaths($base, $relative) {
    return rtrim($base, '/') . '/' . ltrim($relative, '/');
}
?>

實戰應用場景

1. 規範化 Canonical URL(SEO 必備)

<?php
function getCanonicalUrl() {
    $url = getCurrentUrl(false); // 不含查詢字串的標準 URL
    // 可加入邏輯:移除 utm_* 參數、統一 www/非 www、強制 HTTPS
    return $url;
}
?>
<link rel="canonical" href="<?php echo htmlspecialchars(getCanonicalUrl()); ?>">

2. 表單重導回原頁(含錯誤訊息)

<?php
// 登入/註冊/評論後導回
$redirectUrl = $_SERVER['HTTP_REFERER'] ?? getSiteRootUrl();
// 防開放重導向:驗證同域名
if (parse_url($redirectUrl, PHP_URL_HOST) !== ($_SERVER['HTTP_HOST'] ?? '')) {
    $redirectUrl = getSiteRootUrl();
}
header("Location: {$redirectUrl}");
exit;
?>

3. 多語言/多幣別 URL 切換

<?php
function switchLocaleUrl($newLocale) {
    $uri = $_SERVER['REQUEST_URI'] ?? '';
    // 移除舊語言前綴 /zh-TW/、/en/ 等
    $uri = preg_replace('~^/(zh-TW|zh-CN|en|ja)/~', '/', $uri);
    // 加入新語言前綴
    return '/' . $newLocale . $uri;
}
?>

常見問題速查

Q: 為什麼 $_SERVER['HTTPS'] 有時為空? A: 僅在 HTTPS 請求時設定為 'on'。HTTP 請求、或反向代理終結 SSL 且未傳遞 Header 時皆為空。請檢查 Nginx proxy_set_header X-Forwarded-Proto $scheme;。
Q: URL 中文參數顯示亂碼? A: 瀏覽器自動 URL 編碼。PHP 解碼用 urldecode(),編碼用 urlencode() 或 rawurlencode()(RFC 3986 標準,空格編為 %20 而非 +)。
Q: 如何獲取不含查詢字串的純路徑? A: parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 或 strtok($_SERVER['REQUEST_URI'], '?')。
Q: CLI 模式下 $_SERVER 變數不同? A: CLI 無 HTTP_HOST、REQUEST_URI 等 Web 變數。腳本路徑在 $_SERVER['SCRIPT_FILENAME'] 或 $argv[0]。

結語

掌握 $_SERVER 變數特性與組合技巧,能精準構建各類 URL 需求。核心原則:優先用 REQUEST_URI + HTTP_HOST、注意代理環境 Header、防範 PHP_SELF XSS、善用 parse_url/http_build_query 處理查詢字串。將常用邏輯封裝為工具類別(如 Url::current()、Url::previous()、Url::route()),提升開發效率與程式碼可維護性。

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