【问题标题】:Should I use filter_var to validate email?我应该使用 filter_var 来验证电子邮件吗?
【发布时间】:2013-10-22 15:27:32
【问题描述】:

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

【问题讨论】:

  • 如果您想确保电子邮件的格式正确,那么就可以了。如果您想知道它是否是一个真实的电子邮件地址,那么您很可能需要为电子邮件验证服务付费。
  • 您无需为验证服务付费。只需发送一封带有验证链接的电子邮件。
  • 是的,您绝对应该使用filter_var 来检查这是否是一个有效的电子邮件地址,因为这是 PHP 中检查电子邮件地址的唯一可靠方法……

标签: php validation


【解决方案1】:

是的,你应该这样做。

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

  1. 许多人已经看到了您将使用的代码(至少有两个),希望在将其合并到一个版本之前就有电子邮件验证方面的经验。
  2. 我是unit tested
  3. 其他人将使用相同的检查和report bugs,您可以在 php 更新中免费获得这些修复。

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

【讨论】:

  • 不幸的是,只有当有人真正维护并进一步改进这样的验证库时,这才是正确的。在这种情况下使用filter_var有一些危险:它在本地部分不支持UTF8,即使这已经有效了一段时间,实际上您必须自己将域名转换为idn_to_ascii才能验证国际域名, 这并不明显。为许多失败的电子邮件地址做好准备,如果您使用filter_var,这些电子邮件地址实际上是有效的,而当 unicode 更广泛地用于电子邮件地址时,情况只会变得更糟。
【解决方案2】:

是的,您应该使用filter_var,这就是您可以合并它的方式:

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

【讨论】:

    【解决方案3】:

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

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

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

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

    【讨论】:

    • 您需要清理电子邮件地址,因此您的观点并不能完全解释事情......查看过滤器选项等......
    【解决方案4】:

    是的。但是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() 非常即时(根据我的经验),我还没有找到它不起作用的环境。

    【讨论】:

    • 有时间我会检查一下,但我可以向您保证,您提到的 @gmailcom 示例没有通过 filter_var 验证。
    • 确实如此。这本身就是一个问题,尽管只是在非常模糊的情况下。有一个PHP bug report 列出了 FILTER_VALIDATE_EMAIL 的失败;执行 checkdnsrr() 将捕获那里的键盘示例中给出的所有误报。
    • 请注意,checkdnsrr 的超时时间为 20 秒,这显然无法更改(某些域的响应方式使其达到 20 秒,正如我在我的应用程序中所经历的那样),并且域名应首先由idn_to_ascii() 处理,以支持带有特殊字符(如ö、é 等)的域。我切换到使用passthru 调用dig,这样我就可以指定超时以及要使用的名称服务器。
    【解决方案5】:

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

    Symfony 自己的 Email Validation 也使用不支持 UTF-8 的 Regex。

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

    【讨论】:

      【解决方案6】:

      我看到的一些 cmets 与我的测试不匹配,所以我想指出我在 PHP 5.x 中发现的功能。如果您首先清理电子邮件,它将删除您不想要的所有字符,然后您可以验证。我有两个功能,以防有人只想做一个或另一个。

      检查电子邮件是否有效:

      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
      

      【讨论】:

      • 非常重要的是要注意,您应该首先使用 FILTER_SANITIZE_EMAIL。这允许 php 在通过其唯一的验证过程运行之前对其进行清理。
      猜你喜欢
      • 2020-02-04
      • 2015-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-19
      • 2017-04-11
      相关资源
      最近更新 更多