Получить доменное имя (не субдомен) в 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. Как я могу извлечь example.com (или .net, независимо от того, что tld случается. Мне нужно, чтобы он работал с любым TLD.) Из всех этих элементов через одно регулярное выражение?

Ну, вы можете использовать parse_url для получения хоста:

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

Затем вы можете сделать некоторые причудливые вещи, чтобы получить только TLD и Host

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

Не очень элегантный, но должен работать.


Если вы хотите объяснения, вот оно:

Сначала мы хватаем все между схемой ( http:// и т. Д.), Используя возможности parse_url для … ну … анализировать URL. 🙂

Затем мы берем имя хоста и разделяем его на массив, основанный на том, где падают периоды, поэтому test.world.hello.myname станет:

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

После этого мы берем число элементов в массиве (4).

Затем мы вычитаем 2 из него, чтобы получить вторую последнюю строку (имя хоста или example в вашем примере)

Затем мы вычитаем 1 из него, чтобы получить последнюю строку (потому что ключи массива начинаются с 0), также называемые TLD

Затем мы объединяем эти две части с периодом, и у вас есть ваше базовое имя хоста.

Мое решение в https://gist.github.com/pocesar/5366899

и тесты здесь http://codepad.viper-7.com/GAh1tP

Он работает с любыми TLD и отвратительными шаблонами поддоменов (до 3 поддоменов).

Есть тест, включаемый во многие доменные имена.

Не будет вставлять функцию здесь из-за странного отступа для кода в StackOverflow (могли бы быть огражденные блоки кода, такие как github)

Невозможно получить доменное имя, не используя список TLD для сравнения, поскольку их существует много случаев с полностью одинаковой структурой и длиной:

  1. http://www.db.de (Субдомен) по сравнению с bbc.co.uk (Домен)
  2. big.uk.com (SLD) против http://www.uk.com (TLD)

Общий список суффикса Mozilla должен быть лучшим вариантом, поскольку он используется всеми основными браузерами :
https://publicsuffix.org/list/public_suffix_list.dat

Не стесняйтесь использовать мою функцию:

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

Что это делает:

  • он принимает все входные данные, такие как URL-адреса, имена хостов или домены с или без схемы
  • список загружается по очереди, чтобы избежать использования высокой памяти
  • он создает новый файл на TLD в папке с кешем, поэтому get_domain() нужно проверять через file_exists() если он существует, поэтому ему не нужно включать огромную базу данных для каждого запроса, например TLDExtract .
  • список будет автоматически обновляться каждые 30 дней

Контрольная работа:

 $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)); 

Я рекомендую использовать библиотеку TLDExtract для всех операций с именем домена.

Вот функция, которую я написал, чтобы захватить домен без субдоменов, независимо от того, использует ли домен ccTLD или длинный TLD нового стиля и т. Д. … Нет никакого поиска или огромного массива известных TLD, и нет регулярного выражения , Это может быть намного короче с использованием тернарного оператора и вложенности, но я расширил его для удобочитаемости.

 // 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. Когда я буду использовать, например, 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"; ?>