LoginSignup
17

More than 5 years have passed since last update.

Laravelのemailバリデーションルールは何をチェックしているのかというと

Last updated at Posted at 2017-01-25

本文

Laravel のバリデーションには email というルールが用意されており、そのものずばりフィールドがメールアドレスとして正しいことをバリデートします。

実体としてはfilter_var()FILTER_VALIDATE_EMAILのチェックをしている1ため、RFC822違反のメールアドレスなどはチェックできません。

対応としては、いまどきそんなメールアドレスは無いと割り切る2か、またはカスタムバリデーションを書くのかな:whale2:

おわりです。

カスタムバリデーション

@mpyw さんの PHPで各種バリデーション - Qiitaをほとんどそのままいただきました。

        Validator::extend('simple_email', function ($attribute, $value, $parameters, $validator) {
          /*
          * ドメインの存在確認する?
          */
          $check_dns = empty($parameters[0]) ? 0 : 1;

          switch (true) {
              /*
              * PHP7.1.0 よりも前では FILTER_FLAG_EMAIL_UNICODE が無いため
              * filter_var($email, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE)
              * と書けないのです。
              */
              case false === filter_var($value, FILTER_VALIDATE_EMAIL):
              case !preg_match('/@(?!\[)(.+)\z/', $value, $m):
                  return false;
              case !$check_dns:
              case checkdnsrr($m[1], 'MX'):
              case checkdnsrr($m[1], 'A'):
              case checkdnsrr($m[1], 'AAAA'):
                  return true;
              default:
                  return false;
          }
        }, 'メールアドレスの形式が不正です。');
つかいかた
        Validator::make($request->all(), [
            'mail1' => 'simple_email', // DNSレコードのチェックはしない
            'mail2' => 'simple_email:1', // DNSレコードのチェックもする
        ]);

こんどこそおわりです。

調査メモ

Laravel5.4

Laravelをインストールしてバリデーションのソースを参照しよう
$ composer global require "laravel/installer"
$ laravel new blog

$ php blog/artisan --version
Laravel Framework 5.4.0

$ less blog/vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php
ValidatesAttributes.phpの該当部分
    /**
     * Validate that an attribute is a valid e-mail address.
     *
     * @param  string  $attribute
     * @param  mixed   $value
     * @return bool
     */
    protected function validateEmail($attribute, $value)
    {
        return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
    }

PHP7.1.1

ダウンロードしたPHPのソースを参照しよう
$ tar xzvf php-7.1.1.tar.gz

$ less php-7.1.1/ext/filter/logical_filters.c
logical_filters.cの該当部分
void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{
        /*
         * The regex below is based on a regex by Michael Rushton.
         * However, it is not identical.  I changed it to only consider routeable
         * addresses as valid.  Michael's regex considers a@b a valid address
         * which conflicts with section 2.3.5 of RFC 5321 which states that:
         *
         *   Only resolvable, fully-qualified domain names (FQDNs) are permitted
         *   when domain names are used in SMTP.  In other words, names that can
         *   be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
         *   in Section 5) are permitted, as are CNAME RRs whose targets can be
         *   resolved, in turn, to MX or address RRs.  Local nicknames or
         *   unqualified names MUST NOT be used.
         *
         * This regex does not handle comments and folding whitespace.  While
         * this is technically valid in an email address, these parts aren't
         * actually part of the address itself.
         *
         * Michael's regex carries this copyright:
         *
         * Copyright © Michael Rushton 2009-10
         * http://squiloople.com/
         * Feel free to use and redistribute this code. But please keep this copyright notice.
         *
         */
        pcre       *re = NULL;
        pcre_extra *pcre_extra = NULL;
        int preg_options = 0;
        int         ovector[150]; /* Needs to be a multiple of 3 */
        int         matches;
        zend_string *sregexp;
        const char regexp0[] = "/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E\\pL\\pN]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F\\pL\\pN]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E\\pL\\pN]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F\\pL\\pN]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iDu";
        const char regexp1[] = "/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD";
        const char *regexp;
        size_t regexp_len;

        if (flags & FILTER_FLAG_EMAIL_UNICODE) {
                regexp = regexp0;
                regexp_len = sizeof(regexp0) - 1;
        } else {
                regexp = regexp1;
                regexp_len = sizeof(regexp1) - 1;
        }

        /* The maximum length of an e-mail address is 320 octets, per RFC 2821. */
        if (Z_STRLEN_P(value) > 320) {
                RETURN_VALIDATION_FAILED
        }

        sregexp = zend_string_init(regexp, regexp_len, 0);
        re = pcre_get_compiled_regex(sregexp, &pcre_extra, &preg_options);
        if (!re) {
                zend_string_release(sregexp);
                RETURN_VALIDATION_FAILED
        }
        zend_string_release(sregexp);
        matches = pcre_exec(re, NULL, Z_STRVAL_P(value), (int)Z_STRLEN_P(value), 0, 0, ovector, 3);

        /* 0 means that the vector is too small to hold all the captured substring offsets */
        if (matches < 0) {
                RETURN_VALIDATION_FAILED
        }

}
/* }}} */

IPv6やPunycode対応のためか、自分が知ってた頃よりさらにチェックロジックが複雑になっている…

参考と注釈

'email'
The field under validation must be formatted as an e-mail address.

'FILTER_VALIDATE_EMAIL'
値が妥当な e-mail アドレスであるかどうかを検証します。
この検証は、e-mail アドレスが RFC 822 に沿った形式であるかどうかを確かめます。 ただし、コメントおよび空白の折り返し (whitespace folding) には対応していません。


  1. すくなくとも、わたくしがさわりはじめたLaravel4からこうでした。 

  2. あきらめ 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17