关于正则表达式:如何在PHP中验证电子邮件地址

How to validate an email address in PHP

我有这个功能来验证电子邮件地址:

1
2
3
4
5
function validateEMAIL($EMAIL) {
    $v ="/[a-zA-Z0-9_-.+]+@[a-zA-Z0-9-]+.[a-zA-Z]+/";

    return (bool)preg_match($v, $EMAIL);
}

这可以检查电子邮件地址是否有效吗?


检查电子邮件地址是否格式正确的最简单,最安全的方法是使用filter_var()函数:

1
2
3
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    // invalid emailaddress
}

此外,您可以检查域是否定义MX记录:

1
2
3
if (!checkdnsrr($domain, 'MX')) {
    // domain is not valid
}

但这仍然不能保证邮件存在。找到答案的唯一方法是发送确认邮件。

现在您可以随时阅读有关电子邮件地址验证的内容,如果您需要学习或者只是使用快速答案并继续前进。别往心里放。

尝试使用正则表达式验证电子邮件地址是一项"不可能"的任务。我甚至会说你所制作的正则表达式毫无用处。有三个rfc关于emailaddresses和写一个正则表达式来捕捉错误的电子邮件,同时没有误报是没有凡人可以做的事情。查看此列表,了解PHP的filter_var()函数使用的正则表达式的测试(失败和成功)。

即使是内置的PHP函数,电子邮件客户端或服务器也无法做到正确。仍然在大多数情况下filter_var是最好的选择。

如果您想知道PHP(当前)用于验证电子邮件地址的正则表达式模式,请参阅PHP源代码。

如果您想了解有关电子邮件地址的更多信息,我建议您开始阅读规范,但我必须警告您,这不是一个容易阅读的内容:

  • rfc5322
  • rfc5321
  • rfc3696
  • rfc6531(允许unicode字符,尽管许多客户端/服务器不接受它)

请注意,filter_var()已经说明,仅在PHP 5.2中提供。如果您希望它与早期版本的PHP一起使用,您可以使用PHP中使用的正则表达式:

1
2
3
4
5
6
7
8
9
<?php

$pattern = '/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD';

$emailaddress = '[email protected]';

if (preg_match($pattern, $emailaddress) === 1) {
    // emailaddress is valid
}

附:关于上面使用的正则表达式模式的注释(来自PHP源代码)。看起来Michael Rushton有一些版权。如上所述:"请随意使用并重新分发此代码。但请保留此版权声明。"


您可以使用filter_var。

1
2
3
4
5
<?php
   function validateEmail($email) {
      return filter_var($email, FILTER_VALIDATE_EMAIL);
   }
?>


根据我的经验,regex解决方案有太多误报,filter_var()解决方案有误报(特别是对于所有较新的TLD)。

相反,最好确保地址包含电子邮件地址(用户,"@"符号和域)的所有必需部分,然后验证域本身是否存在。

无法确定(服务器端)是否存在外部域的电子邮件用户。

这是我在Utility类中创建的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static function validateEmail($email)
{
    // SET INITIAL RETURN VARIABLES

        $emailIsValid = FALSE;

    // MAKE SURE AN EMPTY STRING WASN'T PASSED

        if (!empty($email))
        {
            // GET EMAIL PARTS

                $domain = ltrim(stristr($email, '@'), '@') . '.';
                $user   = stristr($email, '@', TRUE);

            // VALIDATE EMAIL ADDRESS

                if
                (
                    !empty($user) &&
                    !empty($domain) &&
                    checkdnsrr($domain)
                )
                {$emailIsValid = TRUE;}
        }

    // RETURN RESULT

        return $emailIsValid;
}


我认为你可能最好使用PHP的内置过滤器 - 在这种特殊情况下:

当提供FILTER_VALIDATE_EMAIL参数时,它可以返回true或false。


这不仅会验证您的电子邮件,还会针对意外字符进行清理:

1
2
3
4
5
6
7
8
9
$email  = $_POST['email'];
$emailB = filter_var($email, FILTER_SANITIZE_EMAIL);

