Test Setup Failed
Push — 3.0.0-dev ( 729ecb...a7a691 )
by Eduardo Gulias
02:10
created

DomainPart::checkLabelLength()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 4
nc 2
nop 1
1
<?php
2
3
namespace Egulias\EmailValidator\Parser;
4
5
use Egulias\EmailValidator\EmailLexer;
6
use Egulias\EmailValidator\Result\InvalidEmail;
7
use Egulias\EmailValidator\Result\Reason\CharNotAllowed;
8
use Egulias\EmailValidator\Result\Reason\DomainHyphened;
9
use Egulias\EmailValidator\Result\Reason\DotAtEnd;
10
use Egulias\EmailValidator\Result\Reason\DotAtStart;
11
use Egulias\EmailValidator\Result\Reason\NoDomainPart;
12
use Egulias\EmailValidator\Result\Reason\ConsecutiveAt;
13
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
14
use Egulias\EmailValidator\Result\Reason\ExpectingDomainLiteralClose;
15
use Egulias\EmailValidator\Result\Result;
16
use Egulias\EmailValidator\Result\ValidEmail;
17
use Egulias\EmailValidator\Warning\DeprecatedComment;
18
use Egulias\EmailValidator\Warning\TLD;
19
use Egulias\EmailValidator\Parser\DomainLiteral as DomainLiteralParser;
20
use Egulias\EmailValidator\Result\Reason\CRLFAtTheEnd;
21
use Egulias\EmailValidator\Result\Reason\DomainTooLong;
22
use Egulias\EmailValidator\Result\Reason\LabelTooLong;
23
24
class DomainPart extends Parser
25
{
26
    const DOMAIN_MAX_LENGTH = 253;
27
    const LABEL_MAX_LENGTH = 63;
28
29
30
    /**
31
     * @var string
32
     */
33
    protected $domainPart = '';
34
35
    public function parse() : Result
36
    {
37
        $this->lexer->moveNext();
38
39
        $domainChecks = $this->performDomainStartChecks();
40
        if ($domainChecks->isInvalid()) {
41
            return $domainChecks;
42
        }
43
44
        if ($this->lexer->token['type'] === EmailLexer::S_AT) {
45
            return new InvalidEmail(new ConsecutiveAt(), $this->lexer->token['value']);
46
        }
47
        $domain = $this->doParseDomainPart();
48
        if ($domain->isInvalid()) {
49
            return $domain;
50
        }
51
52
        $length = strlen($this->domainPart);
53
54
        $end = $this->checkEndOfDomain();
55
        if ($end->isInvalid()) {
56
            return $end;
57
        }
58
59
        if ($length > self::DOMAIN_MAX_LENGTH) {
60
            //$this->warnings[DomainTooLong::CODE] = new DomainTooLong();
61
            return new InvalidEmail(new DomainTooLong(), $this->lexer->token['value']);
62
        }
63
64
        return new ValidEmail();
65
    }
66
67
    private function checkEndOfDomain() : Result
68
    {
69
        $prev = $this->lexer->getPrevious();
70
        if ($prev['type'] === EmailLexer::S_DOT) {
71
            return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
72
        }
73
        if ($prev['type'] === EmailLexer::S_HYPHEN) {
74
            return new InvalidEmail(new DomainHyphened('Hypen found at the end of the domain'), $prev['value']);
75
        }
76
77
        if ($this->lexer->token['type'] === EmailLexer::S_SP) {
78
            return new InvalidEmail(new CRLFAtTheEnd(), $prev['value']);
79
        }
80
        return new ValidEmail();
81
82
    }
83
84
    private function performDomainStartChecks() : Result
85
    {
86
        $invalidTokens = $this->checkInvalidTokensAfterAT();
87
        if ($invalidTokens->isInvalid()) {
88
            return $invalidTokens;
89
        }
90
        
91
        $missingDomain = $this->checkEmptyDomain();
92
        if ($missingDomain->isInvalid()) {
93
            return $missingDomain;
94
        }
95
96
        if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
97
            $this->warnings[DeprecatedComment::CODE] = new DeprecatedComment();
98
        }
99
        return new ValidEmail();
100
    }
101
102
    private function checkEmptyDomain() : Result
103
    {
104
        $thereIsNoDomain = $this->lexer->token['type'] === EmailLexer::S_EMPTY ||
105
            ($this->lexer->token['type'] === EmailLexer::S_SP &&
106
            !$this->lexer->isNextToken(EmailLexer::GENERIC));
107
108
        if ($thereIsNoDomain) {
109
            return new InvalidEmail(new NoDomainPart(), $this->lexer->token['value']);
110
        }
111
112
        return new ValidEmail();
113
    }
114
115
    private function checkInvalidTokensAfterAT() : Result
116
    {
117
        if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
118
            return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
119
        }
120
        if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
121
            return new InvalidEmail(new DomainHyphened('After AT'), $this->lexer->token['value']);
122
        }
123
        return new ValidEmail();
124
    }
125
126
    /**
127
     * @return string
128
     */
129
    public function getDomainPart()
130
    {
131
        return $this->domainPart;
132
    }
133
134 View Code Duplication
    protected function parseComments(): Result
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
135
    {
136
        $commentParser = new Comment($this->lexer, new DomainComment());
137
        $result = $commentParser->parse();
138
        $this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
139
140
        return $result;
141
    }
142
143
    protected function doParseDomainPart() : Result
