PHP如何精准获取URL的根域名代码?跨域适配怎么做?
- 虚拟主机
- 2025-12-17
- 5
在PHP中获取URL的根域名是一个常见的需求,特别是在处理跨域验证、URL解析或动态生成链接时,根域名通常指顶级域名(TLD)及其上一级域名组合,例如在 https://www.example.com/path 中,根域名为 example.com,以下是几种实现方法及其详细解析,涵盖不同场景和代码示例。
使用 parse_url 和 explode 组合
parse_url 是PHP内置函数,用于解析URL并返回其组成部分(如协议、主机、路径等),结合 explode 函数可以提取根域名,以下是基础实现步骤:

- 解析URL:使用 parse_url 获取主机名(host)。
- 分割主机名:将主机名按点号()分割成数组。
- 处理子域名:根据数组长度判断是否为子域名,并拼接根域名。
function getRootDomain($url) { $parsed = parse_url($url); if (!isset($parsed['host'])) { return false; // 无效URL } $host = $parsed['host']; $hostParts = explode('.', $host); // 处理.co.uk等特殊情况 if (count($hostParts) > 2 && in_array($hostParts[count($hostParts) 2], ['co', 'com', 'net', 'org'])) { return $hostParts[count($hostParts) 3] . '.' . $hostParts[count($hostParts) 2] . '.' . $hostParts[count($hostParts) 1]; } // 普通情况(如example.com) return $hostParts[count($hostParts) 2] . '.' . $hostParts[count($hostParts) 1]; } // 示例 $url = "https://www.example.co.uk/path"; echo getRootDomain($url); // 输出: example.co.uk
局限性:该方法无法处理动态TLD(如 .app、.shop),且需手动维护特殊后缀列表。
使用 idn_to_ascii 处理国际化域名(IDN)
若URL包含非ASCII字符(如中文域名),需先转换为ASCII格式,PHP的 idn_to_ascii 函数可完成此任务:

function getRootDomainWithIDN($url) { $parsed = parse_url($url); if (!isset($parsed['host'])) { return false; } $host = idn_to_ascii($parsed['host'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); $hostParts = explode('.', $host); // 同上逻辑处理特殊后缀 if (count($hostParts) > 2 && in_array($hostParts[count($hostParts) 2], ['co', 'com', 'net', 'org'])) { return $hostParts[count($hostParts) 3] . '.' . $hostParts[count($hostParts) 2] . '.' . $hostParts[count($hostParts) 1]; } return $hostParts[count($hostParts) 2] . '.' . $hostParts[count($hostParts) 1]; } // 示例 $url = "https://中国.例子.com"; echo getRootDomainWithIDN($url); // 输出: xnfsqu00a.xn0zwm56d
使用公共后缀列表(Public Suffix List)
更可靠的方法是通过公共后缀列表(如 https://publicsuffix.org/list/)动态判断根域名,该列表包含所有已知TLD及例外规则(如 github.io 的根域名为 io),以下是实现步骤:

- 下载并解析列表:将列表缓存为本地文件或数组。
- 匹配最长后缀:遍历主机名部分,查找列表中最长匹配的后缀。
function getRootDomainWithPSL($url, $pslFile = 'public_suffix_list.txt') { $parsed = parse_url($url); if (!isset($parsed['host'])) { return false; } $host = idn_to_ascii($parsed['host'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); $hostParts = array_reverse(explode('.', $host)); // 读取PSL列表(简化版,实际需完整解析) $psl = file($pslFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $psl = array_filter($psl, function($line) { return !str_starts_with($line, '//') && !str_starts_with($line, '!'); }); $suffix = ''; foreach ($hostParts as $i => $part) { $candidate = implode('.', array_slice($hostParts, 0, $i + 1)); if (in_array($candidate, $psl)) { $suffix = $candidate; break; } } if ($suffix) { $rootParts = array_slice($hostParts, 0, count(explode('.', $suffix)) + 1); return implode('.', array_reverse($rootParts)); } return $hostParts[1] . '.' . $hostParts[0]; // 默认处理 } // 示例(需先下载PSL文件) $url = "https://sub.github.io"; echo getRootDomainWithPSL($url); // 输出: github.io
优点:支持所有动态TLD和特殊规则,准确性高。
缺点:需维护PSL列表,性能较低。
使用第三方库(如 psl)
PHP社区已有成熟的库,如 psl,封装了PSL逻辑:
require 'vendor/autoload.php'; use PdpPublicSuffixListManager; function getRootDomainWithPSL($url) { $pslManager = new PublicSuffixListManager(); $psl = $pslManager>getList(); $domain = $psl>resolveDomain(parse_url($url)['host']); return $domain>registrableDomain>getAscii(); } // 示例 echo getRootDomainWithPSL("https://sub.example.co.uk"); // 输出: example.co.uk
不同场景下的性能对比
| 方法 | 准确性 | 性能 | 适用场景 |
|---|---|---|---|
| parse_url+explode | 低 | 高 | 简单URL,无特殊后缀 |
| idn_to_ascii | 中 | 中 | 国际化域名 |
| 公共后缀列表 | 高 | 低 | 需要严格匹配的复杂域名 |
| 第三方库(psl) | 最高 | 中 | 生产环境,推荐使用 |
相关问答FAQs
Q1: 如何处理IP地址形式的URL(如 http://192.168.1.1)?
A: 若URL是IP地址,直接返回即可,可在函数开头添加判断:
if (filter_var($host, FILTER_VALIDATE_IP)) { return $host; }
Q2: 为什么公共后缀列表能更准确地识别根域名?
A: 公共后缀列表由权威机构维护,包含所有注册的TLD及例外规则(如 blogspot.com 的根域名为 com,而 github.io 的根域名为 io),静态方法无法覆盖这些动态规则,导致误判。