【问题标题】:How to validate an email address in PHP如何在 PHP 中验证电子邮件地址
【发布时间】:2016-06-20 10:16:38
【问题描述】:

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

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

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

这样可以检查邮箱地址是否有效吗?

【问题讨论】:

  • 如果它有效,它就有效。你真的不能让它变得更好,它太小了。唯一不好的就是风格。 validateEmail 是正确的,以及通过 $email,而不是 $EMAIL
  • 只是想确保我在代码中没有任何重大问题 :)
  • 有关如何以及如何不使用正则表达式验证电子邮件地址的更多信息,另请参阅 stackoverflow.com/questions/201323/…
  • 这将无法验证许多有效的电子邮件地址。例如 *@example.com 或 '@example.com 或 me@[127.0.0.1] 或 you@[ipv6:08B0:1123:AAAA::1234]
  • @jcoder,并不是我推荐使用正则表达式,但至少我们可以希望任何使用此类地址进行唱歌等的人在失败时不会抱怨:)

标签: php regex email email-validation


【解决方案1】:

FILTER_VALIDATE_EMAIL 内置了一个更好的正则表达式,但任何正则表达式都可能产生不好的结果。

例如..

// "not an email" is invalid so its false.
php > var_export(filter_var("not an email", FILTER_VALIDATE_EMAIL));
false
// "foo@a.com" looks like an email, so it passes even though its not real.
php > var_export(filter_var("foo@a.com", FILTER_VALIDATE_EMAIL));
'foo@a.com'
// "foo@gmail.com" passes, gmail is a valid email server,
//  but gmail require more than 3 letters for the address.
var_export(filter_var("foo@gmail.com", FILTER_VALIDATE_EMAIL));
'foo@gmail.com'

您可能需要考虑使用像 Real Email 这样的 API,它可以进行深入的邮箱检查以检查电子邮件是否真实。

有点像..

$email = "foo@bar.com";
$api_key = ???;

$request_context = stream_context_create(array(
    'http' => array(
        'header'  => "Authorization: Bearer " . $api_key
    )
));

$result_json = file_get_contents("https://isitarealemail.com/api/email/validate?email=" . $email, false, $request_context);

if (json_decode($result_json, true)['status'] == "valid") {
    echo("email is valid");
} else if (json_decode($result_json, true)['status'] == "invalid") {
    echo("email is invalid");
} else {
  echo("email was unknown");
}

