$_SERVER所有 URL 相關資訊皆來自 $_SERVER,關鍵鍵值如下:
| 鍵名 | 說明 | 範例 |
|---|---|---|
REQUEST_URI | 請求 URI(含查詢字串) | /path/script.php?a=1&b=2 |
PHP_SELF | 當前腳本路徑(相對 DocumentRoot) | /path/script.php |
QUERY_STRING | 查詢字串(不含 ?) | a=1&b=2 |
HTTP_HOST | Host 標頭(含端口若非標準) | example.com:8080 |
SERVER_NAME | 伺服器設定主機名 | example.com |
SERVER_PORT | 伺服器監聽端口 | 80 / 443 |
HTTPS | 是否 HTTPS(on/off) | on |
REQUEST_SCHEME | 請求協定(http/https) | https |
HTTP_REFERER | 來源頁面 URL(不可靠) | https://google.com/ |
function getCurrentUrl(): string
{
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
(isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] === 'https')
? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? '';
$uri = $_SERVER['REQUEST_URI'] ?? '';
// 非標準端口才顯示
$port = $_SERVER['SERVER_PORT'] ?? 80;
if (($scheme === 'http' && $port != 80) || ($scheme === 'https' && $port != 443)) {
// HTTP_HOST 已含端口,避免重複
if (strpos($host, ':') === false) {
$host .= ':' . $port;
}
}
return $scheme . '://' . $host . $uri;
}
function getDomain(): string
{
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? '';
return preg_replace('/:\d+$/', '', $host); // 移除端口
}
function getBaseUrl(): string
{
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? '';
return $scheme . '://' . $host;
}
function getScriptDirUrl(): string
{
$base = getBaseUrl();
$scriptDir = dirname($_SERVER['SCRIPT_NAME'] ?? $_SERVER['PHP_SELF'] ?? '/');
return rtrim($base . $scriptDir, '/') . '/';
}
HTTP_HOST vs SERVER_NAME:前者來自客戶端 Host 標頭(可偽造),後者來自伺服器設定(可信)。生產環境建議用 SERVER_NAME 或經白名單驗證的 HTTP_HOST。HTTP_X_FORWARDED_PROTO、HTTP_X_FORWARDED_PORT,需信任代理並正確設定 trusted_proxies(Laravel/Symfony 等框架內建處理)。$_SERVER['REQUEST_URI'] 等鍵不存在,需判斷 php_sapi_name() === 'cli' 避免報錯。REQUEST_URI 為原始編碼,若需解碼路徑段落請用 rawurldecode()。$url = 'https://user:pass@example.com:8080/path?query=value#fragment';
$parts = parse_url($url);
/*
[
'scheme' => 'https',
'host' => 'example.com',
'port' => 8080,
'user' => 'user',
'pass' => 'pass',
'path' => '/path',
'query' => 'query=value',
'fragment' => 'fragment'
]
*/
// 解析查詢字串為陣列
parse_str($parts['query'] ?? '', $queryArr);
// $queryArr = ['query' => 'value']
// 重建 URL(修改某參數)
$queryArr['page'] = 2;
$newQuery = http_build_query($queryArr);
$newUrl = $parts['scheme'] . '://' . $parts['host']
. (isset($parts['port']) ? ':' . $parts['port'] : '')
. $parts['path'] . '?' . $newQuery;
final class CurrentUrl
{
private string $scheme;
private string $host;
private int $port;
private string $path;
private string $query;
public static function fromGlobals(): self
{
$https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (($_SERVER['REQUEST_SCHEME'] ?? '') === 'https');
$self = new self();
$self->scheme = $https ? 'https' : 'http';
$self->host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
$self->port = (int)($_SERVER['SERVER_PORT'] ?? ($https ? 443 : 80));
$self->path = $_SERVER['REQUEST_URI'] ?? '/';
if (($pos = strpos($self->path, '?')) !== false) {
$self->query = substr($self->path, $pos + 1);
$self->path = substr($self->path, 0, $pos);
} else {
$self->query = '';
}
return $self;
}
public function full(): string
{
$host = $this->host;
if (($this->scheme === 'http' && $this->port !== 80) ||
($this->scheme === 'https' && $this->port !== 443)) {
if (strpos($host, ':') === false) $host .= ':' . $this->port;
}
return $this->scheme . '://' . $host . $this->path
. ($this->query ? '?' . $this->query : '');
}
public function domain(): string { return preg_replace('/:\d+$/', '', $this->host); }
public function base(): string { return $this->scheme . '://' . $this->host; }
public function path(): string { return $this->path; }
public function query(): string { return $this->query; }
public function queryArray(): array { parse_str($this->query, $arr); return $arr; }
}
// 使用
$url = CurrentUrl::fromGlobals();
echo $url->full(); // 完整 URL
echo $url->domain(); // 僅域名
echo $url->base(); // 基礎 URL
掌握 $_SERVER 關鍵鍵值差異、正確處理 HTTPS 與非標準端口、注意代理環境下的 Header 信任鏈,即能可靠取得各種形式的 URL。建議封裝為工具類或使用框架內建 Request 物件,避免重複造輪子與漏洞。