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

How to validate an email address in PHP

提问人:Cameron 提问时间:8/19/2012 最后编辑:hakreCameron 更新时间:6/9/2023 访问量:393235

问:

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

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

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

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

PHP 正则表达 电子邮件验证

评论

2赞 Stan 8/19/2012
如果它有效,它就会有效。你不能真的让它变得更好,它太小了。唯一不好的是风格。 将是 corret,以及传递 ,而不是 .validateEmail$email$EMAIL
0赞 Cameron 8/19/2012
只是想确保我在代码中没有任何重大问题,这都是:)
0赞 legoscia 8/21/2012
另请参阅 stackoverflow.com/questions/201323/...,详细了解如何以及如何不使用正则表达式来验证电子邮件地址。
6赞 jcoder 9/29/2012
这将无法验证许多有效的电子邮件地址。例如,*@example.com 或 '@example.com 或 me@[127.0.0.1] 或 you@[ipv6:08B0:1123:AAAA::1234]
7赞 Halil Özgür 2/11/2013
@jcoder,并不是说我推荐正则表达式,但至少我们可以希望任何使用此类地址进行唱歌等的人在失败时都不会抱怨:)

答:

704赞 PeeHaa 8/19/2012 #1

检查电子邮件地址格式是否正确的最简单、最安全的方法是使用 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 源代码

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

评论

6赞 PeeHaa 3/22/2013
如上所述,它不适用于所有电子邮件地址。另请参阅我的答案中的失败测试列表,看看一些带引号的字符串确实有效,而另一些则无效。
4赞 PeeHaa 1/27/2014
不,对该模式的失败测试太多 emailtester.pieterhordijk.com/test-pattern/MTAz :-)
1赞 Vlado 10/29/2015
这种模式非常复杂,以防您需要将其与“preg_match_all”等功能一起使用,而不是在包含电子邮件的大文本字符串上。如果你们中的任何人有更简单的,请分享。我的意思是,如果你愿意:preg_match_all($pattern, $text_string, $matches);然后,如果您需要解析非常大的文本,这种复杂的模式将使服务器过载。
2赞 PeeHaa 3/7/2016
XSS预防与此答案无关。
6赞 iquito 5/20/2016
@PeeHaa:Postfix 3.0 支持它近两年了:postfix.org/SMTPUTF8_README.html ,它包含在 Ubuntu 16.04 中,例如,它将包含在下一个 Debian 版本中。Exim 有实验支持。像Gmail这样的网络邮件提供商也增加了对发送/接收此类电子邮件的支持,尽管您还不能创建unicode帐户。广泛使用和支持是触手可及的,即使他们现在改变它,也会落后相当长一段时间(我已经发布了一个错误报告)。filter_var
12赞 Fluffeh 8/19/2012 #2

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

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

53赞 Cameron Martin 8/19/2012 #3

为此,您可以使用filter_var

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

评论

3赞 Herr Nentu' 2/11/2020
停止添加此函数,因为这不会验证域。如果要添加some@address则这是有效的。事实并非如此!
9赞 4/2/2020
所有包含一行函数的单行函数是怎么回事?我到处都能看到他们。这是什么时候变成“东西”的?(修辞)。这需要停止。
7赞 Vladimir Hidalgo 9/22/2021
@user2607743我认为这是有道理的,如果你在一年后在你的项目中使用了 100 次,并且你想改进你验证电子邮件的方式......那么编辑 1 个函数会比编辑 100 个地方更快。
2赞 Christopher K. 2/21/2022
@HerrNentu'some@address怎么了?这是一个完全有效的电子邮件地址。就像root@localhost是其中之一。你只是做错了事。您正在从语法上验证电子邮件地址的形式,并且根据 RFC some@address有效。但您要做的是验证地址是否可访问。只有当主机在您的网络中已知时,才能访问some@address。若要验证可访问性,可以检查 DNS(检查主机是否存在)或使用 SMTP(检查邮箱是否存在)。address
1赞 Herr Nentu' 2/21/2022
@ChristopherK。问题在于它验证了没有域的电子邮件地址。
5赞 FlameStorm 12/14/2016 #4

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

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

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