【讨论】:

    【解决方案2】:

    使用下面的代码:

    // Variable to check
    $email = "john.doe@example.com";
    
    // Remove all illegal characters from email
    $email = filter_var($email, FILTER_SANITIZE_EMAIL);
    
    
    // Validate e-mail
    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
      echo("Email is a valid email address");
    }
    

    【讨论】:

    • 在大多数情况下,您可能不想在验证时像这样去除非法字符。如果您检查包含非法字符的电子邮件地址,则不应验证。
    【解决方案3】:

    在这里阅读答案后,这就是我的结论:

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

    【讨论】:

    • array_slice 有什么原因吗?为什么不直接使用explode("@", $email)[1]? @ 字符可以出现在电子邮件地址的用户部分吗?
    • @User1337 我认为这是为了向后兼容。在 PHP 5.4 之前(我认为)不支持直接访问返回类型。但是,这是一个相当旧且不受支持的版本,所以我可能会按照您的建议进行操作。
    • 我刚刚测试过,其实你是对的。从几年前开始编码的人的角度来看,程序员必须处理的事情才能实现最简单的事情是令人难以置信的。
    【解决方案4】:

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

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

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

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

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


    现在您有了简单的答案,如果您想学习或只是使用快速答案并继续前进,请随时阅读有关电子邮件地址验证的信息。没有难过的感觉。

    尝试使用正则表达式验证电子邮件地址是一项“不可能”的任务。我什至会说你所做的那个正则表达式是没用的。有三个关于电子邮件地址的 rfc,编写正则表达式来捕获错误的电子邮件地址,同时没有误报,这是凡人无法做到的。查看 this list 以了解 PHP 的 filter_var() 函数使用的正则表达式的测试(失败和成功)。

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

    如果您想知道 PHP(当前)使用哪种正则表达式模式来验证电子邮件地址,请参阅 the PHP source

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

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

    <?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 = 'test@gmail.com';
    
    if (preg_match($pattern, $emailaddress) === 1) {
        // emailaddress is valid
    }
    

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

    【讨论】:

    • 如上所述,它不适用于 所有 电子邮件地址。另请参阅我的答案中的失败测试列表,以了解一些引用的字符串确实有效,而另一些则无效。
    • 不,太多失败的测试模式emailtester.pieterhordijk.com/test-pattern/MTAz :-)
    • 这种模式非常复杂,以防您需要将其与“preg_match_all”之类的函数一起用于包含电子邮件的大文本字符串。如果你们中的任何人有更简单的请分享。我的意思是如果你想: preg_match_all($pattern, $text_string, $matches);那么如果你需要解析非常大的文本,这种复杂的模式会使服务器过载。
    • 你的观点是什么@sergio?正如@sergio 所说,有几个“失败”。
    • @PeeHaa:Postfix 3.0 支持它已经快两年了:postfix.org/SMTPUTF8_README.html,它包含在 Ubuntu 16.04 中,并将包含在下一个 Debian 版本中,例如。 Exim 有实验支持。 Gmail 等网络邮件提供商也增加了对发送/接收此类电子邮件的支持,尽管您还不能创建 unicode 帐户。广泛使用和支持触手可及,filter_var 将落后相当长的一段时间,即使他们现在更改它(我已经发布了错误报告)。
    【解决方案5】:

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

    /*
    * 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]),只有 命名的 主机可以。

    查看更多here

    【讨论】:

    • 如果电子邮件地址的主机部分是 IPv6 格式的 IP 地址,我认为它不会起作用
    【解决方案6】:

    根据我的经验,regex 解决方案有太多误报,filter_var() 解决方案有误报(尤其是所有较新的TLDs)。

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

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

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

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

    【讨论】:

    • Neverbounce 声称他们的 API 能够验证 97% 的交付率。当然,只要您不介意交出您的联系人数据库。
    • stristr 如果有多个@符号,将无法获取域。最好explode('@',$email) 并检查sizeof($array)==2
    • @AaronGillion 虽然就获取域部分的更好方法而言,您是正确的,但该方法仍将返回 false,因为如果域中有 @ 符号,checkdnsrr() 将返回 false。跨度>
    【解决方案7】:
    /(?![[:alnum:]]|@|-|_|\.)./
    

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

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

    Find the regex used by HTML5 forms for validation
    https://regex101.com/r/mPEKmy/1

    【讨论】:

    • 我也讨厌没有解释的反对票。好吧,我猜他可能会说:浏览器电子邮件检查(客户端)根本不安全。任何人都可以通过更改代码向服务器发送任何内容。因此,进行(再次)服务器端检查是显而易见且最安全的方法。这里的问题是基于 PHP 的,所以很明显 Cameron 是在寻找服务器解决方案而不是客户端解决方案。
    • 这个答案可能与 PHP 不完全相关,但 HTML 建议涵盖了仅使用手机/PC 的“标准”用户。此外,用户在使用网站时直接在“他的”浏览器中获取信息。当然,服务器端的实际检查不包括在内。顺便说一句,@Thelicious 提到了 PHP 更改,所以他的评论与恕我直言有关。
    • 它可能因为假设您“80% 安全,因为浏览器引擎有自己的验证器”而遭到反对。除了通过浏览器之外,还有许多其他方式可以发送 http 请求,因此您不能假设任何请求都是安全的……即使您检查了浏览器代理。
    【解决方案8】:

    这不仅会验证您的电子邮件,还会清除意外字符:

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

    【讨论】:

      【解决方案9】:

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

      【讨论】:

        【解决方案10】:

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

        对我来说检查电子邮件的正确方法是:

        1. 检查符号@是否存在,并且在它之前和之后有一些非@符号:/^[^@]+@[^@]+$/
        2. 尝试使用一些“激活码”向该地址发送电子邮件。
        3. 当用户“激活”他的电子邮件地址时,我们会看到一切正常。

        当然,你可以在前端显示一些警告或工具提示,当用户 输入“奇怪”的电子邮件来帮助他避免常见错误,比如没有 域部分中的点或名称中的空格而不引用等。但 如果用户真的需要,你必须接受地址“hello@world”。

        另外,您必须记住,电子邮件地址标准曾经并且可以 evolute,所以你不能只输入一些“标准有效”的正则表达式,然后 永远。你必须记住,一些具体的互联网 服务器可能会失败一些通用标准的细节,实际上可以使用 自己的“修改标准”。

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

        【讨论】:

        • 您的正则表达式确实会检查 @,但它并没有真正检查它是否符合任何管理电子邮件的 RFC。它也不能像写的那样工作。我通过 regex101.com 运行它,但它无法匹配有效地址
        • 您是只读正则表达式还是整个答案?完全不同意你。请说我,根据 RFC gmail.com 服务器假设 joe@gmail.com 和 jo.e@gmail.com 是同一个地址?有很多服务器不符合标准或不符合 FRESH 标准。但是他们提供用户的电子邮件。如果您键入一些正则表达式并仅通过它进行验证,则无法保证它将来会保持正确,并且您未来的用户不会因为他们的“新方式”电子邮件而失败。所以,我的立场是一样的:如果你想验证电子邮件地址的要点 - 只需发送激活电子邮件。
        • @Machavity 但感谢正则表达式中的错误报告,我将其从 /^[^@]+@[^@+]$/ 修复为 /^[^@]+@[^@]+$/
        • 对您修复正则表达式的建议,但是与filter_var 方法相比,它如何改进?它也不能解决它接受格式错误的地址的问题。您的正则表达式会很乐意接受 joe@domain 作为有效的电子邮件地址,但它不是
        • @Machavity,例如,您的服务器上有一个具体的 PHP 版本,您无法将其更新到最新版本。例如,您有 php 5.5.15 。 2018 年有效电子邮件标准得到扩展。它将很快在 php 7.3.10 中实现。并且会有很好的功能filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions)。但是您在服务器上有旧功能,在某些情况下您无法更新。你会用一些新的有效电子邮件失去客户。此外,我再次注意到,并非所有电子邮件服务服务器都严格按照电子邮件地址的通用和现代标准工作。
        【解决方案11】:

        您可以为此使用filter_var

        <?php
           function validateEmail($email) {
              return filter_var($email, FILTER_VALIDATE_EMAIL);
           }
        ?>
        

        【讨论】:

        • 停止添加此功能,因为这不会验证域。如果您要添加 some@address,这是有效的。而不是!
        • 所有包含单行函数的单行函数是怎么回事?我到处都能看到它们。这什么时候变成了“东西”? (修辞)。这需要停止。
        • 我认为这是有道理的编辑 1 个函数比编辑 100 个位置更快。
        【解决方案12】:

        我认为使用 PHP 的内置 filters 可能会更好 - 在这种特殊情况下:

        当与FILTER_VALIDATE_EMAIL 参数一起提供时,它可以返回真或假。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-05-28
          • 2011-04-19
          • 2014-06-12
          • 1970-01-01
          • 2021-06-11
          相关资源
          最近更新 更多