$pattern = "/http:[\/]{2}[a-z]+[.]{1}[a-z\d\-]+[.]{1}[a-z\d]*[\/]*[A-Za-z\d]*[\/]*[A-Za-z\d]*[.]*html/";
問題:
https://,不支援 https。a.b),無法匹配 localhost、example.com、a.b.c.d。.html 結尾。[\/]{2} → //,[.]{1} → \.。<?php
$url = 'https://user:pass@example.com:8080/path?query=value#fragment';
// 1. filter_var (PHP 5.2+) - 最簡單、覆蓋 RFC 基礎規則
if (filter_var($url, FILTER_VALIDATE_URL)) {
echo "合法 URL";
}
// 2. 額外要求:必須有 scheme + host
if (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
echo "完整 URL";
}
// 3. 解析組件
$parts = parse_url($url);
print_r($parts);
/* Array
(
[scheme] => https
[user] => user
[pass] => pass
[host] => example.com
[port] => 8080
[path] => /path
[query] => query=value
[fragment] => fragment
) */
$regex = '~
^ # 開始
(?:https?|ftp):// # 協議
(?: # 用戶信息 (可選)
[^\s:@/]+(?::[^\s:@/]*)?@
)?
(?: # 主機
(?:[a-z0-9-]+\.)+[a-z]{2,} # 域名
| # 或
\d{1,3}(?:\.\d{1,3}){3} # IPv4
| # 或
\[[a-f0-9:]+\] # IPv6
| # 或
localhost # 本地
)
(?::\d{1,5})? # 端口 (可選)
(?:/[^\s?#]*)? # 路徑 (可選)
(?:\?[^\s#]*)? # 查詢 (可選)
(?:\#[^\s]*)? # 片段 (可選)
$ # 結束
~ix';
參考 Mathias Bynens 收集 或 league/uri 庫內部正則。
// 需 intl 擴展
$idn = idn_to_ascii('中文域名.中国'); // -> xn--fiqs8s.xn--fiqs8s
// 驗證時先轉 ASCII 再用正則/filter_var
filter_var($url, FILTER_VALIDATE_URL):標準庫、無依賴、持續更新、覆蓋絕大多數合法 URL。parse_url() 解析後進一步判斷 $parts['scheme'], $parts['host'] 等。parse_url 取 host,做 DNS 解析、IP 白名單/黑名單、內網 IP 檢測 (filter_var($ip, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))。function isValidUrl(string $url, array $allowedSchemes = ['http', 'https']): bool {
if (!filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
return false;
}
$parts = parse_url($url);
if (!in_array(strtolower($parts['scheme']), $allowedSchemes, true)) {
return false;
}
// 可選:禁止內網 IP (防 SSRF)
if (isset($parts['host'])) {
$ip = gethostbyname($parts['host']); // 簡單解析,生產用 dns_get_record
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
return false; // 內網/保留 IP
}
}
return true;
}