在php中获取域名(而不是子域名)

我有一个URL可以是以下任何格式:

http://example.com https://example.com http://example.com/foo http://example.com/foo/bar www.example.com example.com foo.example.com www.foo.example.com foo.bar.example.com http://foo.bar.example.com/foo/bar example.net/foo/bar 

基本上,我需要能够匹配任何正常的url。 我怎样才能通过一个正则expression式提取example.com (或.net,无论tld碰巧是什么,我需要这个来处理任何TLD)。

那么你可以使用parse_url来获取主机:

 $info = parse_url($url); $host = $info['host']; 

然后,你可以做一些花哨的东西,只有TLD和主机

 $host_names = explode(".", $host); $bottom_host_name = $host_names[count($host_names)-2] . "." . $host_names[count($host_names)-1]; 

不是很优雅,但应该工作。


如果你想要一个解释,这里是:

首先,我们通过使用parse_url的function来…抓取URL( http://等)之间的所有内容。 🙂

然后我们把主机名称,并根据期间下降的地方分成一个数组,所以test.world.hello.myname会变成:

 array("test", "world", "hello", "myname"); 

之后,我们取数组(4)中元素的个数。

然后,我们从中减去2得到倒数第二个string(在您的示例中是主机名或示例)

然后,我们从中减去1来得到最后一个string(因为数组键从0开始),也称为TLD

然后我们把这两个部分结合在一起,并且有你的基本主机名。

我的解决scheme在https://gist.github.com/pocesar/5366899

testing在这里http://codepad.viper-7.com/GAh1tP

它适用于任何TLD和可怕的子域模式(最多3个子域)。

许多域名都有一个testing。

由于StackOverflow中的代码有奇怪的缩进,因此不会在这里粘贴函数(可能有像github这样的围栅代码块)

如果不使用顶级域名(TLD)列表来比较域名是不可能的,因为它们存在许多具有完全相同结构和长度的情况:

  1. http://www.db.de(Subdomain)与bbc.co.uk(域名);
  2. big.uk.com(SLD)与www.uk.com(TLD)

Mozilla的公共后缀列表应该是所有主stream浏览器使用的最佳选项:
https://publicsuffix.org/list/public_suffix_list.dat

随意使用我的function:

 function tld_list($cache_dir=null) { // we use "/tmp" if $cache_dir is not set $cache_dir = isset($cache_dir) ? $cache_dir : sys_get_temp_dir(); $lock_dir = $cache_dir . '/public_suffix_list_lock/'; $list_dir = $cache_dir . '/public_suffix_list/'; // refresh list all 30 days if (file_exists($list_dir) && @filemtime($list_dir) + 2592000 > time()) { return $list_dir; } // use exclusive lock to avoid race conditions if (!file_exists($lock_dir) && @mkdir($lock_dir)) { // read from source $list = @fopen('https://publicsuffix.org/list/public_suffix_list.dat', 'r'); if ($list) { // the list is older than 30 days so delete everything first if (file_exists($list_dir)) { foreach (glob($list_dir . '*') as $filename) { unlink($filename); } rmdir($list_dir); } // now set list directory with new timestamp mkdir($list_dir); // read line-by-line to avoid high memory usage while ($line = fgets($list)) { // skip comments and empty lines if ($line[0] == '/' || !$line) { continue; } // remove wildcard if ($line[0] . $line[1] == '*.') { $line = substr($line, 2); } // remove exclamation mark if ($line[0] == '!') { $line = substr($line, 1); } // reverse TLD and remove linebreak $line = implode('.', array_reverse(explode('.', (trim($line))))); // we split the TLD list to reduce memory usage touch($list_dir . $line); } fclose($list); } @rmdir($lock_dir); } // repair locks (should never happen) if (file_exists($lock_dir) && mt_rand(0, 100) == 0 && @filemtime($lock_dir) + 86400 < time()) { @rmdir($lock_dir); } return $list_dir; } function get_domain($url=null) { // obtain location of public suffix list $tld_dir = tld_list(); // no url = our own host $url = isset($url) ? $url : $_SERVER['SERVER_NAME']; // add missing scheme ftp:// http:// ftps:// https:// $url = !isset($url[5]) || ($url[3] != ':' && $url[4] != ':' && $url[5] != ':') ? 'http://' . $url : $url; // remove "/path/file.html", "/:80", etc. $url = parse_url($url, PHP_URL_HOST); // replace absolute domain name by relative (http://www.dns-sd.org/TrailingDotsInDomainNames.html) $url = trim($url, '.'); // check if TLD exists $url = explode('.', $url); $parts = array_reverse($url); foreach ($parts as $key => $part) { $tld = implode('.', $parts); if (file_exists($tld_dir . $tld)) { return !$key ? '' : implode('.', array_slice($url, $key - 1)); } // remove last part array_pop($parts); } return ''; } 

它是什么使特别的:

  • 它接受每个input,如URL,主机名或域,或者没有scheme
  • 该列表是逐行下载以避免高内存使用量
  • 它会在caching文件夹中为每个TLD创build一个新文件,所以get_domain()只需要检查file_exists()是否存在,这样就不需要像TLDExtract那样在每个请求中包含一个巨大的数据库。
  • 该列表将会每30天自动更新一次

testing:

 $urls = array( 'http://www.example.com',// example.com 'http://subdomain.example.com',// example.com 'http://www.example.uk.com',// example.uk.com 'http://www.example.co.uk',// example.co.uk 'http://www.example.com.ac',// example.com.ac 'http://example.com.ac',// example.com.ac 'http://www.example.accident-prevention.aero',// example.accident-prevention.aero 'http://www.example.sub.ar',// sub.ar 'http://www.congresodelalengua3.ar',// congresodelalengua3.ar 'http://congresodelalengua3.ar',// congresodelalengua3.ar 'http://www.example.pvt.k12.ma.us',// example.pvt.k12.ma.us 'http://www.example.lib.wy.us',// example.lib.wy.us 'com',// empty '.com',// empty 'http://big.uk.com',// big.uk.com 'uk.com',// empty 'www.uk.com',// www.uk.com '.uk.com',// empty 'stackoverflow.com',// stackoverflow.com '.foobarfoo',// empty '',// empty false,// empty ' ',// empty 1,// empty 'a',// empty ); 

最近的版本与解释(德语):
http://www.programmierer-forum.de/domainnamen-ermitteln-t244185.htm

我认为处理这个问题的最好方法是:

 $second_level_domains_regex = '/\.asn\.au$|\.com\.au$|\.net\.au$|\.id\.au$|\.org\.au$|\.edu\.au$|\.gov\.au$|\.csiro\.au$|\.act\.au$|\.nsw\.au$|\.nt\.au$|\.qld\.au$|\.sa\.au$|\.tas\.au$|\.vic\.au$|\.wa\.au$|\.co\.at$|\.or\.at$|\.priv\.at$|\.ac\.at$|\.avocat\.fr$|\.aeroport\.fr$|\.veterinaire\.fr$|\.co\.hu$|\.film\.hu$|\.lakas\.hu$|\.ingatlan\.hu$|\.sport\.hu$|\.hotel\.hu$|\.ac\.nz$|\.co\.nz$|\.geek\.nz$|\.gen\.nz$|\.kiwi\.nz$|\.maori\.nz$|\.net\.nz$|\.org\.nz$|\.school\.nz$|\.cri\.nz$|\.govt\.nz$|\.health\.nz$|\.iwi\.nz$|\.mil\.nz$|\.parliament\.nz$|\.ac\.za$|\.gov\.za$|\.law\.za$|\.mil\.za$|\.nom\.za$|\.school\.za$|\.net\.za$|\.co\.uk$|\.org\.uk$|\.me\.uk$|\.ltd\.uk$|\.plc\.uk$|\.net\.uk$|\.sch\.uk$|\.ac\.uk$|\.gov\.uk$|\.mod\.uk$|\.mil\.uk$|\.nhs\.uk$|\.police\.uk$/'; $domain = $_SERVER['HTTP_HOST']; $domain = explode('.', $domain); $domain = array_reverse($domain); if (preg_match($second_level_domains_regex, $_SERVER['HTTP_HOST']) { $domain = "$domain[2].$domain[1].$domain[0]"; } else { $domain = "$domain[1].$domain[0]"; } 
 $onlyHostName = implode('.', array_slice(explode('.', parse_url($link, PHP_URL_HOST)), -2)); 

我build议使用TLDExtract库进行所有与域名相关的操作。

这是我写的一个函数,用于获取没有子域名的域名,而不pipe域名是使用ccTLD还是使用新型的TLD等等。没有查询或者大量已知的顶级域名(TLD),并且没有正则expression式。 使用三元运算符和嵌套可以缩短很多,但为了便于阅读,我对其进行了扩展。

 // Per Wikipedia: "All ASCII ccTLD identifiers are two letters long, // and all two-letter top-level domains are ccTLDs." function topDomainFromURL($url) { $url_parts = parse_url($url); $domain_parts = explode('.', $url_parts['host']); if (strlen(end($domain_parts)) == 2 ) { // ccTLD here, get last three parts $top_domain_parts = array_slice($domain_parts, -3); } else { $top_domain_parts = array_slice($domain_parts, -2); } $top_domain = implode('.', $top_domain_parts); return $top_domain; } 

有两种方法可以从主机中提取子域:

  1. 第一种更准确的方法是使用一个tld的数据库(如public_suffix_list.dat ),并将其与域进行匹配。 在某些情况下这有点沉重。 有一些PHP类可以像php-domain-parser和TLDExtract一样使用它。

  2. 第二种方法不如第一种方法准确,但是速度很快,在许多情况下它可以给出正确的答案,我为它写了这个函数:

     function get_domaininfo($url) { // regex can be replaced with parse_url preg_match("/^(https|http|ftp):\/\/(.*?)\//", "$url/" , $matches); $parts = explode(".", $matches[2]); $tld = array_pop($parts); $host = array_pop($parts); if ( strlen($tld) == 2 && strlen($host) <= 3 ) { $tld = "$host.$tld"; $host = array_pop($parts); } return array( 'protocol' => $matches[1], 'subdomain' => implode(".", $parts), 'domain' => "$host.$tld", 'host'=>$host,'tld'=>$tld ); } 

    例:

     print_r(get_domaininfo('http://mysubdomain.domain.co.uk/index.php')); 

    返回:

     Array ( [protocol] => https [subdomain] => mysubdomain [domain] => domain.co.uk [host] => domain [tld] => co.uk ) 

我对pocesar提供的解决scheme有问题。 当我将使用例如subdomain.domain.nl它不会返回domain.nl。 相反,它会返回subdomain.domain.nl另一个问题是,domain.com.br将返回com.br

我不知道,但我解决了这些问题与下面的代码(我希望这将有助于某人,如果是的话,我是一个快乐的人):

 function get_domain($domain, $debug = false){ $original = $domain = strtolower($domain); if (filter_var($domain, FILTER_VALIDATE_IP)) { return $domain; } $debug ? print('<strong style="color:green">&raquo;</strong> Parsing: '.$original) : false; $arr = array_slice(array_filter(explode('.', $domain, 4), function($value){ return $value !== 'www'; }), 0); //rebuild array indexes if (count($arr) > 2){ $count = count($arr); $_sub = explode('.', $count === 4 ? $arr[3] : $arr[2]); $debug ? print(" (parts count: {$count})") : false; if (count($_sub) === 2){ // two level TLD $removed = array_shift($arr); if ($count === 4){ // got a subdomain acting as a domain $removed = array_shift($arr); } $debug ? print("<br>\n" . '[*] Two level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false; }elseif (count($_sub) === 1){ // one level TLD $removed = array_shift($arr); //remove the subdomain if (strlen($arr[0]) === 2 && $count === 3){ // TLD domain must be 2 letters array_unshift($arr, $removed); }elseif(strlen($arr[0]) === 3 && $count === 3){ array_unshift($arr, $removed); }else{ // non country TLD according to IANA $tlds = array( 'aero', 'arpa', 'asia', 'biz', 'cat', 'com', 'coop', 'edu', 'gov', 'info', 'jobs', 'mil', 'mobi', 'museum', 'name', 'net', 'org', 'post', 'pro', 'tel', 'travel', 'xxx', ); if (count($arr) > 2 && in_array($_sub[0], $tlds) !== false){ //special TLD don't have a country array_shift($arr); } } $debug ? print("<br>\n" .'[*] One level TLD: <strong>'.join('.', $_sub).'</strong> ') : false; }else{ // more than 3 levels, something is wrong for ($i = count($_sub); $i > 1; $i--){ $removed = array_shift($arr); } $debug ? print("<br>\n" . '[*] Three level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false; } }elseif (count($arr) === 2){ $arr0 = array_shift($arr); if (strpos(join('.', $arr), '.') === false && in_array($arr[0], array('localhost','test','invalid')) === false){ // not a reserved domain $debug ? print("<br>\n" .'Seems invalid domain: <strong>'.join('.', $arr).'</strong> re-adding: <strong>'.$arr0.'</strong> ') : false; // seems invalid domain, restore it array_unshift($arr, $arr0); } } $debug ? print("<br>\n".'<strong style="color:gray">&laquo;</strong> Done parsing: <span style="color:red">' . $original . '</span> as <span style="color:blue">'. join('.', $arr) ."</span><br>\n") : false; return join('.', $arr); } 

这是一个适用于所有域名的域名,包括像“co.uk”这样的二级域名的域名。

 function strip_subdomains($url){ # credits to gavingmiller for maintaining this list $second_level_domains = file_get_contents("https://raw.githubusercontent.com/gavingmiller/second-level-domains/master/SLDs.csv"); # presume sld first ... $possible_sld = implode('.', array_slice(explode('.', $url), -2)); # and then verify it if (strpos($second_level_domains, $possible_sld)){ return implode('.', array_slice(explode('.', $url), -3)); } else { return implode('.', array_slice(explode('.', $url), -2)); } } 

看起来这里有一个重复的问题: delete-subdomain-from-url-string-if-subdomain-is-found

只需尝试一下:

 <?php $host = $_SERVER['HTTP_HOST']; preg_match("/[^\.\/]+\.[^\.\/]+$/", $host, $matches); echo "domain name is: {$matches[0]}\n"; ?>