当然,当用户 输入“奇怪”的电子邮件,以帮助他避免常见的错误,比如没有 域中的点、部分或名称中的空格,不带引号等。但 如果用户确实需要地址“hello@world”,则必须接受它。

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

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

评论

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

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

19赞 Jabari 2/4/2017 #6

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

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

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

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

public static function validateEmail(string $email): bool {

    // SET INITIAL RETURN VARIABLE
    // ENSURE -> EMAIL ISN'T EMPTY | AN @ SYMBOL IS PRESENT 

        $emailIsValid = FALSE;

        if (
            !empty($email) &&
            strpos($email, '@') !== FALSE
        ) {

            // GET EMAIL PARTS

                $email  = explode('@', $email);
                $user   = $email[0];
                $domain = $email[1];

            // VALIDATE EMAIL ADDRESS

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

    // RETURN RESULT

        return $emailIsValid;
}

评论

1赞 Tom Russell 9/25/2017
Neverbounce 声称他们的 API 能够验证 97% 的交付率。当然,只要您不介意交出您的联系人数据库。
0赞 Aaron Gillion 2/16/2020
stristr如果有多个 @ 符号,将无法获取域。最好检查一下explode('@',$email)sizeof($array)==2
0赞 Jabari 2/16/2020
@AaronGillion 虽然就获取域部分的更好方法而言,您是正确的,但该方法仍将返回 false,如果域中有 @ 符号,则返回 false。checkdnsrr()
12赞 Artur Bieniek 3/23/2017 #7

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

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

评论

0赞 Gray Programmerz 7/18/2022
它被视为有效的电子邮件。请注意,它包含 .error`@gmail.com`
2赞 Thielicious 8/14/2017 #8
/(?![[:alnum:]]|@|-|_|\.)./

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

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

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

评论

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

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

/*
* 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)       
    }
}

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

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

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

在此处查看更多内容。

评论

0赞 GordonM 5/2/2018
如果电子邮件地址的主机部分是 IPv6 格式的 IP 地址,我认为它不会起作用
0赞 Christopher K. 2/21/2022
接收电子邮件不需要 MX 条目。如果不存在,则将使用 A 条目。查看 serverfault.com/questions/470649/...
7赞 Pelmered 4/26/2019 #10

看完这里的答案后,这就是我最终得到的:

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

评论

0赞 User1337 4/1/2021
有什么原因吗?你为什么不直接使用?@字符可以出现在电子邮件地址的用户部分吗?array_sliceexplode("@", $email)[1]
0赞 Pelmered 4/2/2021
@User1337我认为这是为了向后兼容。在 PHP 5.4 之前(我认为)不支持像这样直接访问返回类型。但是,到目前为止,这是一个非常古老且不受支持的版本,所以我可能会按照您的建议进行操作。
0赞 User1337 4/3/2021
我刚刚测试了它,你实际上是对的。从几年前开始编码的人的角度来看,程序员为了实现最简单的事情而必须处理的事情令人难以置信。
0赞 Christopher K. 2/21/2022
接收电子邮件不需要 MX 条目。如果不存在,则将使用 A 条目。查看 serverfault.com/questions/470649/...
0赞 Pelmered 2/23/2022
@ChristopherK。哦,这很有趣。我在各种项目中使用过这样的检查,并且可能已经验证了超过一百万个电子邮件地址,这从来都不是问题。我认为这是一个很好的检查,以确保域实际上指向某个地方。也许可以使用 A 指针的回退检查,但即使它看起来更正确,也可能弊大于利。
7赞 Mostafa Norzade 10/3/2020 #11

使用以下代码:

// Variable to check
$email = "[email protected]";

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

评论

2赞 Pelmered 4/2/2021
在大多数情况下,您可能不想在验证时剥离这样的非法字符。如果您检查包含非法字符的电子邮件地址,则不应验证。
1赞 Stephen 9/13/2021 #12

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

例如。。

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

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

有点像..

$email = "[email protected]";
$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");
}
1赞 ThinkTrans 3/22/2022 #13

有三个 RFC 为“Internet 消息格式”奠定了基础。

  1. RFC 822 (英语)
  2. RFC 2822(取代 RFC 822)
  3. RFC 5322(取代 RFC 2822)

但是,RFC 5322 以最技术性的方式定义了电子邮件 ID 及其命名结构。这更合适,奠定一个互联网标准的基础,这个标准足够自由,允许所有的用例,但又足够保守,可以用某种形式主义来约束它。

但是,来自软件开发人员社区的电子邮件验证要求具有以下需求:

  • 避免不需要的垃圾邮件发送者
  • 确保用户不会无意中犯错
  • 确保电子邮件ID属于实际输入者

他们对实现一个技术上包罗万象的定义并不完全感兴趣,该定义允许电子邮件 ID 的所有形式(IP 地址,包括端口 ID 和所有)。适合其用例的解决方案应仅确保所有合法的电子邮件持有者都能够通过。从技术角度(RFC 5322 方式)到可用性角度(此解决方案),“合法”的定义差异很大。验证的可用性方面旨在确保通过验证机制验证的所有电子邮件 ID 都属于实际人员,并将它们用于通信目的。因此,这为验证过程引入了另一个角度,确保实际“正在使用”的电子邮件 ID,而 RFC-5322 定义显然不足以满足这一要求。

因此,在实践中,实际要求可以归结为以下几点——

  1. 确保一些非常基本的验证检查
  2. 确保输入的电子邮件正在使用中

第二个要求通常涉及,向输入的电子邮件 ID 发送寻求电子邮件的标准响应,并根据响应机制中描述的操作对用户进行身份验证。这是使用最广泛的机制,用于确保验证“正在使用”的电子邮件 ID 的第二个要求。这确实涉及从后端服务器实现的往返,而不是直接的单屏实现,但是,不能消除这一点。

第一个要求源于开发人员不希望完全“非电子邮件”字符串作为电子邮件传递的需要。这通常涉及空白、没有“@”符号或没有域名的字符串。鉴于域名的 punycode 表示形式,如果需要启用域验证,则需要进行全面的实施,以确保域名有效。因此,鉴于这方面需求的基本性质,验证“<something>@<something>.<something>”是满足要求的唯一合适方法。

可以满足此要求的典型正则表达式是: ^[^@\s]+@[^@\s.]+.[^@\s.]+$ 上面的正则表达式遵循标准的Perl正则表达式标准,被大多数编程语言广泛遵循。验证语句为: <除空格和“@”符号之外的任何内容>@<除空格和“@”符号之外的任何内容>.<除空格、@ 符号和点之外的任何内容>

对于那些想要更深入地了解更相关的实现的人,他们可以遵循以下验证方法。 <e-mail local part>@<domain name>

对于<电子邮件本地部分> - 遵循“普遍适用性指导小组”的指导方针 - UASG-026 对于<域名>,您可以使用标准库遵循任何域验证方法,具体取决于您的编程语言。有关该主题的最新研究,请遵循文档UASG-018A

那些有兴趣了解在实施国际化电子邮件解决方案时可能遇到的整个过程、挑战和问题的人,他们也可以通过以下 RFC:

RFC 6530(国际化电子邮件概述和框架) RFC 6531(国际化电子邮件的 SMTP 扩展) RFC 6532(国际化电子邮件标头) RFC 6533(国际化交付状态和处置通知) RFC 6855(对 UTF-8 的 IMAP 支持) RFC 6856(邮局协议版本 3 (POP3) 对 UTF-8 的支持) RFC 6857(国际化电子邮件的传递后邮件降级) RFC 6858(简化国际化电子邮件的 POP 和 IMAP 降级)。

0赞 Gray Programmerz 7/18/2022 #14

我准备了一个检查电子邮件有效性的函数:

function isValidEmail($email)
{
    $re = '/([\w\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)/m';
    preg_match_all($re, $email, $matches, PREG_SET_ORDER, 0);
    if(count($matches) > 0) return $matches[0][0] === $email;
    return false;
}

问题在于,它甚至认为无效的电子邮件是有效的。FILTER_VALIDATE_EMAIL

以下是示例:

if(isValidEmail("[email protected]")) echo "valid";
if(!isValidEmail("fo^[email protected]")) echo "invalid";