2010-04-21 9 views
24

J'ai une URL qui peut être l'un des formats suivants:Obtenir le nom de domaine (pas de sous-domaine) en php

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 

Essentiellement, je dois être en mesure de faire correspondre une URL normale. Comment puis-je extraire example.com (ou .net, quel que soit le tld se trouve être.Je en ai besoin pour travailler avec n'importe quel TLD.) De tout cela via une seule regex?

+0

duplication possible de http://stackoverflow.com/questions/399250/going-where-php-parse-url-doesnt-parsing-only-the-domain – Galen

+0

Pour les domaines de premier niveau à deux segments, vous pouvez passer par ceci: lien http://stackoverflow.com/questions/1201194/ php-getting-domain-name-from-subdomain –

Répondre

32

Eh bien, vous pouvez utiliser parse_url pour obtenir l'hôte:

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

Ensuite, vous pouvez faire des choses extravagantes pour obtenir que le TLD et l'hôte

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

Pas très élégant, mais devrait travail.


Si vous voulez une explication, ici il va:

D'abord, nous saisir tout entre le régime (http://, etc.), en utilisant les capacités de parse_url ... eh bien .... parse URL. :)

Ensuite, nous prenons le nom d'hôte, et les séparer dans un tableau en fonction de l'endroit où les périodes tombent, alors test.world.hello.myname deviendrait:

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

Après cela, nous prenons le nombre d'éléments dans le tableau (4).

Ensuite, nous soustrayons 2 de celui-ci pour obtenir la deuxième à la dernière chaîne (le nom d'hôte ou example, dans votre exemple)

Ensuite, nous soustrayons 1 de celui-ci pour obtenir la dernière chaîne (car les clés du tableau de départ à 0), également connu comme le TLD

Puis nous combinons ces deux parties avec un point, et vous avez votre nom d'hôte de base.

+29

Qu'en est-il des domaines de premier niveau à deux segments comme 'co.uk'? – eyelidlessness

+1

@eyelidlessness ne fonctionnera pas, malheureusement. –

+0

La seule option est d'extraire tous les TLD d'une source fiable et de les utiliser. –

6

Il est impossible d'obtenir le nom de domaine sans utiliser une liste de TLD à comparer avec leur cas avec existe de nombreux complètement la même structure et longueur:

  1. www.db.de (Subdomain) par rapport bbc .fr (Domaine)
  2. big.uk.com (SLD) contre www.uk.com (TLD)

liste suffixe publique de Mozilla devrait être la meilleure option car il est utilisé par tous major browsers:
https://publicsuffix.org/list/public_suffix_list.dat

Ne hésitez pas à utiliser ma fonction:

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

Qu'est-ce qu'il rend spécial:

  • il accepte chaque entrée comme les URL, h ostnames ou domaines avec- ou sans système
  • la liste est téléchargée ligne par ligne pour éviter l'utilisation de mémoire
  • il crée un nouveau fichier par TLD dans un dossier de cache si get_domain() doit seulement vérifier par file_exists() si elle existe il n'a donc pas besoin d'inclure une énorme base de données sur chaque requête, comme TLDExtract le fait.
  • la liste sera mise à jour automatiquement tous les 30 jours

Test:

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

version récente avec des explications (en allemand):
http://www.programmierer-forum.de/domainnamen-ermitteln-t244185.htm

+0

ça marche avec localhost et IPs? 'localhost',' 127.0.0.1', '192.168.1.100' ... – user1643156

+2

Votre question n'a pas de sens car la question est d'extraire le nom de domaine. Qu'est-ce que vous voulez extraire de 'localhost' – mgutt

+0

Est-ce que le site web' uk.com' pourrait mettre des cookies pour 'big.uk.com', parce que ce dernier peut être vu comme sous-domaine du premier? Ou il me manque quelque chose? – Somnium

4
$onlyHostName = implode('.', array_slice(explode('.', parse_url($link, PHP_URL_HOST)), -2)); 
+5

essayez d'offrir une explication à côté du code. L'affiche devrait apprendre ce qu'il fait. Sinon, il va créer d'autres questions avec le même problème. – CosminO

+0

@Ameoo Je ne vais pas poser la question à nouveau, s'il vous plaît noter que c'est plus de deux ans – Cyclone

12

Ma solution dans https://gist.github.com/pocesar/5366899

et les tests sont ici http://codepad.viper-7.com/GAh1tP

Il fonctionne avec n'importe quel TLD, et des modèles de sous-domaines hideux (jusqu'à 3 sous-domaines).

Un test est inclus avec de nombreux noms de domaine.

ne collera pas la fonction ici à cause de l'empreinte bizarre pour le code dans StackOverflow (pourrait avoir clôturé les blocs de code comme github)

+1

Merci! C'est un script très intelligent qui a fait le travail sans aucune recherche. – Nirmal

+0

pas de problème, bon de voir que c'est utile pour d'autres personnes – pocesar

+0

Merci d'avoir posté ça! – James

6

Je pense que la meilleure façon de gérer ce problème est:

$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]"; 
} 
+1

Cela ne fonctionnera avec rien après le TLD – Cyclone

+2

Cela ne fonctionnera pas avec une extension comme .co.uk. – Spode

+0

Maintenant, cela fonctionne pour les domaines de second niveau, tels que .co.uk @Spode –

3

Voici une fonction que j'ai écrite pour saisir le domaine sans sous-domaine (s), que le domaine utilise un ccTLD ou un nouveau style long TLD, etc ... Il n'y a pas de recherche ou un grand nombre de TLD connus, et il n'y a pas regex. Il peut être beaucoup plus court en utilisant l'opérateur ternaire et l'imbrication, mais je l'ai élargi pour plus de lisibilité.

// 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; 
} 
+0

ne fonctionne pas pour 'http: // sub.example.co' et retourner' sub.example.co' –

4

Je recommande d'utiliser la bibliothèque TLDExtract pour toutes les opérations avec un nom de domaine.

+1

Ceci est la meilleure solution . –

1

J'ai eu des problèmes avec la solution fournie par Pocesar. Lorsque j'utiliserais par exemple subdomain.domain.nl, il ne retournerait pas domain.nl. Au lieu de cela, il retournerait subdomain.domain.nl Un autre problème était que domain.com.br retournerait com.br

Je ne suis pas sûr mais je fixe ces problèmes avec le code suivant (je l'espère aider quelqu'un, si oui, je suis un homme heureux):

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); 
} 
3

Il y a deux façons d'extraire un sous-domaine de un hôte:

  1. La première méthode qui est plus précis consiste à utiliser une base de données de domaines de premier niveau (comme public_suffix_list.dat) et le domaine de correspondance avec elle. C'est un peu lourd dans certains cas. Il existe des classes PHP pour l'utiliser comme php-domain-parser et TLDExtract.

  2. La deuxième façon est pas aussi précis que le premier, mais il est très rapide et il peut donner la bonne réponse dans de nombreux cas, je l'ai écrit cette fonction pour elle:

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

    Exemple:

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

    Retours:

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

est ici un qui fonctionne pour tous les domaines, y compris ceux qui ont des domaines de second niveau comme « 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)); 
    } 
} 

On dirait qu'il ya une double question ici: delete-subdomain-from-url-string-if-subdomain-is-found

0

Essayez simplement ceci:

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