Получить доменное имя (не поддомен) в php - PullRequest
29 голосов
/ 21 апреля 2010

У меня есть 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.) Из всего этого через одно регулярное выражение?

Ответы [ 13 ]

40 голосов
/ 21 апреля 2010

Ну, вы можете использовать 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

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

13 голосов
/ 12 апреля 2013

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

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

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

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

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

7 голосов
/ 09 марта 2012

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

  1. www.db.de (поддомен) против bbc.co.uk (домен)
  2. big.uk.com (SLD) против 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

5 голосов
/ 20 апреля 2013

Я думаю, что лучший способ решить эту проблему:

$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]";
}
5 голосов
/ 27 февраля 2013
$onlyHostName = implode('.', array_slice(explode('.', parse_url($link, PHP_URL_HOST)), -2));
4 голосов
/ 03 сентября 2016

Существует два способа извлечь поддомен из хоста:

  1. Первый более точный метод - использовать базу данных tlds (например, 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
    )
    
4 голосов
/ 12 апреля 2016

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

3 голосов
/ 15 декабря 2015

Вот функция, которую я написал для захвата домена без поддоменов, независимо от того, использует ли домен 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 голос
/ 03 мая 2019
echo getDomainOnly("http://example.com/foo/bar");

function getDomainOnly($host){
    $host = strtolower(trim($host));
    $host = ltrim(str_replace("http://","",str_replace("https://","",$host)),"www.");
    $count = substr_count($host, '.');
    if($count === 2){
        if(strlen(explode('.', $host)[1]) > 3) $host = explode('.', $host, 2)[1];
    } else if($count > 2){
        $host = getDomainOnly(explode('.', $host, 2)[1]);
    }
    $host = explode('/',$host);
    return $host[0];
}
1 голос
/ 04 января 2017

Вот тот, который работает для всех доменов, включая домены второго уровня, такие как "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

...