萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP 正則表達式提取域名:從 URL 解析到主域名獲取

核心需求

從任意 URL/文本中提取主域名 (如 example.com、sub.example.co.uk),支援多級後綴 (.com.cn, .org.hk 等)。

方案對比

方法優點缺點推薦度
parse_url() + 公共後綴列表標準庫、無依賴、準確需維護後綴列表 (PSL)⭐⭐⭐⭐⭐ 生產首選
正則表達式無依賴、靈活難覆蓋所有後綴、邊界情況多⭐⭐⭐ 簡單場景
第三方庫 (TLDExtract 等)自動更新 PSL、極準確需 Composer 依賴⭐⭐⭐⭐⭐ 企業級

方案一:parse_url + 公共後綴列表 (PSL) - 標準做法

<?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;
}
?>

方案三:生產級庫 TLDExtract (推薦)

<?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";
}
?>

應用場景

  • 多租戶 SaaS 識別租戶域名
  • Cookie 域名設置 (setcookie(..., '.example.com'))
  • CORS 白名單驗證
  • 鏈接安全檢測 (防釣魚)
  • 數據分析:按主域名聚合流量

邊界情況測試用例

<?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() 配合常見後綴白名單,但需明確其局限性。

copyright © 萬盛學電腦網 all rights reserved