PHP $_SERVER 超全局數組中與 URL 路徑相關的四個變量,是構建路由、生成鏈接、安全檢查、日志記錄的基石。本文通過具體示例對比,建立清晰的心智模型。
| 變量 | 含義 | 包含內容 | 典型值示例 |
|---|---|---|---|
$_SERVER['QUERY_STRING'] | 查詢字符串 (問號後) | 不含 ?,原始未解碼 | p=1&q=test |
$_SERVER['REQUEST_URI'] | 請求 URI (域名後完整路徑) | 含路徑、查詢串、錨點 (瀏覽器不發送錨點) | /index.php?p=1&q=test |
$_SERVER['SCRIPT_NAME'] | 當前執行腳本路徑 | 從網站根目錄起的絕對路徑 | /index.php |
$_SERVER['PHP_SELF'] | 當前腳本文件名 (相對 DocumentRoot) | 同 SCRIPT_NAME,但 CGI 模式下可能不同 | /index.php |
<?php
// 測試入口:https://example.com/index.php?p=1&q=test#anchor
echo 'QUERY_STRING: ' . $_SERVER['QUERY_STRING'] . "\n"; // p=1&q=test
echo 'REQUEST_URI: ' . $_SERVER['REQUEST_URI'] . "\n"; // /index.php?p=1&q=test
echo 'SCRIPT_NAME: ' . $_SERVER['SCRIPT_NAME'] . "\n"; // /index.php
echo 'PHP_SELF: ' . $_SERVER['PHP_SELF'] . "\n"; // /index.php
// 子目錄項目:https://example.com/shop/index.php?act=goods
// QUERY_STRING: act=goods
// REQUEST_URI: /shop/index.php?act=goods
// SCRIPT_NAME: /shop/index.php
// PHP_SELF: /shop/index.php
<?php
// 1. 獲取完整當前 URL (含協議、域名、端口、路徑、查詢)
function getCurrentUrl(): string
{
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
return $scheme . '://' . $host . $uri;
}
// 2. 獲取當前腳本基礎 URL (無查詢參數)
function getScriptBaseUrl(): string
{
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$script = $_SERVER['SCRIPT_NAME'] ?? '/index.php';
return $scheme . '://' . $host . $script;
}
// 3. 獲取項目根目錄 URL (去掉入口文件)
function getBasePath(): string
{
$baseUrl = getScriptBaseUrl();
return rtrim(dirname($baseUrl), '/') . '/';
}
// 4. 安全重定向回來的頁面 (防開放重定向)
function safeRedirectBack(string $fallback = '/'): void
{
$referer = $_SERVER['HTTP_REFERER'] ?? '';
$host = parse_url($referer, PHP_URL_HOST);
$allowedHosts = [$_SERVER['HTTP_HOST'], 'www.example.com']; // 白名單
if ($referer && in_array($host, $allowedHosts)) {
header('Location: ' . $referer);
} else {
header('Location: ' . $fallback);
}
exit;
}
| 場景 | 推薦變量 | 錯誤用法後果 |
|---|---|---|
| 構建當前頁面完整 URL (含參數) | REQUEST_URI | 用 PHP_SELF 丟失查詢參數 |
| 表單 action 自提交 | SCRIPT_NAME 或 PHP_SELF | 用 REQUEST_URI 導致參數疊加、無限循環 |
| 獲取查詢參數原始字符串 | QUERY_STRING | 手動拼接 $_GET 丟失重複鍵、編碼不一致 |
| 路由匹配/權限判斷 | REQUEST_URI (解析 path) | 用 SCRIPT_NAME 無法區分 PATH_INFO 模式路由 |
| XSS 防護輸出 URL | htmlspecialchars($url, ENT_QUOTES) | 直接輸出 PHP_SELF 導致反射型 XSS (/script.php/">) |
<?php
// 危險代碼:直接輸出 PHP_SELF 作為表單 action
// 請求:/index.php/">
// 響應:
SCRIPT_NAME:Nginx fastcgi_param SCRIPT_NAME 設置值,通常為 $fastcgi_script_name (如 /index.php)。PHP_SELF:PHP 內部根據 SCRIPT_NAME 推導,通常相同。REQUEST_URI:Nginx $request_uri,保留原始請求路徑 (含未解碼參數、雙斜杠)。PATH_INFO:需 Nginx 配置 fastcgi_split_path_info 並傳遞 fastcgi_param PATH_INFO,PHP 才能在 $_SERVER['PATH_INFO'] 獲取。徹底搞清四大變量的語義邊界,配合安全編碼習慣,路由系統、鏈接生成、重定向邏輯才能經得起生產環境考驗。