從任意 URL/文本中提取主域名 (如 example.com、sub.example.co.uk),支援多級後綴 (.com.cn, .org.hk 等)。
| 方法 | 優點 | 缺點 | 推薦度 |
|---|---|---|---|
parse_url() + 公共後綴列表 | 標準庫、無依賴、準確 | 需維護後綴列表 (PSL) | ⭐⭐⭐⭐⭐ 生產首選 |
| 正則表達式 | 無依賴、靈活 | 難覆蓋所有後綴、邊界情況多 | ⭐⭐⭐ 簡單場景 |
| 第三方庫 (TLDExtract 等) | 自動更新 PSL、極準確 | 需 Composer 依賴 | ⭐⭐⭐⭐⭐ 企業級 |
<?php
/**
* 獲取註冊域名 (eTLD+1),基於 Public Suffix List
* 需定期更新 $publicSuffixes 列表
*/
function getRegisteredDomain(string $url, array $publicSuffixes = []): ?string {
// 1. 解析 Host
$parsed = parse_url($url);
if (!$parsed || empty($parsed['host'])) return null;
$host = strtolower($parsed['host']);
// 2. 移除端口
$host = preg_replace('/:\d+$/', '', $host);
// 3. IP 地址直接返回
if (filter_var($host, FILTER_VALIDATE_IP)) return $host;
// 4. 內置常見後綴 (生產環境請下載完整 PSL: https://publicsuffix.org/list/public_suffix_list.dat)
$defaultSuffixes = [
'com', 'net', 'org', 'edu', 'gov', 'mil', 'int',
'cn', 'com.cn', 'net.cn', 'org.cn', 'gov.cn',
'hk', 'com.hk', 'net.hk', 'org.hk',
'tw', 'com.tw', 'org.tw', 'idv.tw',
'jp', 'co.jp', 'ne.jp', 'or.jp',
'uk', 'co.uk', 'org.uk', 'me.uk',
'eu', 'de', 'fr', 'it', 'nl', 'es', 'ru', 'br', 'au', 'ca',
'info', 'biz', 'name', 'mobi', 'pro', 'xxx', 'io', 'ai', 'co', 'tv', 'me',
// ... 更多
];
$suffixes = $publicSuffixes ? $publicSuffixes : $defaultSuffixes;
// 5. 從長到短匹配最長後綴
$parts = explode('.', $host);
$count = count($parts);
if ($count < 2) return $host; // 單標籤 (如 localhost)
for ($i = 1; $i < $count; $i++) {
$suffix = implode('.', array_slice($parts, -$i));
if (in_array($suffix, $suffixes, true)) {
// 找到公共後綴,再往前一級即為註冊域名
if ($i < $count) {
return implode('.', array_slice($parts, -$i - 1));
}
return $host; // 整個就是公共後綴 (罕見)
}
}
// 6. 兜底:返回倒數兩級 (如 example.未知後綴 -> example.未知後綴)
return implode('.', array_slice($parts, -2));
}
// 使用
$urls = [
'https://www.example.com/path',
'https://mail.google.com',
'https://sub.domain.co.uk',
'https://localhost:8080',
'192.168.1.1',
'https://github.io', // 公共後綴
];
foreach ($urls as $url) {
echo "$url => " . (getRegisteredDomain($url) ?? 'NULL') . "\n";
}
/* 輸出:
https://www.example.com/path => example.com
https://mail.google.com => google.com
https://sub.domain.co.uk => domain.co.uk
https://localhost:8080 => localhost
192.168.1.1 => 192.168.1.1
https://github.io => github.io
*/
?>
<?php
function extractDomainRegex(string $url): ?string {
// 匹配協議可選、子域名、主域名、多級後綴
$pattern = '/(?:https?:\/\/)?(?:[^@\/\s]+@)?([^:\/\s]+)/i';
if (!preg_match($pattern, $url, $m)) return null;
$host = strtolower($m[1]);
// 簡易後綴匹配 (不完整)
$tldPattern = '/\.(com|net|org|gov|edu|cn|com\.cn|net\.cn|org\.cn|gov\.cn|co\.uk|org\.uk|co\.jp|com\.hk)$/i';
if (preg_match($tldPattern, $host, $m)) {
$tld = $m[1];
// 獲取主域名部分
$mainPattern = '/([a-z0-9-]+\.' . preg_quote($tld, '/') . ')$/i';
if (preg_match($mainPattern, $host, $m2)) return $m2[1];
}
// 兜底:倒數兩級
$parts = explode('.', $host);
return count($parts) >= 2 ? implode('.', array_slice($parts, -2)) : $host;
}
?>
<?php
// composer require jeremykendall/tld-extract
require 'vendor/autoload.php';
use JeremyKendall\TldExtract\TldExtract;
$extract = new TldExtract(); // 自動下載/緩存 PSL
$urls = [
'https://www.example.com',
'https://blog.subdomain.example.co.uk',
'https://github.io',
'https://localhost',
];
foreach ($urls as $url) {
$result = $extract->extract($url);
echo "URL: $url\n";
echo " 子域名: " . $result->getSubdomain() . "\n";
echo " 主域名: " . $result->getRegisterableDomain() . "\n"; // 註冊域名 (eTLD+1)
echo " TLD: " . $result->getTld() . "\n";
echo " 是否公共後綴: " . ($result->isPublicSuffix() ? '是' : '否') . "\n";
}
?>
setcookie(..., '.example.com'))<?php
$testCases = [
'https://example.com' => 'example.com',
'https://www.example.com' => 'example.com',
'https://a.b.c.example.com' => 'example.com',
'https://example.co.uk' => 'example.co.uk',
'https://sub.example.co.uk' => 'example.co.uk',
'https://localhost' => 'localhost',
'https://127.0.0.1' => '127.0.0.1',
'https://[::1]' => '::1',
'https://github.io' => 'github.io', // 公共後綴
'https://my.github.io' => 'github.io', // 仍是公共後綴
'https://example.unknown' => 'example.unknown', // 未知後綴兜底
];
foreach ($testCases as $url => $expected) {
$actual = getRegisteredDomain($url);
$status = $actual === $expected ? '✅' : '❌';
echo "$status $url => $actual (期望: $expected)\n";
}
?>
提取主域名看似簡單,實則涉及公共後綴列表 (PSL) 這一動態標準。生產環境務必使用維護良好的庫 (如 TLDExtract) 並定期更新 PSL。簡易場景可用 parse_url() 配合常見後綴白名單,但需明確其局限性。