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


How to validate an email address in PHP

我有这个函数来验证电子邮件地址:

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

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

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

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

  • 射频5322
  • 射频5321
  • RFC3696
  • RFC6531(允许Unicode字符,尽管许多客户端/服务器不接受它)
您可以使用

filter_var。

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

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

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

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

这是我在实用程序类中创建的方法:

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

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

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

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

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

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

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

使用以下代码:

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

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

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

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

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

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

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

如果要

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

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

在此处查看更多内容。

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

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

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

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

查找 HTML5 表单用于验证
的正则表达式https://regex101.com/r/mPEKmy/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");
}

有三个 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 表示形式,如果需要启用域验证,他们需要参与全面的实施,以确保域名有效。因此,鉴于这方面需求的基本性质,验证"<某物>@<某物>.<某物>"是满足要求的唯一合适方法。

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

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

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

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

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

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

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

虽然我有点晚了,但这可以帮助那些仍然想检查更多内容的人。

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

这只会验证电子邮件格式是否正确。但是,如果您想使用更多,例如此电子邮件是否真的存在。您需要按照@Stephan的建议使用一些邮件验证API,但您需要为此付费。

如果您想免费使用API,那么这将是您的最佳选择。这是我目前在我的网站上使用的代码。

    if(isEmialExist("EMAIL_ADDRESS_THAT"))
{
    echo "email exists, real email";
}
else
{
    echo "email doesn't exist";
}

function isEmialExist($emailAddress)
{
    if (!filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
     return false; //invalid format
    }
    //now check if email really exist
    $postdata = http_build_query(array('api_key' => 'YOUR_API_KEY', 'email' => $emailAddress ));
    $url = "https://email-validator.com/api/v1.0/json";
    $opts = array('http' => array( 'method'  => 'POST', 'header'  => 'Content-Type: application/x-www-form-urlencoded', 'content' => $postdata ));
    
    $context  = stream_context_create($opts);
    $result = file_get_contents($url, false, $context);
    $data = json_decode($result, false);
    return $data->is_exists;
}

您可以在此处找到更多详细信息。https://email-validator.com/tutorial

此API的优点是,它提供实时结果,您将获得10,000个免费积分