我应该使用filter_var来验证电子邮件吗?


Should I use filter_var to validate email?

我有一个类,可以在将每个输入发送到数据库层之前对其进行验证。请注意,我的问题不是关于逃跑或任何事情。我的数据库层将处理SQL注入问题。我要做的就是验证电子邮件是否有效,因为稍后该电子邮件可能会被用作"发送到"。例如,用户将通过发送到电子邮件的链接恢复对其帐户的访问权限。我读了很多关于filter_var的文章,有一群人反对它,而其他人则赞成。将重点放在"我只想验证电子邮件,而不是过滤数据库或html或XSS或其他任何东西"上,使用filter_var有问题吗?

是的,你应该这样做。

使用标准库验证而不是自制库验证具有多种好处:

  1. 许多眼球已经看到了您将使用的代码(嗯,至少两个),希望在合并到版本中之前就具有电子邮件验证的经验。
  2. 它经过单元测试。
  3. 其他人将使用相同的检查和报告错误,您可以在 php 更新中免费获得这些修复程序。

但是,检查电子邮件地址的格式只是第一道防线,如果您真的想知道它是真实的还是假的,则必须向其发送消息。

是的,你应该使用filter_var,这就是你如何合并它:

if( filter_var( $email ,FILTER_VALIDATE_EMAIL ) )
{
    /*
     * Rest of your code
     */
}

是的。但checkdnsrr()在这里可能也值得一提。

filter_var()将批准看似不完整的域,因为它在本地上下文中可能有效(例如someone@localhost)。这可能会导致误报,即人们只是错过了 TLD 或域名中的点(例如 hattie.jacques@gmailcom)

通过对域进行checkdnsrr()查找来捕获这些 - 如果您可以找到域的MX记录并且地址有效,那么您几乎已经尽力了。

示例代码:

if(filter_var($email, FILTER_VALIDATE_EMAIL)) 
{
    list($userName, $mailDomain) = explode("@", $email);
    if (!checkdnsrr($mailDomain, "MX"))
    {
        // Email is unreachable.
    }
}
else
{
    // Email is bad.
}

checkdnsrr()非常即时(根据我的经验),我还没有找到它不起作用的环境。

不幸的是,filter_var不支持地址本地部分(@ 之前)的 UTF8,要支持国际域名,您需要单独通过idn_to_ascii运行域名(这很麻烦且不明显)。

在我看来,这使得filter_var相当无用:Unicode电子邮件地址在野外出现的越多,合法的电子邮件地址就越失败,对于中国或巴西等国家来说尤其如此,这些国家对这些地址有明显的需求。 filter_var也不允许像root@localhost这样的电子邮件地址,这些地址是有效的,并且在服务器上下文中可能有用。

如果存在一个电子邮件库来根据特定说明进行验证,那将非常有用 - 是否只允许域名,还是也允许像 localhost 这样的任意主机,或者是否有应该有效的自定义域白名单?应该允许 unicode 吗?免费邮件域名(如@homail.com)的常见拼写错误也应该失败吗?

此外,以更具体的方式验证某些域名是明智的 - hotmail.com 目前不允许 unicode 字符,并且对可用字符有特定的限制。由于PHP应用程序中最常用的电子邮件地址集中在大约100个不同的域上,因此可以使用它以更好的方式验证这些域名。不幸的是,据我所知,目前还没有这样的库。

在 2021 年,没有。 filter_var的验证实现(从 PHP 8 开始)不验证国际域名,我认为这足以成为放弃的理由。

Symfony自己的电子邮件验证也使用没有UTF-8支持的正则表达式。

使用 https://github.com/egulias/EmailValidator。它遵循电子邮件 RFC,还可以检查 MX 记录以进一步验证域是否存在。

我看到的一些评论与我的测试不符,所以我想指出我在 PHP 5.x 中找到的功能。如果您首先对电子邮件进行 SANITIZATION,它将删除您不需要的所有字符,然后您可以进行验证。 我有两个功能,以防有人只想做一个或另一个。

检查电子邮件是否有效:

function isValidEmailAddress($email = '', $check_domain = false)
{
    if (empty($email)) {
        return false;
    } else {
        $success = true;
    }
    if (!filter_var((string) $email, FILTER_VALIDATE_EMAIL)) {
        $success = false;
    }
    if ($check_domain && $success) {
        list($name, $domain) = explode('@', trim($email) . "@");
        if (!checkdnsrr($domain, 'MX')) {
            $success = false;
        }
    }
    return array("success" => $success, "email" => $email);
}

删除<>和 UTF8 等:

function sanitizeEmailAddress($email = '') {
    if (!empty($email)) {
        $email = filter_var(strtolower(trim($email)), FILTER_SANITIZE_EMAIL);
    }
    return $email;
}

示例用法:

// test -- 
$list = array('goodÂ@bad.com', 'mikeq@google.com', 'puser@porsche.us', 'quick@us', '', '<yo@marist.edu>', 'hello@us.edu', 'rgil@yahoo.com', 'abc@2r.edu', 'one2@e3.edu', 'key@gen.us');
foreach($list as $email) {
    $ret = isValidEmailAddress( sanitizeEmailAddress($email), false );
    if ($ret['success']) {
        echo "GOOD " . $ret['email'];
    } else {
       echo "BAD  " .$email;
    }
        echo "'n";
    }

结果:

GOOD good@bad.com
GOOD mikeq@google.com
GOOD puser@porsche.us
BAD  quick@us
BAD  
GOOD yo@marist.edu
GOOD hello@us.edu
GOOD rgil@yahoo.com
GOOD abc@2r.edu
GOOD one2@e3.edu
GOOD key@gen.us

如果您使用域选项:$ret = isValidEmailAddress( sanitizeEmailAddress($email), true );

GOOD good@bad.com
GOOD mikeq@google.com
GOOD puser@porsche.us
BAD  quick@us
BAD  
GOOD yo@marist.edu
GOOD hello@us.edu
GOOD rgil@yahoo.com
BAD  abc@2r.edu
BAD  one2@e3.edu
BAD  key@gen.us