Completed
Push — 3.0.0-dev ( 34d49c...6abede )
by Eduardo Gulias
02:52 queued 01:22
created

DomainLiteral::parse()   C

Complexity

Conditions 11
Paths 52

Size

Total Lines 69

Duplication

Lines 9
Ratio 13.04 %

Importance

Changes 0
Metric Value
dl 9
loc 69
rs 6.5296
c 0
b 0
f 0
cc 11
nc 52
nop 1

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\Reason\CRNoLF;
6
use Egulias\EmailValidator\Result\Reason\ExpectingDTEXT;
7
use Egulias\EmailValidator\Result\ValidEmail;
8
use Egulias\EmailValidator\Result\InvalidEmail;
9
use Egulias\EmailValidator\Warning\DomainLiteral as WarningDomainLiteral;
10
use Egulias\EmailValidator\Warning\CFWSWithFWS;
11
use Egulias\EmailValidator\Warning\IPV6BadChar;
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
21
class DomainLiteral extends Parser
22
{
23
    public function parse($remove)
24
    {
25
        $this->addTagWarnings();
26
27
        $IPv6TAG = false;
28
        $addressLiteral = '';
29
30
        do {
31 View Code Duplication
            if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
32
                return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token['value']);
33
            }
34
35
            $this->addObsoleteWarnings();
36
37 View Code Duplication
            if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
38
                return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token['value']);
39
            }
40
41
            if ($this->lexer->isNextTokenAny(
42
                array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
43
            )) {
44
                $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
45
                $this->parseFWS();
46
            }
47
48 View Code Duplication
            if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
49
                return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']);
50
            }
51
52
            if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
53
                $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
54
                $addressLiteral .= $this->lexer->token['value'];
55
                $this->lexer->moveNext();
56
                $this->validateQuotedPair();
57
            }
58
            if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
59
                $IPv6TAG = true;
60
            }
61
62
            if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) {
63
                break;
64
            }
65
66
            $addressLiteral .= $this->lexer->token['value'];
67
68
        } while ($this->lexer->moveNext());
69
70
71
        //Encapsulate
72
        $addressLiteral = str_replace('[', '', $addressLiteral);
73
        $isAddressLiteralIPv4 = $this->checkIPV4Tag($addressLiteral);
74
75
        if (!$isAddressLiteralIPv4) {
76
            return new ValidEmail();
77
        } else {
78
            $addressLiteral = $this->convertIPv4ToIPv6($addressLiteral);
79
        }
80
81
        if (!$IPv6TAG) {
82
            $this->warnings[WarningDomainLiteral::CODE] = new WarningDomainLiteral();
83
            return new ValidEmail();
84
        }
85
86
        $this->warnings[AddressLiteral::CODE] = new AddressLiteral();
87
88
        $this->checkIPV6Tag($addressLiteral);
89
90
        return new ValidEmail();
91
    }
92
93
    /**
94
     * @param string $addressLiteral
95
     * @param int $maxGroups
96
     */
97
    public function checkIPV6Tag($addressLiteral, $maxGroups = 8)
98
    {
99
        $prev = $this->lexer->getPrevious();
100
        if ($prev['type'] === EmailLexer::S_COLON) {
101
            $this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd();
102
        }
103
104
        $IPv6       = substr($addressLiteral, 5);
105
        //Daniel Marschall's new IPv6 testing strategy
106
        $matchesIP  = explode(':', $IPv6);
107
        $groupCount = count($matchesIP);
108
        $colons     = strpos($IPv6, '::');
109
110
        if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
111
            $this->warnings[IPV6BadChar::CODE] = new IPV6BadChar();
112
        }
113
114
        if ($colons === false) {
115
            // We need exactly the right number of groups
116
            if ($groupCount !== $maxGroups) {
117
                $this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount();
118
            }
119
            return;
120
        }
121
122
        if ($colons !== strrpos($IPv6, '::')) {
123
            $this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon();
124
            return;
125
        }
126
127
        if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
128
            // RFC 4291 allows :: at the start or end of an address
129
            //with 7 other groups in addition
130
            ++$maxGroups;
131
        }
132
133
        if ($groupCount > $maxGroups) {
134
            $this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups();
135
        } elseif ($groupCount === $maxGroups) {
136
            $this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated();
137
        }
138
    }
139
    
140
    public function convertIPv4ToIPv6($addressLiteralIPv4) : string
141
    {
142
        $matchesIP  = array();
143
        $IPv4Match = preg_match(
144
            '/\\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]?)$/',
145
            $addressLiteralIPv4,
146
            $matchesIP);
147
148
        // Extract IPv4 part from the end of the address-literal (if there is one)
149
        if ($IPv4Match > 0) {
150
            $index = strrpos($addressLiteralIPv4, $matchesIP[0]);
151
            //There's a match but it is at the start
152
            if ($index > 0) {
153
                // Convert IPv4 part to IPv6 format for further testing
154
                return substr($addressLiteralIPv4, 0, (int) $index) . '0:0';
155
            }
156
        }
157
158
        return $addressLiteralIPv4;
159
    }
160
161
    /**
162
     * @param string $addressLiteral
163
     *
164
     * @return string
165
     */
166
    protected function checkIPV4Tag($addressLiteral) : bool
167
    {
168
        $matchesIP  = array();
169
        $IPv4Match = preg_match(
170
            '/\\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]?)$/',
171
            $addressLiteral,
172
            $matchesIP);
173
174
        // Extract IPv4 part from the end of the address-literal (if there is one)
175
176
        if ($IPv4Match > 0) {
177
            $index = strrpos($addressLiteral, $matchesIP[0]);
178
            //There's a match but it is at the start
179
            if ($index === 0) {
180
                $this->warnings[AddressLiteral::CODE] = new AddressLiteral();
181
                return false;
182
            }
183
        }
184
185
        return true;
186
    }
187
188
    private function addObsoleteWarnings()
189
    {
190
        if ($this->lexer->token['type'] === EmailLexer::INVALID ||
191
            $this->lexer->token['type'] === EmailLexer::C_DEL   ||
192
            $this->lexer->token['type'] === EmailLexer::S_LF
193
        ) {
194
            $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
195
        }
196
    }
197
198
    private function addTagWarnings()
199
    {
200
        if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
201
            $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
202
        }
203
        if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
204
            $lexer = clone $this->lexer;
205
            $lexer->moveNext();
206
            if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
207
                $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
208
            }
209
        }
210
    }
211
212
}