【问题标题】:How to get the base domain name from an URL using PHP?如何使用 PHP 从 URL 获取基本域名?
【发布时间】:2010-07-09 09:34:31
【问题描述】:

我需要从 URL 中获取域名。以下示例都应返回google.com

google.com
images.google.com
new.images.google.com
www.google.com

同样,以下 URL 都应返回 google.co.uk

google.co.uk
images.google.co.uk
new.images.google.co.uk
http://www.google.co.uk

我对使用正则表达式犹豫不决,因为像 domain.com/google.com 这样的东西可能会返回不正确的结果。

如何使用 PHP 获得顶级域?这需要适用于所有平台和主机。

【问题讨论】:

  • 这很棘手。对于google.com,您对TLD 和二级域名感兴趣。对于google.co.uk,您需要TLD 以及二级和三级域名。没有定义的“基本名称”,您所说的“基本名称”对于不同的注册商/TLD 是不同的。
  • 我很确定你在这里必须有点啰嗦,你要求的是吃你的蛋糕并拥有它。如果没有 TLD 列表,就无法区分 co.uk 和 google.com,它们都是主机名。
  • 我猜你们是对的,如果没有大量代码,看起来任何事情都无法正常工作

标签: php


【解决方案1】:

你可以这样做:

$urlData = parse_url($url);

$host = $urlData['host'];

** 更新**

我能想到的最好方法是对您要处理的所有 TLD 进行映射,因为某些 TLD 可能很棘手 (co.uk)。

// you can add more to it if you want
$urlMap = array('com', 'co.uk');

$host = "";
$url = "http://www.google.co.uk";

$urlData = parse_url($url);
$hostData = explode('.', $urlData['host']);
$hostData = array_reverse($hostData);

if(array_search($hostData[1] . '.' . $hostData[0], $urlMap) !== FALSE) {
  $host = $hostData[2] . '.' . $hostData[1] . '.' . $hostData[0];
} elseif(array_search($hostData[0], $urlMap) !== FALSE) {
  $host = $hostData[1] . '.' . $hostData[0];
}

echo $host;

