ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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,并不是说我推荐使用正则表达式,但至少我们可以希望任何使用此类地址进行唱歌等的人在失败时都不会抱怨:)

e
emkey08

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

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

RFC5322

RFC5321

RFC3696

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


如上所述,它不适用于所有电子邮件地址。另请参阅我的答案中的失败测试列表,以查看一些引用的字符串确实有效,而另一些则无效。
不,在该模式 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 将落后相当长的一段时间,即使他们现在更改它(我已发布错误报告)。
A
Alex

为此,您可以使用 filter_var

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

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

根据我的经验,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。
F
Fluffeh

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

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


s
smottt

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

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

它认为 error`@gmail.com 是有效的电子邮件。请注意,它包含 `
P
Pelmered

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

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 之前(我认为)不支持直接访问返回类型。但是,到目前为止,这是一个非常旧且不受支持的版本,所以我可能会按照您的建议进行操作。
我刚刚测试了它,你实际上是对的。从几年前开始编码的人的角度来看,程序员必须处理什么才能实现最简单的事情是令人难以置信的。
接收电子邮件不需要 MX 条目。如果不存在,将使用 A 条目。请参阅serverfault.com/questions/470649/…
@ChristopherK。哦,那很有趣。我在各种项目中使用过这样的检查,并且可能已经验证了超过一百万个电子邮件地址,这从来都不是问题。我认为这是一个很好的检查,以确保域实际上指向某个地方。也许可以使用对 A 指针的后备检查,但这可能弊大于利,即使它看起来像是更正确的检查。
M
Mostafa Norzade

使用以下代码:

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

在大多数情况下,您可能不想在验证时删除这样的非法字符。如果您检查包含非法字符的电子邮件地址,则不应验证。
C
Community

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

对我来说检查电子邮件的正确方法是:检查符号@是否存在,并且在它之前和之后有一些非@符号: /^[^@]+@[^@]+$/ 尝试发送电子邮件至这个地址带有一些“激活码”。当用户“激活”他的电子邮件地址时,我们会看到一切正常。当然,当用户输入“奇怪”的电子邮件时,您可以在前端显示一些警告或工具提示,以帮助他避免常见错误,例如域部分没有点或名称中有空格而没有引用等等。但是如果用户真的想要它,你必须接受地址“hello@world”。此外,您必须记住,电子邮件地址标准曾经并且可以不断发展,因此您不能只输入一些“标准有效”的正则表达式,一劳永逸。而且您必须记住,一些具体的互联网服务器可能无法满足通用标准的某些细节,实际上可以使用自己的“修改后的标准”。

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


您的正则表达式确实会检查 @,但它并没有真正检查它对于管理电子邮件的任何 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)。但是您在服务器上有旧功能,在某些情况下您无法更新。你会用一些新的有效电子邮件失去客户。此外,我再次注意到,并非所有电子邮件服务服务器都严格按照电子邮件地址的通用和现代标准工作。
B
Bud Damyanov

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

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

查看更多here


如果电子邮件地址的主机部分是 IPv6 格式的 IP 地址,我认为它不会起作用
接收电子邮件不需要 MX 条目。如果不存在,将使用 A 条目。请参阅serverfault.com/questions/470649/…
s
smulholland2

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


T
Thielicious
/(?![[: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 请求还有很多其他方法,因此您不能假设任何请求都是安全的……即使您检查了浏览器代理。
S
Stephen

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

T
ThinkTrans

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

RFC 822 RFC 2822(取代 RFC 822) RFC 5322(取代 RFC 2822)

然而,RFC 5322 以最具技术性的方式定义了电子邮件 ID 及其命名结构。这更适合为互联网标准奠定基础,该标准足够自由以允许所有用例,足够保守以将其绑定在某种形式主义中。

然而,来自软件开发者社区的电子邮件验证要求,有以下需求——

避免不需要的垃圾邮件发送者

确保用户不会犯错误

确保电子邮件 ID 属于输入它的实际人

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

因此,根据实际情况,实际要求归结为 -

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

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

第一个要求源于开发人员不希望完全“非电子邮件式”字符串作为电子邮件传递的需要。这通常涉及空格、不带“@”符号或不带域名的字符串。鉴于域名的 punycode 表示,如果需要启用域验证,他们需要参与确保域名有效的完整实施。因此,鉴于这方面需求的基本性质,验证“@.”是满足需求的唯一恰当方式。

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

对于那些想要更深入地了解更相关的实现的人,他们可以遵循以下验证方法。 <电子邮件本地部分>@<域名>

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

那些有兴趣了解在实施国际化电子邮件解决方案时可能遇到的整体流程、挑战和问题的人,还可以查看以下 RFC:

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


T
ThinkTrans

问题标题相当笼统,但问题的主体表明它是关于基于 PHP 的解决方案。将尝试解决这两个问题。

一般而言,对于所有编程语言:通常,使用 reg-ex 验证电子邮件地址是任何基于 Internet 的服务提供商都应该停止的事情。各种域名和电子邮件的可能性- 邮件地址的种类增加了很多,任何验证尝试,如果没有很好的考虑最终可能会拒绝一些有效的用户进入您的系统。为了避免这种情况,最好的方法之一是向用户发送电子邮件并验证它是否被接收。“Universal Acceptance Steering Group”的好人编制了一个语言列表,这些库被发现符合/不符合各种参数,包括针对国际化域名和国际化电子邮件地址的验证。请通过 herehere 找到这些文档的链接。

具体来说 PHP:PHP 中有一个很好的库,即 EmailValidator。它是一个电子邮件地址验证器,包括许多验证方法,例如 DNS 验证。特别推荐的验证器称为 RFCValidator,它根据多个 RFC 验证电子邮件地址。在包容 IDN 和国际化电子邮件地址方面,它具有良好的合规性。


u
user1444314

我已经使 Python 和 PHP 实现正确验证任何电子邮件地址,该地址被确认为来自真实域的邮件服务器的真实电子邮件地址。

根据 GPL-3.0 许可发布。

你去:

https://lja.fi/index.php/github-stuff/

--lja


G
Gray Programmerz

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

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("foo@gmail.com")) echo "valid";
if(!isValidEmail("fo^o@gmail.com")) echo "invalid";