如何在PHP中验证域名?

How to validate domain name in PHP?

提问人:Richard Knop 提问时间:11/18/2009 最后编辑:kenderRichard Knop 更新时间:9/24/2020 访问量:127451

问:

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

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

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');
}
PHP 正则表达 式域名

评论

2赞 Andrew 11/18/2009
为什么要避免使用正则表达式?有效率
0赞 Matteo Riva 11/18/2009
@your编辑:您错误地使用了“===”,preg_match返回一个 int,而不是 .false
2赞 Alnitak 9/23/2010
domaia@name 有效的域名。但是,它不是有效的主机名。看我的答案。

答:

3赞 James Brooks 11/18/2009 #1

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

  • 万维网。/ 子域
  • 域名
  • 。外延

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

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

评论

0赞 nacholibre 10/6/2016
可以肯定的是,正则表达式不是检查域验证的最有效方法。最好逐个 char 或类似的东西迭代 char。
1赞 Matteo Riva 11/18/2009 #2

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

$str = 'domain-name';

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

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

7赞 Erkan BALABAN 11/18/2009 #3

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

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

评论

0赞 RoboTamer 11/18/2011
去掉一个等号,应该看起来像这样$ipAddress == $myDomainName
0赞 eXe 3/20/2020
parse_url('www.domain.com')导致结果数组与键而不是pathhost
2赞 Arthur Reutenauer 11/18/2009 #4

你的正则表达式很好,但你没有正确使用。它返回一个(0 或 1),而不是布尔值。只需写preg_matchintif(!preg_match($regex, $string)) { ... }

6赞 Cups 11/19/2009 #5

我认为一旦你隔离了域名,比如说,使用 Erklan 的想法:

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

您可以使用:

if( false === filter_var( $myDomainName, FILTER_VALIDATE_URL ) ) {
// failed test

}

PHP5s 过滤器函数就是为了这个目的,我本来以为的。

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

评论

0赞 Josh Koenig 5/3/2011
我不确定这是否真的有效。URI 的 RRF(这是筛选器的作用)包括 file:///some/path 等内容。URL/URI 不一定包含有效的主机名。
-6赞 bong 9/23/2010 #6

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

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

10赞 Alnitak 9/23/2010 #7

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

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

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

但是,URL 中需要主机名,并且这些主机名由 RFC 952 和 1123 管理。有效主机名是域名的子集。具体来说,只允许使用字母、数字和连字符。此外,第一个和最后一个字符不能是连字符。RFC 952 不允许第一个字符使用数字,但 RFC 1123 随后放宽了这一点。

因此:

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

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

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

146赞 velcrow 1/15/2011 #8
<?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

评论

7赞 472084 6/15/2012
不要忘记检查是否 (count($pieces) > 1)
1赞 velcrow 2/21/2013
Kendall,谢谢你的正则表达式。此外,由于以下原因,现在限制为 253 个:blog.sacaluta.com/2011/12/...
4赞 kabeersvohra 7/28/2015
根据经验,您应该使用单引号在 php 中编写正则表达式,这样它就不会处理内部的任何特殊字符
1赞 nerdoc 3/9/2016
这个正则表达式不包括变音符域和其他类似的特殊字符,这些字符是完全有效的......
1赞 John 11/17/2018
125个赞成...此函数将拒绝有效的 UTF-8 域,它将接受电话号码作为域,它将拒绝 IPV6 ips,但接受 IPV4 ips,并且它使用 3 个性能繁重的正则表达式搜索来执行此操作。请谨慎使用。
67赞 RoboTamer 11/19/2011 #9

有了这个,您不仅可以检查域是否具有有效的格式,还可以检查它是否处于活动状态/分配了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。

评论

