【问题标题】:How to validate domain name in PHP?如何在 PHP 中验证域名?
【发布时间】:2010-12-17 19:54:02
【问题描述】:

不使用正则表达式可以吗?

例如,我想检查一个字符串是否是一个有效的域:

domain-name
abcd
example

是有效的域。这些当然是无效的:

domaia@name
ab$%cd

等等。所以基本上它应该以字母数字字符开头,然后可能有更多的 alnum 字符加上连字符。它也必须以 alnum 字符结尾。

如果不可能,您能否建议我使用正则表达式模式来执行此操作?

编辑:

为什么这不起作用?我是否错误地使用了 preg_match?

$domain = '@djkal';
$regexp = '/^[a-zA-Z0-9][a-zA-Z0-9\-\_]+[a-zA-Z0-9]$/';
if (false === preg_match($regexp, $domain)) {
    throw new Exception('Domain invalid');
}

【问题讨论】:

  • 为什么要避免使用正则表达式?有效率
  • @您的编辑:您错误地使用 '===',preg_match 返回一个 int,而不是 false
  • domaia@name 一个有效的域名。但是,它不是有效的 host 名称。看我的回答。

标签: php regex domain-name


【解决方案1】:
<?php
function is_valid_domain_name($domain_name)
{
    return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name) //valid chars check
            && preg_match("/^.{1,253}$/", $domain_name) //overall length check
            && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)   ); //length of each label
}
?>

测试用例:

is_valid_domain_name? [a]                       Y
is_valid_domain_name? [0]                       Y
is_valid_domain_name? [a.b]                     Y
is_valid_domain_name? [localhost]               Y
is_valid_domain_name? [google.com]              Y
is_valid_domain_name? [news.google.co.uk]       Y
is_valid_domain_name? [xn--fsqu00a.xn--0zwm56d] Y
is_valid_domain_name? [goo gle.com]             N
is_valid_domain_name? [google..com]             N
is_valid_domain_name? [google.com ]             N
is_valid_domain_name? [google-.com]             N
is_valid_domain_name? [.google.com]             N
is_valid_domain_name? [<script]                 N
is_valid_domain_name? [alert(]                  N
is_valid_domain_name? [.]                       N
is_valid_domain_name? [..]                      N
is_valid_domain_name? [ ]                       N
is_valid_domain_name? [-]                       N
is_valid_domain_name? []                        N

【讨论】:

  • 别忘了检查 if (count($pieces) > 1)
  • 较短的单个正则表达式:^[a-z\d](-*[a-z\d])*$.
  • 肯德尔,感谢您的正则表达式。此外,由于:blog.sacaluta.com/2011/12/…,现在限制为 253 个
  • 小心这个正则表达式。它将允许xn---gnter--o2a.de 被翻译回-günter-.de,这显然不应该被允许。
  • 根据经验,您应该使用单引号在 php 中编写正则表达式,这样它就不会处理内部的任何特殊字符
【解决方案2】:

这样,您不仅可以检查域是否具有有效的格式,还可以检查它是否处于活动状态/是否有分配给它的 IP 地址。

$domain = "stackoverflow.com";

if(filter_var(gethostbyname($domain), FILTER_VALIDATE_IP))
{
    return TRUE;
}

请注意,此方法要求 DNS 条目处于活动状态,因此如果您需要验证域字符串而不在 DNS 中,请使用上面 velcrow 给出的正则表达式方法。

此外,此函数不用于验证 URL 字符串,请使用 FILTER_VALIDATE_URL。我们不对域使用 FILTER_VALIDATE_URL,因为域字符串不是有效的 URL。

【讨论】:

  • 只有我会使用过滤器:FILTER_VALIDATE_URL 而不是FILTER_VALIDATE_IP
  • FILTER_VALIDATE_URL 只会找到有效的 ASCII URL;国际化域名(包含非 ASCII 字符)将失败。 (php.net/manual/en/filter.filters.validate.php)
  • gethostbyname 会进行阻塞式 dns 查找,所以不要运行它来遍历大型数据集,否则运行时间会很糟糕。
  • 带有查询字符串的 url 失败,即https://www.google.co.uk/search?q=google&amp;gws_rd=ssl
  • 对于 DNS 中实际不存在的有效域或主机名,这将失败。 -1.
【解决方案3】:

PHP 7

// Validate a domain name
var_dump(filter_var('mandrill._domainkey.mailchimp.com', FILTER_VALIDATE_DOMAIN));
# string(33) "mandrill._domainkey.mailchimp.com"

// Validate an hostname (here, the underscore is invalid)
var_dump(filter_var('mandrill._domainkey.mailchimp.com', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME));
# bool(false)

此处未记录:http://www.php.net/filter.filters.validate,对此的错误请求位于此处:https://bugs.php.net/bug.php?id=72013

【讨论】:

  • 嗯,这可行,但会产生一些误报:``` >>> filter_var('ripe', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) => "ripe" ```
  • @М.Б。这是一个符合规范的有效域名RFC 1035"它们必须以字母开头,以字母或数字结尾,并且内部字符只能是字母、数字和连字符。还有一些限制长度。标签不得超过 63 个字符。" 。您可以添加更多限制,必须包含一个点 . 来制作您想要的内容
  • @Accountant 好的,谢谢!我认为. 已经包含在验证器本身中。
  • @Accountantم,我认为可能有一个扩展该定义的 RFC,因为我曾经拥有域“2tp.com”,并且我见过许多其他以 a 开头的域名数字。
【解决方案4】:

使用 checkdnsrr http://php.net/manual/en/function.checkdnsrr.php

$domain = "stackoverflow.com";

checkdnsrr($domain , "A");

//returns true if has a dns A record, false otherwise

【讨论】:

  • 如果您想检查具有有效结构但尚未注册(尚未)的域,则不是很有用。
  • 不是必须的,php 会在网络中查找 dns 服务器,如果您在网络中的本地级别注册域,您将得到来自 checkdnsrr 的真实响应
  • 所以您基本上是建议先在本地注册每个要检查的域?这没有意义,您也可以在本地注册无效域,这超出了@richard-knop 在这里试图完成的目的。
【解决方案5】:

首先,你应该澄清你的意思是:

  1. 个别域名标签
  2. 整个域名(即多个点分隔标签)
  3. 主机名

之所以需要区分,是因为标签在技术上可以包含任何个字符,包括 NUL、@ 和“.”字符。 DNS 支持 8 位,并且完全有可能拥有一个包含“an\0odd\.l@bel”条目的区域文件。当然不建议这样做,尤其是因为人们很难将标签内的点与分隔标签区分开来,但它是合法的。

然而,URLs 需要一个 host 名称,这些名称受 RFC 952 和 1123 的约束。有效的 host 名称是一个子集域名名。具体来说,只允许使用字母、数字和连字符。此外,第一个和最后一个字符不能是连字符。 RFC 952 不允许第一个字符使用数字,但 RFC 1123 随后放宽了这一点。

因此:

  • a - 有效
  • 0 - 有效
  • a- - 无效
  • a-b - 有效
  • xn--dasdkhfsd - 有效(IDN 的 punycode 编码)

在我的脑海中,我认为不可能用一个简单的正则表达式使a- 示例无效。我能想到的最好的检查 single host 标签是:

if (preg_match('/^[a-z\d][a-z\d-]{0,62}$/i', $label) &&
   !preg_match('/-$/', $label))
{
    # label is legal within a hostname
}

更复杂的是,一些域名条目(通常是SRV 记录)使用带有下划线前缀的标签,例如_sip._udp.example.com。这些不是主机名,而是合法的域名。

【讨论】:

    【解决方案6】:

    我认为一旦您使用 Erklan 的想法隔离了域名:

    $myUrl = "http://www.domain.com/link.php"; $myParsedURL = parse_url($myUrl); $myDomainName= $myParsedURL['host'];

    你可以使用:

    if(false === filter_var($myDomainName, FILTER_VALIDATE_URL)) {
    // 测试失败
    
    }

    PHP5s 的过滤器函数正是出于我所想的目的。

    我知道它没有严格回答您的问题,因为它不使用正则表达式。

    【讨论】:

    • 我不确定这是否真的有效。 URI 的 RRF(这是过滤器所做的)包括诸如 file:///some/path 之类的内容。 URL/URI 不一定包含有效的主机名。
    【解决方案7】:

    这是另一种没有正则表达式的方法。

    $myUrl = "http://www.domain.com/link.php";
    $myParsedURL = parse_url($myUrl);
    $myDomainName= $myParsedURL['host'];
    $ipAddress = gethostbyname($myDomainName);
    if($ipAddress == $myDomainName)
    {
       echo "There is no url";
    }
    else
    {
       echo "url found";
    }
    

    【讨论】:

    • 去掉一个等号,应该是这样 $ipAddress == $myDomainName
    • parse_url('www.domain.com') 导致结果数组的键为 path 而不是 host
    【解决方案8】:

    正则表达式是检查域验证的最有效方法。如果您坚决不使用正则表达式(IMO 很愚蠢),那么您可以拆分域的每个部分:

    • 万维网。 /子域
    • 域名
    • .extension

    然后,您必须在某种循环中检查每个字符,以查看它是否与有效域匹配。

    就像我说的,使用正则表达式更有效。

    【讨论】:

    • 肯定正则表达式不是检查域验证的最有效方法。逐个字符或类似的东西迭代字符会更好。
    【解决方案9】:

    您的正则表达式很好,但您没有使用preg_match 对。它返回 int(0 或 1),而不是布尔值。写if(!preg_match($regex, $string)) { ... }

    【讨论】:

      【解决方案10】:

      如果不想使用正则表达式,可以试试这个:

      $str = 'domain-name';
      
      if (ctype_alnum(str_replace('-', '', $str)) && $str[0] != '-' && $str[strlen($str) - 1] != '-') {
          echo "Valid domain\n";
      } else {
          echo "Invalid domain\n";
      }
      

      但正如所说,正则表达式是最好的工具。

      【讨论】:

        【解决方案11】:

        如果要检查特定域名或ip地址是否存在,也可以使用checkdnsrr
        这是文档http://php.net/manual/en/function.checkdnsrr.php

        【讨论】:

          【解决方案12】:

          对我来说,一个有效的域是我可以注册的东西,或者至少是看起来我可以注册的东西。这就是我喜欢将其与“localhost”-names 分开的原因。

          最后我对主要问题感兴趣,如果避免使用正则表达式会更快,这是我的结果:

          <?php
          function filter_hostname($name, $domain_only=false) {
              // entire hostname has a maximum of 253 ASCII characters
              if (!($len = strlen($name)) || $len > 253
              // .example.org and localhost- are not allowed
              || $name[0] == '.' || $name[0] == '-' || $name[ $len - 1 ] == '.' || $name[ $len - 1 ] == '-'
              // a.de is the shortest possible domain name and needs one dot
              || ($domain_only && ($len < 4 || strpos($name, '.') === false))
              // several combinations are not allowed
              || strpos($name, '..') !== false
              || strpos($name, '.-') !== false
              || strpos($name, '-.') !== false
              // only letters, numbers, dot and hypen are allowed
          /*
              // a little bit slower
              || !ctype_alnum(str_replace(array('-', '.'), '', $name))
          */
              || preg_match('/[^a-z\d.-]/i', $name)
              ) {
                  return false;
              }
              // each label may contain up to 63 characters
              $offset = 0;
              while (($pos = strpos($name, '.', $offset)) !== false) {
                  if ($pos - $offset > 63) {
                      return false;
                  }
                  $offset = $pos + 1;
              }
              return $name;
          }
          ?>
          

          velcrow 's function 和 10000 次迭代相比的基准测试结果(complete results 包含许多代码变体。找到最快的很有趣。):

          filter_hostname($domain);// $domains: 0.43556308746338 $real_world: 0.33749794960022
          is_valid_domain_name($domain);// $domains: 0.81832790374756 $real_world: 0.32248711585999
          

          $real_world 不包含超长域名以产生更好的结果。现在我可以回答你的问题了:使用ctype_alnum() 可以在没有正则表达式的情况下实现它,但由于preg_match() 更快,我更喜欢这样。

          如果您不喜欢“local.host”是有效域名这一事实,请使用this function,而不是对公共 tld 列表有效。也许有人找到时间将两者结合起来。

          【讨论】:

            【解决方案13】:

            正确的答案是你不......你让一个单元测试的工具为你工作:

            // return '' if host invalid --
            private function setHostname($host = '')
            {
                $ret = (!empty($host)) ? $host : '';
                if(filter_var('http://'.$ret.'/', FILTER_VALIDATE_URL) === false) {
                    $ret = '';
                }
                return $ret;
            }
            

            进一步阅读:https://www.w3schools.com/php/filter_validate_url.asp

            【讨论】:

            • 可能需要额外的验证,因为 IP 地址可以通过此验证。
            【解决方案14】:

            我知道这是一个老问题,但它是 Google 搜索的第一个答案,所以看起来很相关。我最近遇到了同样的问题。我的解决方案是只使用公共后缀列表:

            https://publicsuffix.org/learn/

            列出的建议的语言特定库都应该允许轻松验证域格式以及顶级域有效性。

            【讨论】:

              【解决方案15】:

              如果您可以运行 shell 命令,以下是确定域是否已注册的最佳方法。

              这个函数返回false,如果没有注册域名,否则返回域名。

              function get_domain_name($domain) { 
                  //Step 1 - Return false if any shell sensitive chars or space/tab were found
                  if(escapeshellcmd($domain)!=$domain || count(explode(".", $domain))<2 || preg_match("/[\s\t]/", $domain)) {
                          return false;
                  }
              
                  //Step 2 - Get the root domain in-case of subdomain
                  $domain = (count(explode(".", $domain))>2 ? strtolower(explode(".", $domain)[count(explode(".", $domain))-2].".".explode(".", $domain)[count(explode(".", $domain))-1]) : strtolower($domain));
              
                  //Step 3 - Run shell command 'dig' to get SOA servers for the domain extension
                  $ns = shell_exec(escapeshellcmd("dig +short SOA ".escapeshellarg(explode(".", $domain)[count(explode(".", $domain))-1]))); 
              
                  //Step 4 - Return false if invalid extension (returns NULL), or take the first server address out of output
                  if($ns===NULL) {
                          return false;
                  }
                  $ns = (((preg_split('/\s+/', $ns)[0])[strlen(preg_split('/\s+/', $ns)[0])-1]==".") ? substr(preg_split('/\s+/', $ns)[0], 0, strlen(preg_split('/\s+/', $ns)[0])-1) : preg_split('/\s+/', $ns)[0]);
              
                  //Step 5 - Run another dig using the obtained address for our domain, and return false if returned NULL else return the domain name. This assumes an authoritative NS is assigned when a domain is registered, can be improved to filter more accurately.
                  $ans = shell_exec(escapeshellcmd("dig +noall +authority ".escapeshellarg("@".$ns)." ".escapeshellarg($domain))); 
                  return (($ans===NULL) ? false : ((strpos($ans, $ns)>-1) ? false : $domain));
              }
              

              优点

              1. 适用于任何域,而 php dns 功能可能在某些域上失败。 (我的 .pro 域在 php dns 上失败)
              2. 适用于没有任何 dns(如 A)记录的新域
              3. Unicode 友好

              缺点

              1. 可能使用了 shell 执行

              【讨论】:

                【解决方案16】:
                <?php
                
                if(is_valid_domain('https://www.google.com')==1){
                  echo 'Valid';
                }else{
                   echo 'InValid';
                }
                
                 function is_valid_domain($url){
                
                    $validation = FALSE;
                    /*Parse URL*/    
                    $urlparts = parse_url(filter_var($url, FILTER_SANITIZE_URL));
                
                    /*Check host exist else path assign to host*/    
                    if(!isset($urlparts['host'])){
                        $urlparts['host'] = $urlparts['path'];
                    }
                
                    if($urlparts['host']!=''){
                        /*Add scheme if not found*/        if (!isset($urlparts['scheme'])){
                        $urlparts['scheme'] = 'http';
                        }
                
                        /*Validation*/        
                    if(checkdnsrr($urlparts['host'], 'A') && in_array($urlparts['scheme'],array('http','https')) && ip2long($urlparts['host']) === FALSE){ 
                        $urlparts['host'] = preg_replace('/^www\./', '', $urlparts['host']);
                        $url = $urlparts['scheme'].'://'.$urlparts['host']. "/";            
                
                            if (filter_var($url, FILTER_VALIDATE_URL) !== false && @get_headers($url)) {
                                $validation = TRUE;
                            }
                        }
                    }
                
                    return $validation;
                
                }
                ?>
                

                【讨论】:

                  【解决方案17】:

                  在阅读了添加功能的所有问题后,我决定我需要更准确的东西。 这是我想出的适合我的方法。

                  如果您需要专门验证主机名(它们必须以字母数字字符开头和结尾,并且仅包含字母数字和连字符),此功能就足够了。

                  function is_valid_domain($domain) {
                      // Check for starting and ending hyphen(s)
                      if(preg_match('/-./', $domain) || substr($domain, 1) == '-') {
                          return false;
                      }
                  
                      // Detect and convert international UTF-8 domain names to IDNA ASCII form
                      if(mb_detect_encoding($domain) != "ASCII") {
                          $idn_dom = idn_to_ascii($domain);
                      } else {
                          $idn_dom = $domain;
                      }
                  
                      // Validate
                      if(filter_var($idn_dom, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) != false) {
                          return true;
                      }
                      return false;
                  }
                  

                  请注意,此功能适用于大多数(尚未测试所有语言)LTR 语言。它不适用于 RTL 语言。

                  is_valid_domain('a');                                                                       Y
                  is_valid_domain('a.b');                                                                     Y
                  is_valid_domain('localhost');                                                               Y
                  is_valid_domain('google.com');                                                              Y
                  is_valid_domain('news.google.co.uk');                                                       Y
                  is_valid_domain('xn--fsqu00a.xn--0zwm56d');                                                 Y
                  is_valid_domain('area51.com');                                                              Y
                  is_valid_domain('japanese.コム');                                                           Y
                  is_valid_domain('домейн.бг');                                                               Y
                  is_valid_domain('goo gle.com');                                                             N
                  is_valid_domain('google..com');                                                             N
                  is_valid_domain('google-.com');                                                             N
                  is_valid_domain('.google.com');                                                             N
                  is_valid_domain('<script');                                                                 N
                  is_valid_domain('alert(');                                                                  N
                  is_valid_domain('.');                                                                       N
                  is_valid_domain('..');                                                                      N
                  is_valid_domain(' ');                                                                       N
                  is_valid_domain('-');                                                                       N
                  is_valid_domain('');                                                                        N
                  is_valid_domain('-günter-.de');                                                             N
                  is_valid_domain('-günter.de');                                                              N
                  is_valid_domain('günter-.de');                                                              N
                  is_valid_domain('sadyasgduysgduysdgyuasdgusydgsyudgsuydgusydgsyudgsuydusdsdsdsaad.com');    N
                  is_valid_domain('2001:db8::7');                                                             N
                  is_valid_domain('876-555-4321');                                                            N
                  is_valid_domain('1-876-555-4321');                                                          N
                  

                  【讨论】:

                    【解决方案18】:

                    查看php函数checkdnsrr

                    function validate_email($email){
                    
                       $exp = "^[a-z\'0-9]+([._-][a-z\'0-9]+)*@([a-z0-9]+([._-][a-z0-9]+))+$";
                    
                       if(eregi($exp,$email)){
                    
                          if(checkdnsrr(array_pop(explode("@",$email)),"MX")){
                            return true;
                          }else{
                            return false;
                          }
                    
                       }else{
                    
                          return false;
                    
                       }   
                    }
                    

                    【讨论】:

                      【解决方案19】:

                      这是javascript中的域名验证:

                      <script>
                      function frmValidate() {
                       var val=document.frmDomin.name.value;
                       if (/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(val)){
                            alert("Valid Domain Name");
                            return true;
                       } else {
                            alert("Enter Valid Domain Name");
                            val.name.focus();
                            return false;
                       }
                      }
                      </script>
                      

                      【讨论】:

                        【解决方案20】:

                        这很简单。一些 php egnine 有 split() 的问题。 下面的代码将起作用。

                        <?php
                        $email = "vladimiroliva@ymail.com"; 
                        $domain = strtok($email, "@");
                        $domain = strtok("@");
                        if (@getmxrr($domain,$mxrecords)) 
                           echo "This ". $domain." EXIST!"; 
                        else 
                           echo "This ". $domain." does not exist!"; 
                        ?>
                        

                        【讨论】:

                          猜你喜欢
                          • 2011-03-08
                          • 2011-03-02
                          • 2013-08-22
                          • 1970-01-01
                          • 2011-05-27
                          • 1970-01-01
                          • 2011-01-31
                          • 2018-12-13
                          • 1970-01-01
                          相关资源
                          最近更新 更多