【讨论】:

    【解决方案2】:

    顶级域和二级域可能有 2 个字符长,但注册的子域必须至少有 3 个字符长。

    编辑:由于 pjv 的评论,我了解到澳大利亚域名是一个例外,因为它们允许 5 个 TLD 作为 SLD(com、net、org、asn、id)例如:somedomain.com.au。我猜 com.au 是“共享”的国家控制域名。因此,从技术上讲,“com.au”仍将是“基本域”,但这没有用。

    编辑:有 47,952 个可能的三字母域名(模式:[a-zA-Z0-9][a-zA-Z0-9-][a-zA-Z0-9] 或 36 * 37 * 36) 结合 8 个最常见的 TLD(com、org 等),我们有 383,616 种可能性——甚至没有添加整个 TLD 范围。 1 字母和 2 字母域名仍然存在,但以后不再有效。

    在 google.com 中——“google”是“com”的子域

    在 google.co.uk 中——“google”是“co”的子域,而后者又是“uk”的子域,或者实际上是二级域,因为“co”也是有效的顶级域级域

    在 www.google.com 中——“www”是“google”的子域,是“com”的子域

    “co.uk”不是有效的主机,因为没有有效的域名

    假设这个函数在几乎所有情况下都会返回正确的“basedomain”,而不需要“url map”。

    如果您碰巧是少数情况之一,也许您可​​以修改它以满足特定需求...

    编辑:您必须将域字符串作为带有协议(http://、ftp:// 等)的 URL 传递,否则 parse_url() 不会认为它是有效的 URL(除非您想将代码修改为行为不同)

    function basedomain( $str = '' )
    {
        // $str must be passed WITH protocol. ex: http://domain.com
        $url = @parse_url( $str );
        if ( empty( $url['host'] ) ) return;
        $parts = explode( '.', $url['host'] );
        $slice = ( strlen( reset( array_slice( $parts, -2, 1 ) ) ) == 2 ) && ( count( $parts ) > 2 ) ? 3 : 2;
        return implode( '.', array_slice( $parts, ( 0 - $slice ), $slice ) );
    }
    

    如果您需要准确使用fopencurl 打开此网址: http://data.iana.org/TLD/tlds-alpha-by-domain.txt

    然后将这些行读入一个数组并使用它来比较域部分

    编辑:允许澳大利亚域名:

    function au_basedomain( $str = '' )
    {
        // $str must be passed WITH protocol. ex: http://domain.com
        $url = @parse_url( $str );
        if ( empty( $url['host'] ) ) return;
        $parts = explode( '.', $url['host'] );
        $slice = ( strlen( reset( array_slice( $parts, -2, 1 ) ) ) == 2 ) && ( count( $parts ) > 2 ) ? 3 : 2;
        if ( preg_match( '/\.(com|net|asn|org|id)\.au$/i', $url['host'] ) ) $slice = 3;
        return implode( '.', array_slice( $parts, ( 0 - $slice ), $slice ) );
    }
    

    重要的附加说明:我不使用此功能来验证域。它是通用代码,我仅用于从全局$_SERVER['SERVER_NAME'] 中提取运行它的服务器的基域,以便在各种内部脚本中使用。考虑到我只在美国境内的网站上工作过,我从未遇到过 pjv 询问的澳大利亚变体。它对内部使用很方便,但距离完整的域验证过程还有很长的路要走。如果您尝试以这种方式使用它,我建议您不要这样做,因为匹配无效域的可能性太多。

    【讨论】:

    • 如果您将 strlen() == 2 更改为 <=3 您将捕获 99% 的域,将子域保存在 localhost 等等。这是我整理的修订版:gist.github.com/anonymous/fe77c97e632675411c3c
    • 不,修订版无法正常工作。它必须是 == 2 因为当最后一部分的下一个是 3 时
    • @Mahn 此外,您的修订版中有许多额外的位——不需要的变量分配和不需要的条件嵌套。更多代码和不想要的结果——您是否彻底测试了您的修订版?
    • @Mahn 还有,您的修订在$middlePart = array_slice($parts, -2, 1)[0]; 附近触发了一个错误[0]
    • 我的版本在 5.5 的生产环境中运行良好,也许您使用的是较旧的 PHP 版本?额外的嵌套和变量赋值是为了保持理智和可读性,我个人不喜欢看起来像黑客马拉松一样被黑客攻击的代码,但这只是个人喜好。我还发现<=3 对我的需求来说足够准确,因为我不使用三个字母的域,它可能对大多数人来说应该足够准确。
    【解决方案3】:

    尝试使用:http://php.net/manual/en/function.parse-url.php。像这样的东西应该可以工作:

    $urlParts = parse_url($yourUrl);
    $hostParts = explode('.', $urlParts['host']);
    $hostParts = array_reverse($hostParts);
    $host = $hostParts[1] . '.' . $hostParts[0];
    

    【讨论】:

    • 如果你有这样的东西会破坏:google.co.uk - 在这种情况下,它会返回“co.uk”。
    • 确实,唯一的方法是使用 TLD 列表。
    【解决方案4】:

    与 xil3 混合回答这是我要检查 localhost 和 ip,所以你也可以在开发环境中工作。
    您仍然必须定义要使用的 TLD。除此之外一切正常。

    <?php
    function getTopLevelDomain($url){
        $urlData = parse_url($url);
        $urlHost = isset($urlData['host']) ? $urlData['host'] : '';
        $isIP = (bool)ip2long($urlHost);
        if($isIP){ /** To check if it's ip then return same ip */
            return $urlHost;
        }
        /** Add/Edit you TLDs here */
        $urlMap = array('com', 'com.pk', 'co.uk');
    
        $host = "";
        $hostData = explode('.', $urlHost);
        if(isset($hostData[1])){ /** To check "localhost" because it'll be without any TLDs */
            $hostData = array_reverse($hostData);
    
            if(array_search($hostData[1] . '.' . $hostData[0], $urlMap) !== FALSE) {
                $host = $hostData[2] . '.' . $hostData[1] . '.' . $hostData[0];
            } elseif(array_search($hostData[0], $urlMap) !== FALSE) {
                $host = $hostData[1] . '.' . $hostData[0];
            }
            return $host;
        }
        return ((isset($hostData[0]) && $hostData[0] != '') ? $hostData[0] : 'error no domain'); /* You can change this error in future */
    }
    ?>
    

    你可以这样使用它

    $string = 'http://googl.com.pk';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://googl.com.pk:23';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://googl.com';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://googl.com:23';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://adad.asdasd.googl.com.pk';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://adad.asdasd.googl.com.pk:23';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://adad.asdasd.googl.com';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://adad.asdasd.googl.com:23';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://192.168.0.101:23';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://192.168.0.101';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'http://localhost';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = 'https;//';
    echo getTopLevelDomain( $string ) . '<br>';
    
    $string = '';
    echo getTopLevelDomain( $string ) . '<br>';
    

    你会得到这样的字符串

    googl.com.pk
    googl.com.pk
    googl.com
    googl.com
    googl.com.pk
    googl.com.pk
    googl.com
    googl.com
    192.168.0.101
    192.168.0.101
    localhost
    error no domain
    error no domain
    

    【讨论】:

      【解决方案5】:

      我不是 PHP 开发人员,我知道这不是完整的解决方案,但我认为一般问题实际上是识别所有可能的公共域名。

      幸运的是,https://publicsuffix.org/list/ 维护了一个公共域列表。该列表分为两部分。第一部分是公共域名,其中包括这些 cmets 中列出的许多域名,例如 .com.com.au。公共域名以===BEGIN ICANN DOMAINS======END ICANN DOMAINS===分隔。

      如果您只加载 ICANN 域列表,那么您可以识别顶级域名。但是需要 PHP 开发人员来解释如何有效地做到这一点:)

      如果您加载整个列表,那么您还可以获得有关私有子域的信息,例如github.io 下的那些。

      【讨论】:

        【解决方案6】:

        您可能想要使用公共后缀列表。

        https://publicsuffix.org/

        在 php ypu 中可以使用 regdom 库做到这一点:

        https://github.com/usrflo/registered-domain-libs/

        【讨论】:

          【解决方案7】:

          此处的答案均不支持包含 3 部分的公共后缀,这些后缀也存在(例如,.k12.ak.us

          这是一个更完整的解决方案,它允许任何长度的公共后缀:

          public function getBaseDomain($domain)
              {
                  if (empty($domain) || substr_count($domain, ".") < 2) {
                      return $domain;
                  }
                  $publicSuffixes = [".com",".co.uk",".k12.ak.us", ......];
                  $domainParts = explode(".", $domain);
                  $checkDomain = array_pop($domainParts);
          
                  do {
                      $checkDomain = array_pop($domainParts) . "." . $checkDomain;
                      if (empty($domainParts)) {
                          break;
                      }
                  } while (array_search("." . $checkDomain, $publicSuffixes) !== false);
          
          
                  return $checkDomain;
              }
          

          注意:这里的代码已经假定它是一个域,而不是一个 IP,并假定它是一个有效域,没有 https://

          有关可用公共后缀的最完整列表,请参阅https://publicsuffix.org/list/public_suffix_list.dat

          【讨论】:

            【解决方案8】:

            使用此功能:

            function getHost($url){
                if (strpos($url,"http://")){
                    $httpurl=$url;
                } else {
                    $httpurl="http://".$url;
                }
                $parse = parse_url($httpurl);
                $domain=$parse['host'];
            
                $portion=explode(".",$domain);
                $count=sizeof($portion)-1;
                if ($count>1){
                    $result=$portion[$count-1].".".$portion[$count];
                } else {
                    $result=$domain;
                }
                return $result;
            }
            

            回答示例 URL 的所有变体。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-02-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-18
              • 2011-08-05
              • 1970-01-01
              相关资源
              最近更新 更多