0赞 Nir Alfasi 10/10/2012
只是我会使用过滤器:而不是FILTER_VALIDATE_URLFILTER_VALIDATE_IP
0赞 Edson Medina 1/8/2013
FILTER_VALIDATE_URL只会发现 ASCII URL 有效;国际化域名(包含非 ASCII 字符)将失败。(php.net/manual/en/filter.filters.validate.php)
9赞 velcrow 2/21/2013
GetHostByName 会阻止 DNS 查找,所以不要运行它来循环大型数据集,否则运行时会很糟糕。
1赞 php_nub_qq 2/4/2015
@Templar它是一个验证主机名而不是 URL 的功能
6赞 Shadur 4/19/2016
对于 DNS 中实际不存在的有效域或主机名,这将失败。-1.
13赞 jacktrade 5/1/2012 #10

使用 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

评论

6赞 Ludo - Off the record 4/7/2015
如果您想检查具有有效结构但尚未注册的域,则不是很有用。
0赞 jacktrade 6/10/2019
没有必要,PHP会在网络中查找DNS服务器,如果您在网络中的本地级别注册域,您将从checkdnsrr获得真实的响应
2赞 Ludo - Off the record 6/18/2019
所以你基本上是建议先在本地注册你想检查的每个域名?这没有意义,您也可以在本地注册无效域,这违背了 @richard-knop 在这里试图实现的目的。
0赞 luky 7/31/2023
如果您想与现有域结合使用,则很有用
1赞 Agustinus Verdy 10/17/2012 #11

如果要检查某个特定的域名或IP地址是否存在,也可以使用
Here is the doc http://php.net/manual/en/function.checkdnsrr.php
checkdnsrr

-3赞 codeCraft 6/28/2013 #12

检查 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;

   }   
}
-3赞 KS Rajput 1/5/2015 #13

这是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>
1赞 mgutt 3/5/2015 #14

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

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

<?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的函数和10000次迭代(完整的结果包含许多代码变体)进行比较。找到最快的很有趣。

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”是有效域名这一事实,请使用此函数,该函数对公共 TLD 列表有效。也许有人有时间将两者结合起来。

-1赞 jeffers102 12/13/2017 #15

我知道这是一个老问题,但它是谷歌搜索的第一个答案,所以它似乎是相关的。我最近遇到了同样的问题。就我而言,解决方案是仅使用公共后缀列表:

https://publicsuffix.org/learn/

列出的建议语言特定库不仅可以轻松验证域格式,还可以轻松验证顶级域有效性。

评论

0赞 Christian Rauchenwald 10/4/2022
引用自网站:有些人使用 PSL 来确定什么是有效的域名,什么不是。这很危险。gTLD 和 ccTLD 在不断更新、来来去去,当然不是一成不变的。
39赞 Rob 2/15/2018 #16

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

评论

1赞 М.Б. 6/27/2019
嗯,这有效,但给出了一些误报: ''' >>> filter_var('成熟', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) => “成熟” '''
1赞 Accountant م 9/3/2019
@М.Б.根据规范 RFC 1035,这是一个有效的域名,“它们必须以字母开头,以字母或数字结尾,并且只有字母、数字和连字符作为内部字符。长度也有一些限制。标签不得超过 63 个字符。" .您可以添加更多限制,必须包含一个点才能制作您想要的东西.
0赞 М.Б. 9/3/2019
@Accountant好的,谢谢!我以为已经包含在验证器本身中。.
1赞 scott8035 12/13/2019
@Accountant م,我认为可能有一个 RFC 扩展了该定义,因为我曾经有域名“2tp.com”,而且我见过许多其他以数字开头的域名。
0赞 Loenix 2/3/2023
与在线域名的使用不匹配...
1赞 Mike Q 3/23/2018 #17

正确答案是你不...您可以让经过单元测试的工具为您完成工作:

// 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

评论

1赞 ion 5/19/2020
可能需要额外的验证,因为 IP 地址可以通过此验证。
1赞 Ajay Singh 5/13/2019 #18

如果可以运行 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 执行
0赞 Musab Ibn Siraj 5/17/2019 #19
<?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;

}
?>
0赞 GTodorov 12/5/2019 #20

在阅读了附加功能的所有问题后,我决定我需要更准确的东西。 这是我想出的对我有用的东西。

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

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