web-dev-qa-db-ja.com

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(または.ld、TLDが何であれ。これをTLDで動作させるために必要です)を抽出するにはどうすればよいですか?

29
Cyclone

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];

あまりエレガントではありませんが、機能するはずです。


説明が必要な場合は、次のとおりです。

最初に、http://の機能を使用して、スキーム(parse_urlなど)間のすべてを取得します。 :)

次に、ホスト名を取得し、ピリオドの位置に基づいて配列に分離します。したがって、test.world.hello.mynameは次のようになります。

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

その後、配列の要素数を取得します(4)。

次に、2から最後の文字列(ホスト名、または例ではexample)を取得するために2を引きます

次に、そこから1を減算して、TLDとも呼ばれる最後の文字列を取得します(配列キーは0から始まるため)

次に、これら2つの部分をピリオドで結合し、ベースホスト名を取得します。

40
Tyler Carter

https://Gist.github.com/pocesar/5366899 での私のソリューション

テストはこちら http://codepad.viper-7.com/GAh1tP

TLD、および恐ろしいサブドメインパターン(最大3つのサブドメイン)で動作します。

多くのドメイン名に含まれるテストがあります。

StackOverflowのコードに奇妙なインデントがあるため、ここに関数を貼り付けません(githubのようなフェンスで囲まれたコードブロックがある可能性があります)

13
pocesar

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

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

この問題に対処する最善の方法は次のとおりです。

$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

ドメイン名を使用するすべての操作に TLDExtract ライブラリを使用することをお勧めします。

4
happy_marmoset

ホストからサブドメインを抽出するには、2つの方法があります。

  1. より正確な最初の方法は、TLDのデータベース( public_suffix_list.dat など)を使用し、それとドメインを一致させることです。これは、場合によっては少し重いです。 php-domain-parserTLDExtract のように、使用するためのいくつかのPHPクラスがあります。

  2. 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
Ehsan Chavoshi

ドメインがccTLDを使用しているか新しいスタイルの長い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;
}
3
Greg Z

「co.uk」などの第2レベルドメインを含むすべてのドメインで機能するものを次に示します。

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

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

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

これを試してください:

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

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

これは大部分のドメインで機能します。