如何在PHP中验证电子邮件地址


218

我具有此功能来验证电子邮件地址:

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

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

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


1
如果有效,则可以。您真的不能做得更好,它太小了。唯一不好的是风格。validateEmail不会是路过的$email,也不会是路过的$EMAIL
斯坦(Stan)2012年

只是想确保我在代码中没有任何大问题:)
Cameron

另请参阅stackoverflow.com/questions/201323/…,以了解有关如何以及如何不使用正则表达式来验证电子邮件地址的更多信息。
legoscia 2012年

5
那将无法验证许多有效的电子邮件地址。例如*@example.com或'@example.com或me @ [127.0.0.1]或you @ [ipv6:08B0:1123:AAAA :: 1234]
编码器

7
@jcoder,不是我建议使用正则表达式,但是至少我们可以希望使用此类地址进行唱歌等操作的人在失败时都不会抱怨:)
HalilÖzgür13年

Answers:


568

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

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

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

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

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


既然您已经有了简单的答案,则可以随时阅读有关电子邮件地址验证的内容,如果您想学习还是以其他方式使用快速答案继续进行下去。别往心里放。

尝试使用正则表达式验证电子邮件地址是一项“不可能”的任务。我要说的是,您制作的正则表达式是没有用的。关于电子邮件地址和编写正则表达式以捕获错误的电子邮件地址,共有三个RFC,同时没有误报是凡人无法做的。签出此列表以测试PHP filter_var()函数使用的正则表达式的测试(失败和成功)。

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

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

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

注意,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
}

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


很好的答案,但是根据以下链接:haacked.com/archive/2007/08/21/…本地用户名可以用双引号引起来,但FILTER_VALIDATE_EMAIL不接受。
Daniel DeLeón13年

3
如上所述,它不适用于所有电子邮件地址。另请参阅我的答案中失败的测试列表,以查看某些带引号的字符串可以工作,而其他则不能。
PeeHaa

4
不,在该模式下失败的测试太多,电子邮件地址为testtest.pieterhordijk.com/test-pattern/MTAz :-)
PeeHaa 2014年

1
如果您需要在带有电子邮件的大文本字符串上使用“ preg_match_all”之类的功能,则此模式非常复杂。如果你们中的任何一个简单一些,请分享。我的意思是,如果您想:preg_match_all($ pattern,$ text_string,$ matches); 如果您需要解析很大的文本,那么这种复杂的模式将使服务器超载。
弗拉多2015年

4
@PeeHaa:Postfix 3.0支持它已有近两年了:postfix.org/SMTPUTF8_README.html,它包含在Ubuntu 16.04中,例如将包含在下一个Debian版本中。进出口有实验性的支持。Gmail等网络邮件提供商也增加了对发送/接收此类电子邮件的支持,尽管您尚无法创建unicode帐户。广泛的使用和支持是可以实现的,并且filter_var即使他们现在改变了它,也将落后相当一段时间(我已经发布了一个错误报告)。
伊基托

43

您可以为此使用filter_var

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

1
停止添加此功能,因为这不会验证域。如果您要添加some @ address,则此名称有效。不是!
Nentu先生

包含一个行函数的所有一个行函数是什么?我到处都看到他们。什么时候成为“事物”?(修辞)。这需要停止。
蓝色之水

15

以我的经验,regex解决方案有太多的误报,而解决方案有很多误报filter_var()(尤其是对于所有较新的TLD而言)。

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

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

这是我在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
Aaron Gillion

@AaronGillion尽管您对获取域部分的更好方法是正确的,但该方法仍将返回false,checkdnsrr()如果域中有@符号,则该方法仍将返回false 。
Jabari

11

我认为使用PHP的内置过滤器可能会更好-在这种情况下:

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


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

4

在有关电子邮件验证的“首要问题”中回答了此问题https://stackoverflow.com/a/41129750/1848217

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

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

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

此外,您还必须记住,电子邮件地址标准曾经而且可能会不断发展,因此,您不能一劳永逸地只键入一些“标准有效”的正则表达式。您必须记住,某些具体的Internet服务器可能会使通用标准的某些细节失效,并且实际上可以使用自己的“修改后的标准”。

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


1
您的regex会检查@,但实际上并不会根据管理电子邮件的任何RFC来检查其是否有效。它也不能按书面规定工作。我通过regex101.com运行它,但它与有效地址不匹配
Machavity

您只阅读正则表达式还是整个答案?完全不同意你的看法。请问我,根据RFC,gmail.com服务器假定joe@gmail.com和jo.e@gmail.com是同一地址吗?有许多服务器不能按标准或按FRESH标准工作。但是,thay服务于其用户的电子邮件。如果您一次输入某个正则表达式,并且仅以此进行验证,则不能保证它会在将来继续存在,并且将来的用户不会因“新方式”电子邮件而失败。因此,我的立场是相同的:要验证电子邮件地址的要点-仅发送激活电子邮件。
FlameStorm

@Machavity,但感谢您在正则表达式中的错误报告,我将其修复/^[^@]+@[^@+]$//^[^@]+@[^@]+$/
FlameStorm

支持您修复正则表达式,但是与该filter_var方法相比有何改进?它也不能解决接受格式错误的地址的问题。您的正则表达式会很乐意接受joe@domain有效的电子邮件地址(如果不是)
Machavity

@Machavity,例如,服务器上有一个具体的PHP版本,您无法将其更新为最新版本。例如,您有php 5.5.15。在2018年,有效电子邮件的标准得到了扩展。它将很快在php 7.3.10中实现。并且将具有良好的工作功能filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions)。但是您在服务器上具有旧功能,在某些情况下无法更新。然后,您将使用一些新的有效电子邮件来失去客户。另外,我再次注意到,并非所有的电子邮件服务服务器都严格按照通用和现代的电子邮件地址标准工作。
FlameStorm

3

如果要检查从电子邮件地址提供的域是否有效,请使用以下方法:

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

另外请记住,user@[IPv6:2001:db8::1]不能验证IPv4或IPv6作为电子邮件中的域部分(例如),只有命名主机可以被验证。

在这里查看更多。


我认为,如果电子邮件地址的主机部分为IPv6格式的IP地址,它将无法正常工作
-GordonM,

2

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

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

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


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

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

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

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


我也讨厌投票,没有解释。好吧,我猜他可能会说:浏览器电子邮件检查(客户端)根本不安全。任何人都可以通过更改代码将任何内容发送到服务器。因此,这是再次进行服务器端检查的最明显,最安全的方法。这里的问题是基于PHP的,因此Cameron显然是在寻找服务器解决方案而不是客户端解决方案。
强尼

这个答案可能不完全与PHP相关,但HTML建议涵盖仅使用电话/ PC的“标准”用户。用户在使用该网站时也可以直接在“他的”浏览器中获取信息。当然,不能在服务器端进行实际检查。顺便说一句,@ Thielicious提到了PHP的更改,因此他的评论与恕我直言有关。
k00ni

假设您“由于浏览器引擎具有自己的验证器,所以安全性为80%”,因此它可能获得了否决票。除了通过浏览器发送HTTP请求外,还有许多其他发送方法,因此即使您检查了浏览器代理,您也不能认为任何请求都是安全的。
Jabari
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.