Test Failed
Pull Request — 3.x (#339)
by
unknown
01:52
created

DomainLiteral::parse()   B

Complexity

Conditions 11
Paths 30

Size

Total Lines 64
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 11.1806

Importance

Changes 0
Metric Value
cc 11
eloc 35
c 0
b 0
f 0
nc 30
nop 0
dl 0
loc 64
ccs 31
cts 35
cp 0.8857
crap 11.1806
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Egulias\EmailValidator\Parser;
4
5
use Egulias\EmailValidator\EmailLexer;
6
use Egulias\EmailValidator\Result\Result;
7
use Egulias\EmailValidator\Result\ValidEmail;
8
use Egulias\EmailValidator\Result\InvalidEmail;
9
use Egulias\EmailValidator\Warning\CFWSWithFWS;
10
use Egulias\EmailValidator\Warning\IPV6BadChar;
11
use Egulias\EmailValidator\Result\Reason\CRNoLF;
12
use Egulias\EmailValidator\Warning\IPV6ColonEnd;
13
use Egulias\EmailValidator\Warning\IPV6MaxGroups;
14
use Egulias\EmailValidator\Warning\ObsoleteDTEXT;
15
use Egulias\EmailValidator\Warning\AddressLiteral;
16
use Egulias\EmailValidator\Warning\IPV6ColonStart;
17
use Egulias\EmailValidator\Warning\IPV6Deprecated;
18
use Egulias\EmailValidator\Warning\IPV6GroupCount;
19
use Egulias\EmailValidator\Warning\IPV6DoubleColon;
20
use Egulias\EmailValidator\Result\Reason\ExpectingDTEXT;
21
use Egulias\EmailValidator\Result\Reason\UnusualElements;
22
use Egulias\EmailValidator\Warning\DomainLiteral as WarningDomainLiteral;
23
24
class DomainLiteral extends PartParser
25
{
26
    public const IPV4_REGEX = '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
27
28
    public const OBSOLETE_WARNINGS = [
29
        EmailLexer::INVALID,
30
        EmailLexer::C_DEL,
31
        EmailLexer::S_LF,
32
        EmailLexer::S_BACKSLASH
33
    ];
34
35 18
    public function parse(): Result
36
    {
37 18
        $this->addTagWarnings();
38
39 18
        $IPv6TAG = false;
40 18
        $addressLiteral = '';
41
42
        do {
43 18
            if ($this->lexer->token->isA(EmailLexer::C_NUL)) {
0 ignored issues
show
Bug introduced by
Egulias\EmailValidator\EmailLexer::C_NUL of type integer is incompatible with the type Doctrine\Common\Lexer\T expected by parameter $types of Doctrine\Common\Lexer\Token::isA(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

43
            if ($this->lexer->token->isA(/** @scrutinizer ignore-type */ EmailLexer::C_NUL)) {
Loading history...
44
                return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token->value);
45
            }
46
47 18
            $this->addObsoleteWarnings();
48
49 18
            if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENBRACKET, EmailLexer::S_OPENBRACKET))) {
50 1
                return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token->value);
51
            }
52
53 17
            if ($this->lexer->isNextTokenAny(
54 17
                array(EmailLexer::S_HTAB, EmailLexer::S_SP, EmailLexer::CRLF)
55 17
            )) {
56
                $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
57
                $this->parseFWS();
58
            }
59
60 17
            if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
0 ignored issues
show
Bug introduced by
Egulias\EmailValidator\EmailLexer::S_CR of type integer is incompatible with the type Doctrine\Common\Lexer\T expected by parameter $type of Doctrine\Common\Lexer\AbstractLexer::isNextToken(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

60
            if ($this->lexer->isNextToken(/** @scrutinizer ignore-type */ EmailLexer::S_CR)) {
Loading history...
61 1
                return new InvalidEmail(new CRNoLF(), $this->lexer->token->value);
62
            }
63
64 16
            if ($this->lexer->token->isA(EmailLexer::S_BACKSLASH)) {
65 1
                return new InvalidEmail(new UnusualElements($this->lexer->token->value), $this->lexer->token->value);
66
            }
67 16
            if ($this->lexer->token->isA(EmailLexer::S_IPV6TAG)) {
68 9
                $IPv6TAG = true;
69
            }
70
71 16
            if ($this->lexer->token->isA(EmailLexer::S_CLOSEBRACKET)) {
72
                break;
73
            }
74
75 16
            $addressLiteral .= $this->lexer->token->value;
76 16
        } while ($this->lexer->moveNext());
77
78
79
        //Encapsulate
80 15
        $addressLiteral = str_replace('[', '', $addressLiteral);
81 15
        $isAddressLiteralIPv4 = $this->checkIPV4Tag($addressLiteral);
82
83 15
        if (!$isAddressLiteralIPv4) {
84 3
            return new ValidEmail();
85
        } else {
86 12
            $addressLiteral = $this->convertIPv4ToIPv6($addressLiteral);
87
        }
88
89 12
        if (!$IPv6TAG) {
90 3
            $this->warnings[WarningDomainLiteral::CODE] = new WarningDomainLiteral();
91 3
            return new ValidEmail();
92
        }
93
94 9
        $this->warnings[AddressLiteral::CODE] = new AddressLiteral();
95
96 9
        $this->checkIPV6Tag($addressLiteral);
97
98 9
        return new ValidEmail();
99
    }