144
    {
145
        $tldMissing = true;
146
        $hasComments = false;
147
        $domain = '';
148
        do {
149
            $prev = $this->lexer->getPrevious();
150
151
            $notAllowedChars = $this->checkNotAllowedChars($this->lexer->token);
152
            if ($notAllowedChars->isInvalid()) {
153
                return $notAllowedChars;
154
            }
155
156 View Code Duplication
            if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS || 
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...
157
                $this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
158
                $hasComments = true;
159
                $commentsResult = $this->parseComments();
160
161
                //Invalid comment parsing
162
                if($commentsResult->isInvalid()) {
163
                    return $commentsResult;
164
                }
165
            }
166
167
            $dotsResult = $this->checkConsecutiveDots();
168
            if ($dotsResult->isInvalid()) {
169
                return $dotsResult;
170
            }
171
172
            if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET) {
173
                $literalResult = $this->parseDomainLiteral();
174
175
                $this->addTLDWarnings($tldMissing);
176
                //Invalid literal parsing
177
                //if($literalResult->isInvalid()) {
178
                //    return $literalResult;
179
                //}
180
                return $literalResult;
181
            }
182
183
            $labelCheck = $this->checkLabelLength($prev);
184
            if ($labelCheck->isInvalid()) {
185
                return $labelCheck;
186
            }
187
188
            $FwsResult = $this->parseFWS();
189
            if($FwsResult->isInvalid()) {
190
                return $FwsResult;
191
            }
192
193
            $domain .= $this->lexer->token['value'];
194
195 View Code Duplication
            if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
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...
196
                $tldMissing = false;
197
            }
198
199
            $exceptionsResult = $this->checkDomainPartExceptions($prev, $hasComments);
200
            if ($exceptionsResult->isInvalid()) {
201
                return $exceptionsResult;
202
            }
203
            $this->lexer->moveNext();
204
            //if ($this->lexer->token['type'] === EmailLexer::S_SP) {
205
            //    return new InvalidEmail(new CharNotAllowed(), $this->lexer->token['value']);
206
            //}
207
208
        } while (null !== $this->lexer->token['type']);
209
210
        $this->addTLDWarnings($tldMissing);
211
212
        $this->domainPart = $domain;
213
        return new ValidEmail();
214
    }
215
216
    private function checkNotAllowedChars(array $token) : Result
217
    {
218
        $notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true];
219
        if (isset($notAllowed[$token['type']])) {
220
            return new InvalidEmail(new CharNotAllowed(), $token['value']);
221
        }
222
        return new ValidEmail();
223
    }
224
225
    /**
226
     * @return Result
227
     */
228
    protected function parseDomainLiteral() : Result
229
    {
230
231
        try {
232
            $this->lexer->find(EmailLexer::S_CLOSEBRACKET);
233
        } catch (\RuntimeException $e) {
234
            return new InvalidEmail(new ExpectingDomainLiteralClose(), $this->lexer->token['value']);
235
        }
236
237
        $domainLiteralParser = new DomainLiteralParser($this->lexer);
238
        $result = $domainLiteralParser->parse();
239
        $this->warnings = array_merge($this->warnings, $domainLiteralParser->getWarnings());
240
        return $result;
241
    }
242
243
    /**
244
     * @return InvalidEmail|ValidEmail
245
     */
246
    protected function checkDomainPartExceptions(array $prev, bool $hasComments) : Result
247
    {
248
        $invalidDomainTokens = array(
0 ignored issues
show
Unused Code introduced by
$invalidDomainTokens is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
249
            EmailLexer::S_DQUOTE => true,
250
            EmailLexer::S_SQUOTE => true,
251
            EmailLexer::S_SEMICOLON => true,
252
            EmailLexer::S_GREATERTHAN => true,
253
            EmailLexer::S_LOWERTHAN => true,
254
        );
255
256
        $validDomainTokens = array(
257
            EmailLexer::GENERIC => true,
258
            EmailLexer::S_HYPHEN => true,
259
            EmailLexer::S_DOT => true,
260
        );
261
262
        if ($hasComments) {
263
            $validDomainTokens[EmailLexer::S_OPENPARENTHESIS] = true;
264
            $validDomainTokens[EmailLexer::S_CLOSEPARENTHESIS] = true;
265
        }
266
267
        if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) {
268
            return new InvalidEmail(new ExpectingATEXT('OPENBRACKET not after AT'), $this->lexer->token['value']);
269
        }
270
271
        if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
272
            return new InvalidEmail(new DomainHyphened('Hypen found near DOT'), $this->lexer->token['value']);
273
        }
274
275
        if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
276
            && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
277
            return new InvalidEmail(new ExpectingATEXT('Escaping following "ATOM"'), $this->lexer->token['value']);
278
        }
279
280 View Code Duplication
        if (!isset($validDomainTokens[$this->lexer->token['type']])) {
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...
281
            return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
282
        }
283
284
        return new ValidEmail();
285
    }
286
287
    protected function checkLabelLength(array $prev) : Result
288
    {
289
        if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
290
            $prev['type'] === EmailLexer::GENERIC &&
291
            strlen($prev['value']) > self::LABEL_MAX_LENGTH
292
        ) {
293
            //$this->warnings[LabelTooLong::CODE] = new LabelTooLong();
294
           return new InvalidEmail(new LabelTooLong(), $this->lexer->token['value']);
295
        }
296
        return new ValidEmail();
297
    }
298
299
    private function addTLDWarnings(bool $isTLDMissing) : void
300
    {
301
        if ($isTLDMissing) {
302
            $this->warnings[TLD::CODE] = new TLD();
303
        }
304
    }
305
}