Passed
Pull Request — 3.x (#348)
by
unknown
01:48
created

DomainLiteral::parse()   B

Complexity

Conditions 11
Paths 30

Size

Total Lines 65
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
nc 30
nop 0
dl 0
loc 65
ccs 31
cts 35
cp 0.8857
crap 11.1806
rs 7.3166
c 0
b 0
f 0

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
namespace Egulias\EmailValidator\Parser;
3
4
use Egulias\EmailValidator\EmailLexer;
5
use Egulias\EmailValidator\Result\Result;
6
use Egulias\EmailValidator\Result\ValidEmail;
7
use Egulias\EmailValidator\Result\InvalidEmail;
8
use Egulias\EmailValidator\Warning\CFWSWithFWS;
9
use Egulias\EmailValidator\Warning\IPV6BadChar;
10
use Egulias\EmailValidator\Result\Reason\CRNoLF;
11
use Egulias\EmailValidator\Warning\IPV6ColonEnd;
12
use Egulias\EmailValidator\Warning\IPV6MaxGroups;
13
use Egulias\EmailValidator\Warning\ObsoleteDTEXT;
14
use Egulias\EmailValidator\Warning\AddressLiteral;
15
use Egulias\EmailValidator\Warning\IPV6ColonStart;
16
use Egulias\EmailValidator\Warning\IPV6Deprecated;
17
use Egulias\EmailValidator\Warning\IPV6GroupCount;
18
use Egulias\EmailValidator\Warning\IPV6DoubleColon;
19
use Egulias\EmailValidator\Result\Reason\ExpectingDTEXT;
20
use Egulias\EmailValidator\Result\Reason\UnusualElements;
21
use Egulias\EmailValidator\Warning\DomainLiteral as WarningDomainLiteral;
22
23
class DomainLiteral extends PartParser
24
{
25
    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]?)$/';
26
27
    public const OBSOLETE_WARNINGS = [
28
        EmailLexer::INVALID,
29
        EmailLexer::C_DEL,
30
        EmailLexer::S_LF,
31
        EmailLexer::S_BACKSLASH
32
    ];
33
34 18
    public function parse() : Result
35
    {
36 18
        $this->addTagWarnings();
37
38 18
        $IPv6TAG = false;
39 18
        $addressLiteral = '';
40
41
        do {
42 18
            if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
43
                return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token['value']);
44
            }
45
46 18
            $this->addObsoleteWarnings();
47
48 18
            if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENBRACKET, EmailLexer::S_OPENBRACKET))) {
49 1
                return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token['value']);
50
            }
51
52 17
            if ($this->lexer->isNextTokenAny(
53 17
                array(EmailLexer::S_HTAB, EmailLexer::S_SP, EmailLexer::CRLF)
54 17
            )) {
55
                $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
56
                $this->parseFWS();
57
            }
58
59 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

59
            if ($this->lexer->isNextToken(/** @scrutinizer ignore-type */ EmailLexer::S_CR)) {
Loading history...
60 1
                return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']);
61
            }
62
63 16
            if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
64 1
                return new InvalidEmail(new UnusualElements($this->lexer->token['value']), $this->lexer->token['value']);
65
            }
66 16
            if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
67 9
                $IPv6TAG = true;
68
            }
69
70 16
            if ($this->lexer->token['type'] === EmailLexer::S_CLOSEBRACKET) {
71
                break;
72
            }
73
74 16
            $addressLiteral .= $this->lexer->token['value'];
75
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['type'] === EmailLexer::S_COLON) {
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
}
212