yiisoft /
validator
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | namespace Yiisoft\Validator\Rule; |
||
| 6 | |||
| 7 | use Yiisoft\Validator\Exception\UnexpectedRuleException; |
||
| 8 | use Yiisoft\Validator\Result; |
||
| 9 | use Yiisoft\Validator\RuleHandlerInterface; |
||
| 10 | use Yiisoft\Validator\ValidationContext; |
||
| 11 | |||
| 12 | use function is_string; |
||
| 13 | use function strlen; |
||
| 14 | |||
| 15 | /** |
||
| 16 | * Validates that the value is a valid email address. |
||
| 17 | */ |
||
| 18 | final class EmailHandler implements RuleHandlerInterface |
||
| 19 | { |
||
| 20 | 100 | public function validate(mixed $value, object $rule, ValidationContext $context): Result |
|
| 21 | { |
||
| 22 | 100 | if (!$rule instanceof Email) { |
|
| 23 | 1 | throw new UnexpectedRuleException(Email::class, $rule); |
|
| 24 | } |
||
| 25 | |||
| 26 | 99 | $result = new Result(); |
|
| 27 | 99 | if (!is_string($value)) { |
|
| 28 | 5 | return $result->addError($rule->getIncorrectInputMessage(), [ |
|
| 29 | 5 | 'attribute' => $context->getTranslatedAttribute(), |
|
| 30 | 5 | 'type' => get_debug_type($value), |
|
| 31 | ]); |
||
| 32 | } |
||
| 33 | |||
| 34 | 94 | $originalValue = $value; |
|
| 35 | |||
| 36 | 94 | if (!preg_match( |
|
|
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||
| 37 | '/^(?P<name>(?:"?([^"]*)"?\s)?)(?:\s+)?((?P<open><?)((?P<local>.+)@(?P<domain>[^>]+))(?P<close>>?))/', |
||
| 38 | $value, |
||
| 39 | $matches |
||
| 40 | )) { |
||
|
0 ignored issues
–
show
|
|||
| 41 | 7 | $valid = false; |
|
| 42 | } else { |
||
| 43 | /** @var array{name:string,local:string,open:string,domain:string,close:string} $matches */ |
||
| 44 | 87 | if ($rule->isIdnEnabled()) { |
|
| 45 | 28 | $matches['local'] = idn_to_ascii($matches['local']); |
|
| 46 | 28 | $matches['domain'] = idn_to_ascii($matches['domain']); |
|
| 47 | 28 | $value = implode('', [ |
|
| 48 | 28 | $matches['name'], |
|
| 49 | 28 | $matches['open'], |
|
| 50 | 28 | $matches['local'], |
|
| 51 | '@', |
||
| 52 | 28 | $matches['domain'], |
|
| 53 | 28 | $matches['close'], |
|
| 54 | ]); |
||
| 55 | } |
||
| 56 | |||
| 57 | 87 | if (is_string($matches['local']) && strlen($matches['local']) > 64) { |
|
| 58 | // The maximum total length of a user name or other local-part is 64 octets. RFC 5322 section 4.5.3.1.1 |
||
| 59 | // https://www.rfc-editor.org/rfc/rfc5321#section-4.5.3.1.1 |
||
| 60 | 2 | $valid = false; |
|
| 61 | } elseif ( |
||
| 62 | 85 | is_string($matches['local']) && |
|
| 63 | 85 | strlen($matches['local']) + strlen((string) $matches['domain']) > 253 |
|
| 64 | ) { |
||
| 65 | // There is a restriction in RFC 2821 on the length of an address in MAIL and RCPT commands |
||
| 66 | // of 254 characters. Since addresses that do not fit in those fields are not normally useful, the |
||
| 67 | // upper limit on address lengths should normally be considered to be 254. |
||
| 68 | // |
||
| 69 | // Dominic Sayers, RFC 3696 erratum 1690 |
||
| 70 | // https://www.rfc-editor.org/errata_search.php?eid=1690 |
||
| 71 | 2 | $valid = false; |
|
| 72 | } else { |
||
| 73 | 83 | $valid = preg_match($rule->getPattern(), $value) || ($rule->isNameAllowed() && preg_match( |
|
| 74 | 48 | $rule->getFullPattern(), |
|
| 75 | $value |
||
| 76 | )); |
||
| 77 | 83 | if ($valid && $rule->shouldCheckDns()) { |
|
| 78 | 8 | $valid = checkdnsrr($matches['domain']); |
|
| 79 | } |
||
| 80 | } |
||
| 81 | } |
||
| 82 | |||
| 83 | 94 | if ($valid === false && $rule->isIdnEnabled()) { |
|
| 84 | 10 | $valid = (bool) preg_match($rule->getIdnEmailPattern(), $originalValue); |
|
| 85 | } |
||
| 86 | |||
| 87 | 94 | if ($valid === false) { |
|
| 88 | 49 | $result->addError($rule->getMessage(), [ |
|
| 89 | 49 | 'attribute' => $context->getTranslatedAttribute(), |
|
| 90 | 'value' => $originalValue, |
||
| 91 | ]); |
||
| 92 | } |
||
| 93 | |||
| 94 | 94 | return $result; |
|
| 95 | } |
||
| 96 | } |
||
| 97 |