Test Failed
Pull Request — master (#175)
by
unknown
12:57
created

Email::__construct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 49
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 3
c 0
b 0
f 0
nc 2
nop 11
dl 0
loc 49
ccs 5
cts 5
cp 1
crap 3
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use RuntimeException;
8
use Yiisoft\Validator\FormatterInterface;
9
use Yiisoft\Validator\Result;
10
use Yiisoft\Validator\Rule;
11
use Yiisoft\Validator\ValidationContext;
12
13
use function function_exists;
14
use function is_string;
15
use function strlen;
16
17
/**
18
 * EmailValidator validates that the attribute value is a valid email address.
19
 */
20
final class Email extends Rule
21
{
22
    public function __construct(
23
        /**
24
         * @var string the regular expression used to validate value.
25
         *
26
         * @link http://www.regular-expressions.info/email.html
27
         */
28
        private string $pattern = '/^[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/',
29
        /**
30
         * @var string the regular expression used to validate email addresses with the name part. This property is used
31
         * only when {@see $allowName} is `true`.
32
         *
33
         * @see $allowName
34
         */
35
        private string $fullPattern = '/^[^@]*<[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?>$/',
36
        /**
37
         * @var string the regular expression used to validate complex emails when idn is enabled.
38
         */
39
        private string $patternIdnEmail = '/^([a-zA-Z0-9._%+-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/',
40
        /**
41
         * @var bool whether to allow name in the email address (e.g. "John Smith <[email protected]>"). Defaults
42
         * to `false`.
43
         *
44
         * @see $fullPattern
45
         */
46
        private bool $allowName = false,
47
        /**
48
         * @var bool whether to check whether the email's domain exists and has either an A or MX record.
49
         * Be aware that this check can fail due to temporary DNS problems even if the email address is
50
         * valid and an email would be deliverable. Defaults to `false`.
51
         */
52
        private bool $checkDNS = false,
53
        /**
54
         * @var bool whether validation process should take into account IDN (internationalized domain
55
         * names). Defaults to false meaning that validation of emails containing IDN will always fail.
56
         * Note that in order to use IDN validation you have to install and enable `intl` PHP extension,
57
         * otherwise an exception would be thrown.
58
         */
59
        private bool $enableIDN = false,
60
        private string $message = 'This value is not a valid email address.',
61
62 38
        ?FormatterInterface $formatter = null,
63
        bool $skipOnEmpty = false,
64 38
        bool $skipOnError = false,
65
        $when = null
66
    ) {
67 37
        parent::__construct(formatter: $formatter, skipOnEmpty: $skipOnEmpty, skipOnError: $skipOnError, when: $when);
68
69 37
        if ($enableIDN && !function_exists('idn_to_ascii')) {
70 37
            throw new RuntimeException('In order to use IDN validation intl extension must be installed and enabled.');
71
        }
72 37
    }
73 1
74 37
    protected function validateValue($value, ?ValidationContext $context = null): Result
75 37
    {
76
        $originalValue = $value;
77
        $result = new Result();
78
79 2
        if (!is_string($value)) {
80
            $valid = false;
81
        } elseif (!preg_match(
82 37
            '/^(?P<name>(?:"?([^"]*)"?\s)?)(?:\s+)?((?P<open><?)((?P<local>.+)@(?P<domain>[^>]+))(?P<close>>?))$/i',
83 35
            $value,
84 35
            $matches
85 35
        )) {
86
            $valid = false;
87
        } else {
88 37
            /** @psalm-var array{name:string,local:string,open:string,domain:string,close:string} $matches */
89
            if ($this->enableIDN) {
90
                $matches['local'] = $this->idnToAscii($matches['local']);
91 1
                $matches['domain'] = $this->idnToAscii($matches['domain']);
92 37
                $value = implode([
93
                    $matches['name'],
94
                    $matches['open'],
95
                    $matches['local'],
96
                    '@',
97
                    $matches['domain'],
98
                    $matches['close'],
99 1
                ]);
100
            }
101 37
102 37
            if (is_string($matches['local']) && strlen($matches['local']) > 64) {
103
                // The maximum total length of a user name or other local-part is 64 octets. RFC 5322 section 4.5.3.1.1
104
                // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
105 37
                $valid = false;
106 1
            } elseif (is_string($matches['local']) && strlen($matches['local'] . '@' . $matches['domain']) > 254) {
107
                // There is a restriction in RFC 2821 on the length of an address in MAIL and RCPT commands
108
                // of 254 characters. Since addresses that do not fit in those fields are not normally useful, the
109
                // upper limit on address lengths should normally be considered to be 254.
110
                //
111 37
                // Dominic Sayers, RFC 3696 erratum 1690
112 35
                // http://www.rfc-editor.org/errata_search.php?eid=1690
113
                $valid = false;
114
            } else {
115 37
                $valid = preg_match($this->pattern, $value) || ($this->allowName && preg_match(
116 37
                    $this->fullPattern,
117
                    $value
118
                ));
119 37
                if ($valid && $this->checkDNS) {
120
                    $valid = checkdnsrr($matches['domain'] . '.', 'MX') || checkdnsrr($matches['domain'] . '.', 'A');
121
                }
122 35
            }
123
        }
124 35
125
        if ($this->enableIDN && $valid === false) {
126
            $valid = (bool) preg_match($this->patternIdnEmail, $originalValue);
127
        }
128
129
        if ($valid === false) {
130
            $result->addError($this->formatMessage($this->message));
131
        }
132
133
        return $result;
134 3
    }
135
136 3
    private function idnToAscii($idn)
137 3
    {
138 3
        return idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
139
    }
140
141 1
    public function getOptions(): array
142
    {
143 1
        return array_merge(parent::getOptions(), [
144 1
            'allowName' => $this->allowName,
145 1
            'checkDNS' => $this->checkDNS,
146
            'enableIDN' => $this->enableIDN,
147
            'message' => $this->formatMessage($this->message),
148 35
        ]);
149
    }
150
}
151