wake-up-neo.net

Holen Sie sich den Domainnamen (nicht Subdomain) in PHP

Ich habe eine URL, die eines der folgenden Formate haben kann:

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

Im Wesentlichen muss ich in der Lage sein, jede normale URL zu finden. Wie kann ich example.com (oder .net, was auch immer das tld ist. Ich brauche das, um mit jeder TLD zu arbeiten.) Aus allen diesen Dateien über eine einzige Regex extrahieren? 

27
Cyclone

Nun, Sie können parse_url verwenden, um den Host zu erhalten:

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

Dann können Sie einige tolle Sachen machen, um nur die TLD und den Host zu erhalten

$Host_names = explode(".", $Host);
$bottom_Host_name = $Host_names[count($Host_names)-2] . "." . $Host_names[count($Host_names)-1];

Nicht sehr elegant, sollte aber funktionieren.


Wenn Sie eine Erklärung wünschen, hier geht es:

Zuerst greifen wir alles zwischen dem Schema (http:// usw.) auf, indem wir die Funktionen von parse_url verwenden, um ... naja ... URLs zu analysieren. :)

Dann nehmen wir den Hostnamen und trennen ihn in ein Array, basierend auf dem Ort, an dem die Perioden liegen, und test.world.hello.myname würde zu:

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

Danach nehmen wir die Anzahl der Elemente im Array (4).

Dann subtrahieren wir 2 von ihm, um die vorletzte Zeichenfolge zu erhalten (den Hostnamen oder example in Ihrem Beispiel).

Dann subtrahieren wir 1 von ihm, um die letzte Zeichenfolge zu erhalten (weil Array-Schlüssel bei 0 beginnen), die auch als TLD bezeichnet wird

Dann kombinieren wir diese beiden Teile mit einem Punkt und Sie haben Ihren Basis-Hostnamen.

38
Tyler Carter

Meine Lösung in https://Gist.github.com/pocesar/5366899

und die Tests sind hier http://codepad.viper-7.com/GAh1tP

Es funktioniert mit jeder TLD und abscheulichen Subdomain-Mustern (bis zu 3 Subdomains).

Es gibt einen Test mit vielen Domainnamen.

Fügt die Funktion hier nicht ein, da der Code in StackOverflow seltsamerweise eingerückt ist (Code-Blöcke wie "github" könnten abgeschnitten sein).

12
pocesar

Es ist nicht möglich, den Domänennamen zu erhalten, ohne eine TLD-Liste zu verwenden, um zu vergleichen, da es viele Fälle gibt, deren Struktur und Länge völlig gleich sind:

  1. www.db.de (Subdomain) versus bbc.co.uk (Domain)
  2. big.uk.com (SLD) versus www.uk.com (TLD)

Die öffentliche Suffixliste von Mozilla sollte die beste Option sein, da sie von allen major-Browsern verwendet wird:
https://publicsuffix.org/list/public_suffix_list.dat

Fühlen Sie sich frei, meine Funktion zu nutzen:

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

Was es besonders macht:

  • es akzeptiert jede Eingabe wie URLs, Hostnamen oder Domänen mit oder ohne Schema
  • die Liste wird Zeile für Zeile heruntergeladen, um eine hohe Speicherauslastung zu vermeiden
  • es erstellt eine neue Datei pro TLD in einem Cache-Ordner, so dass get_domain() nur durch file_exists() prüfen muss, ob sie vorhanden ist, sodass keine große Datenbank in jede Anforderung eingefügt werden muss, wie TLDExtract .
  • die Liste wird automatisch alle 30 Tage aktualisiert

Prüfung:

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

Aktuelle Version mit Erklärungen (deutsch):
http://www.programmierer-forum.de/domainnamen-ermitteln-t244185.htm

6
mgutt
$onlyHostName = implode('.', array_slice(explode('.', parse_url($link, PHP_URL_Host)), -2));
5
user2116044

Ich denke, der beste Weg, um dieses Problem zu lösen, ist:

$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
mmeyer2k

Ich empfehle die Verwendung von TLDExtract library für alle Operationen mit Domainnamen.

4
happy_marmoset

Es gibt zwei Möglichkeiten, eine Subdomain von einem Host zu extrahieren:

  1. Die erste Methode, die genauer ist, besteht darin, eine Datenbank mit tlds (wie public_suffix_list.dat ) zu verwenden und die Domäne damit abzugleichen. Dies ist in manchen Fällen etwas schwer. Es gibt einige PHP - Klassen für die Verwendung wie php-domain-parser und TLDExtract .

  2. Der zweite Weg ist nicht so genau wie der erste, aber er ist sehr schnell und kann in vielen Fällen die richtige Antwort geben. Ich habe diese Funktion dafür geschrieben:

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

    Beispiel:

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

    Kehrt zurück:

    Array
    (
        [protocol] => https
        [subdomain] => mysubdomain
        [domain] => domain.co.uk
        [Host] => domain
        [tld] => co.uk
    )
    
4
Ehsan Chavoshi

Hier ist eine Funktion, die ich geschrieben habe, um die Domain ohne Subdomain (s) zu packen, unabhängig davon, ob die Domain eine ccTLD oder eine neue TLD mit langem Stil usw. verwendet ... Es gibt keine Suche oder ein großes Array bekannter TLDs und es gibt keinen Regex . Mit dem ternären Operator und der Verschachtelung kann es viel kürzer sein, aber ich habe es aus Gründen der Lesbarkeit erweitert.

// 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;
}
3
Greg Z

Hier ist eine Funktion, die für alle Domänen geeignet ist, einschließlich Domänen mit Second-Level-Domänen wie "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));
    }
}

Sieht aus, als gäbe es hier eine doppelte Frage: delete-subdomain-from-url-string-if-subdomain-is-found

1
BvS
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
Kuldip

Ich hatte Probleme mit der von pocesar bereitgestellten Lösung. Wenn ich beispielsweise subdomain.domain.nl verwende, würde es keine domain.nl zurückgeben. Stattdessen würde subdomain.domain.nl .__ zurückgegeben werden. Ein weiteres Problem war, dass domain.com.br com.br zurückgibt

Ich bin nicht sicher, aber ich habe diese Probleme mit dem folgenden Code behoben (ich hoffe, es hilft jemandem, wenn ich ein glücklicher Mann bin):

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);
}
1
Rens Tillmann

Probieren Sie einfach folgendes aus:

   preg_match('/(www.)?([^.]+\.[^.]+)$/', $yourHost, $matches);

   echo "domain name is: {$matches[0]}\n"; 

dies funktioniert für die Mehrheit der Domänen.