100
101
    /**
102
     * @param string $addressLiteral
103
     * @param int $maxGroups
104
     */
105 9
    public function checkIPV6Tag($addressLiteral, $maxGroups = 8): void
106
    {
107 9
        $prev = $this->lexer->getPrevious();
108 9
        if ($prev->isA(EmailLexer::S_COLON)) {
0 ignored issues
show
Bug introduced by
Egulias\EmailValidator\EmailLexer::S_COLON of type integer is incompatible with the type Doctrine\Common\Lexer\T expected by parameter $types of Doctrine\Common\Lexer\Token::isA(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
        if ($prev->isA(/** @scrutinizer ignore-type */ EmailLexer::S_COLON)) {
Loading history...
109 1
            $this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd();
110
        }
111
112 9
        $IPv6       = substr($addressLiteral, 5);
113
        //Daniel Marschall's new IPv6 testing strategy
114 9
        $matchesIP  = explode(':', $IPv6);
115 9
        $groupCount = count($matchesIP);
116 9
        $colons     = strpos($IPv6, '::');
117
118 9
        if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
119 1
            $this->warnings[IPV6BadChar::CODE] = new IPV6BadChar();
120
        }
121
122 9
        if ($colons === false) {
123
            // We need exactly the right number of groups
124 4
            if ($groupCount !== $maxGroups) {
125 1
                $this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount();
126
            }
127 4
            return;
128
        }
129
130 5
        if ($colons !== strrpos($IPv6, '::')) {
131 1
            $this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon();
132 1
            return;
133
        }
134
135 4
        if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
136
            // RFC 4291 allows :: at the start or end of an address
137
            //with 7 other groups in addition
138 2
            ++$maxGroups;
139
        }
140
141 4
        if ($groupCount > $maxGroups) {
142 1
            $this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups();
143 3
        } elseif ($groupCount === $maxGroups) {
144 1
            $this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated();
145
        }
146
    }
147
148 12
    public function convertIPv4ToIPv6(string $addressLiteralIPv4): string
149
    {
150 12
        $matchesIP  = [];
151 12
        $IPv4Match = preg_match(self::IPV4_REGEX, $addressLiteralIPv4, $matchesIP);
152
153
        // Extract IPv4 part from the end of the address-literal (if there is one)
154 12
        if ($IPv4Match > 0) {
155 1
            $index = (int) strrpos($addressLiteralIPv4, $matchesIP[0]);
156
            //There's a match but it is at the start
157 1
            if ($index > 0) {
158
                // Convert IPv4 part to IPv6 format for further testing
159 1
                return substr($addressLiteralIPv4, 0, $index) . '0:0';
160
            }
161
        }
162
163 11
        return $addressLiteralIPv4;
164
    }
165
166
    /**
167
     * @param string $addressLiteral
168
     *
169
     * @return bool
170
     */
171 15
    protected function checkIPV4Tag($addressLiteral): bool
172
    {
173 15
        $matchesIP  = [];
174 15
        $IPv4Match = preg_match(self::IPV4_REGEX, $addressLiteral, $matchesIP);
175
176
        // Extract IPv4 part from the end of the address-literal (if there is one)
177
178 15
        if ($IPv4Match > 0) {
179 4
            $index = strrpos($addressLiteral, $matchesIP[0]);
180
            //There's a match but it is at the start
181 4
            if ($index === 0) {
182 3
                $this->warnings[AddressLiteral::CODE] = new AddressLiteral();
183 3
                return false;
184
            }
185
        }
186
187 12
        return true;
188
    }
189
190 18
    private function addObsoleteWarnings(): void
191
    {
192 18
        if (in_array($this->lexer->token->type, self::OBSOLETE_WARNINGS)) {
193 2
            $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
194
        }
195
    }
196
197 18
    private function addTagWarnings(): void
198
    {
199 18
        if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
0 ignored issues
show
Bug introduced by
Egulias\EmailValidator\EmailLexer::S_COLON of type integer is incompatible with the type Doctrine\Common\Lexer\T expected by parameter $type of Doctrine\Common\Lexer\AbstractLexer::isNextToken(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

199
        if ($this->lexer->isNextToken(/** @scrutinizer ignore-type */ EmailLexer::S_COLON)) {
Loading history...
200
            $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
201
        }
202 18
        if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
203 9
            $lexer = clone $this->lexer;
204 9
            $lexer->moveNext();
205 9
            if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
206 1
                $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
207
            }
208
        }
209
    }
210
}
211