Passed
Pull Request — master (#364)
by
unknown
03:12
created

EmailHandler   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 69
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 16
eloc 39
c 3
b 0
f 0
dl 0
loc 69
ccs 32
cts 32
cp 1
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
C validate() 0 67 16
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 93
    public function validate(mixed $value, object $rule, ValidationContext $context): Result
21
    {
22 93
        if (!$rule instanceof Email) {
23 1
            throw new UnexpectedRuleException(Email::class, $rule);
24
        }
25
26 92
        $originalValue = $value;
27 92
        $result = new Result();
28
29 92
        if (!is_string($value)) {
30 3
            $valid = false;
31 89
        } elseif (!preg_match(
32
            '/^(?P<name>(?:"?([^"]*)"?\s)?)(?:\s+)?((?P<open><?)((?P<local>.+)@(?P<domain>[^>]+))(?P<close>>?))/',
33
            $value,
34
            $matches
35
        )) {
36 7
            $valid = false;
37
        } else {
38
            /** @psalm-var array{name:string,local:string,open:string,domain:string,close:string} $matches */
39 82
            if ($rule->isEnableIDN()) {
40 28
                $matches['local'] = idn_to_ascii($matches['local']);
41 28
                $matches['domain'] = idn_to_ascii($matches['domain']);
42 28
                $value = implode([
43 28
                    $matches['name'],
44 28
                    $matches['open'],
45 28
                    $matches['local'],
46
                    '@',
47 28
                    $matches['domain'],
48 28
                    $matches['close'],
49
                ]);
50
            }
51
52 82
            if (is_string($matches['local']) && strlen($matches['local']) > 64) {
53
                // The maximum total length of a user name or other local-part is 64 octets. RFC 5322 section 4.5.3.1.1
54
                // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
55 2
                $valid = false;
56
            } elseif (
57 80
                is_string($matches['local']) &&
58 80
                strlen($matches['local']) + strlen((string) $matches['domain']) > 253
59
            ) {
60
                // There is a restriction in RFC 2821 on the length of an address in MAIL and RCPT commands
61
                // of 254 characters. Since addresses that do not fit in those fields are not normally useful, the
62
                // upper limit on address lengths should normally be considered to be 254.
63
                //
64
                // Dominic Sayers, RFC 3696 erratum 1690
65
                // http://www.rfc-editor.org/errata_search.php?eid=1690
66 1
                $valid = false;
67
            } else {
68 79
                $valid = preg_match($rule->getPattern(), $value) || ($rule->isAllowName() && preg_match(
69 48
                    $rule->getFullPattern(),
70
                    $value
71
                ));
72 79
                if ($valid && $rule->isCheckDNS()) {
73 6
                    $valid = checkdnsrr($matches['domain']);
74
                }
75
            }
76
        }
77
78 92
        if ($valid === false && $rule->isEnableIDN()) {
79 10
            $valid = (bool) preg_match($rule->getIdnEmailPattern(), $originalValue);
80
        }
81
82 92
        if ($valid === false) {
83 49
            $result->addError($rule->getMessage());
84
        }
85
86 92
        return $result;
87
    }
88
}
89