if (filter_var($emailB, FILTER_VALIDATE_EMAIL) === false ||
    $emailB != $email
) {
    echo"This email adress isn't valid!";
    exit(0);
}

在关于电子邮件验证的"热门问题"中回答了此问题https://stackoverflow.com/a/41129750/1848217

For me the right way for checking emails is:

  • Check that symbol @ exists, and before and after it there are some non-@ symbols: /^[^@]+@[^@]+$/
  • Try to send an email to this address with some"activation code".
  • When the user"activated" his email address, we will see that all is right.
  • Of course, you can show some warning or tooltip in front-end when user
    typed"strange" email to help him to avoid common mistakes, like no
    dot in domain part or spaces in name without quoting and so on. But
    you must accept the address"hello@world" if user really want it.

    Also, you must remember that email address standard was and can
    evolute, so you can't just type some"standard-valid" regexp once and
    for all times. And you must remember that some concrete internet
    servers can fail some details of common standard and in fact work with
    own"modified standard".

    因此,只需检查@,在前端提示用户并在给定地址上发送验证电子邮件。


    如果您只是在寻找允许各种点,下划线和短划线的实际正则表达式,则如下所示:[a-zA-z0-9.-]+\@[a-zA-z0-9.-]+.[a-zA-Z]+。这将允许验证像tom_anderson.1-neo@my-mail_matrix.com这样相当愚蠢的电子邮件。


    在这里阅读答案之后,这就是我最终的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public static function isValidEmail(string $email) : bool
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return false;
        }

        //Get host name from email and check if it is valid
        $email_host = array_slice(explode("@", $email), -1)[0];

        // Check if valid IP (v4 or v6). If it is we can't do a DNS lookup
        if (!filter_var($email_host,FILTER_VALIDATE_IP, [
            'flags' => FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE,
        ])) {
            //Add a dot to the end of the host name to make a fully qualified domain name
            // and get last array element because an escaped @ is allowed in the local part (RFC 5322)
            // Then convert to ascii (http://us.php.net/manual/en/function.idn-to-ascii.php)
            $email_host = idn_to_ascii($email_host.'.');

            //Check for MX pointers in DNS (if there are no MX pointers the domain cannot receive emails)
            if (!checkdnsrr($email_host,"MX")) {
                return false;
            }
        }

        return true;
    }

    如果您想检查提供的电子邮件地址是否有效,请使用以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /*
    * Check for valid MX record for given email domain
    */

    if(!function_exists('check_email_domain')){
        function check_email_domain($email) {
            //Get host name from email and check if it is valid
            $email_host = explode("@", $email);    
            //Add a dot to the end of the host name to make a fully qualified domain name and get last array element because an escaped @ is allowed in the local part (RFC 5322)
            $host = end($email_host) .".";
            //Convert to ascii (http://us.php.net/manual/en/function.idn-to-ascii.php)
            return checkdnsrr(idn_to_ascii($host),"MX"); //(bool)      
        }
    }

    这是过滤大量无效电子邮件地址以及标准电子邮件验证的便捷方式,因为有效的电子邮件格式并不意味着有效的电子邮件。

    请注意,idn_to_ascii()(或其姐妹函数idn_to_utf8())函数可能在PHP安装中不可用,它需要扩展PECL intl> = 1.0.2和PECL idn> = 0.1。

    另请注意,无法验证IPv4或IPv6作为电子邮件中的域部分(例如user@[IPv6:2001:db8::1]),只能命名主机。

    在这里查看更多。


    1
    /(?![[:alnum:]]|@|-|_|\.)./

    如今,如果您使用带有type=email的HTML5表单,那么您已经有80%的安全性,因为浏览器引擎有自己的验证器。为了补充它,将此正则表达式添加到preg_match_all()并取消它:

    1
    if (!preg_match_all("/(?![[:alnum:]]|@|-|_|\.)./",$email)) { .. }

    找到HTML5表单用于验证的正则表达式
    https://regex101.com/r/mPEKmy/1