Test Setup Failed
Push — 3.0.0-dev ( e272a2...73dc53 )
by Eduardo Gulias
01:50
created

DomainPart::checkEndOfDomain()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 4
nc 4
nop 0
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\DomainLiteral;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Egulias\EmailValidator\Parser\DomainLiteral.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
19
use Egulias\EmailValidator\Warning\DomainTooLong;
20
use Egulias\EmailValidator\Warning\LabelTooLong;
21
use Egulias\EmailValidator\Warning\TLD;
22
use Egulias\EmailValidator\Parser\DomainLiteral as DomainLiteralParser;
23
use Egulias\EmailValidator\Result\Reason\CommaInDomain;
24
use Egulias\EmailValidator\Result\Reason\CRLFAtTheEnd;
25
26
class DomainPart extends Parser
27
{
28
    const DOMAIN_MAX_LENGTH = 254;
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
        $domain = $this->doParseDomainPart();
45
        if ($domain->isInvalid()) {
46
            return $domain;
47
        }
48
49
        $length = strlen($this->domainPart);
50
51
        $end = $this->checkEndOfDomain();
52
        if ($end->isInvalid()) {
53
            return $end;
54
        }
55
56
        if ($length > self::DOMAIN_MAX_LENGTH) {
57
            $this->warnings[DomainTooLong::CODE] = new DomainTooLong();
58
        }
59
60
        return new ValidEmail();
61
    }
62
63
    private function checkEndOfDomain() : Result
64
    {
65
        $prev = $this->lexer->getPrevious();
66
        if ($prev['type'] === EmailLexer::S_DOT) {
67
            return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
68
        }
69
        if ($prev['type'] === EmailLexer::S_HYPHEN) {
70
            return new InvalidEmail(new DomainHyphened('Hypen found at the end of the domain'), $prev['value']);
71
        }
72
73
        if ($this->lexer->token['type'] === EmailLexer::S_SP) {
74
            return new InvalidEmail(new CRLFAtTheEnd(), $prev['value']);
75
        }
76
        return new ValidEmail();
77
78
    }
79
80
    private function performDomainStartChecks() : Result
81
    {
82
        $invalidTokens = $this->checkInvalidTokensAfterAT();
83
        if ($invalidTokens->isInvalid()) {
84
            return $invalidTokens;
85
        }
86
        
87
        $missingDomain = $this->checkEmptyDomain();
88
        if ($missingDomain->isInvalid()) {
89
            return $missingDomain;
90
        }
91
92
        if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
93
            $this->warnings[DeprecatedComment::CODE] = new DeprecatedComment();
94
        }
95
        return new ValidEmail();
96
    }
97
98
    private function checkEmptyDomain() : Result
99
    {
100
        $thereIsNoDomain = $this->lexer->token['type'] === EmailLexer::S_EMPTY ||
101
            ($this->lexer->token['type'] === EmailLexer::S_SP &&
102
            !$this->lexer->isNextToken(EmailLexer::GENERIC));
103
104
        if ($thereIsNoDomain) {
105
            return new InvalidEmail(new NoDomainPart(), $this->lexer->token['value']);
106
        }
107
108
        return new ValidEmail();
109
    }
110
111
    private function checkInvalidTokensAfterAT() : Result
112
    {
113
        if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
114
            return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
115
        }
116
        if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
117
            return new InvalidEmail(new DomainHyphened('After AT'), $this->lexer->token['value']);
118
        }
119
        return new ValidEmail();
120
    }
121
122
    /**
123
     * @return string
124
     */
125
    public function getDomainPart()
126
    {
127
        return $this->domainPart;
128
    }
129
130 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...
131
    {
132
        $commentParser = new Comment($this->lexer, new DomainComment());
133
        $result = $commentParser->parse();
134
        $this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
135
136
        return $result;
137
    }
138
139
    protected function doParseDomainPart() : Result
140
    {
141
        $domain = '';
142
        do {
143
            $prev = $this->lexer->getPrevious();
144
145
            $notAllowedChars = $this->checkNotAllowedChars($this->lexer->token);
146
            if ($notAllowedChars->isInvalid()) {
147
                return $notAllowedChars;
148
            }
149
150 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...
151
                $this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
152
                $commentsResult = $this->parseComments();
153
154
                //Invalid comment parsing
155
                if($commentsResult->isInvalid()) {
156
                    return $commentsResult;
157
                }
158
            }
159
160
            $dotsResult = $this->checkConsecutiveDots();
161
            if ($dotsResult->isInvalid()) {
162
                return $dotsResult;
163
            }
164
            $exceptionsResult = $this->checkDomainPartExceptions($prev);
165
            if ($exceptionsResult->isInvalid()) {
166
                return $exceptionsResult;
167
            }
168
169 View Code Duplication
            if ($this->lexer->token['type'] === 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...
170
                $literalResult = $this->parseDomainLiteral();
171
                //Invalid literal parsing
172
                if($literalResult->isInvalid()) {
173
                    return $literalResult;
174
                }
175
            }
176
177
            $this->checkLabelLength($prev);
178
179
            $FwsResult = $this->parseFWS();
180
            if($FwsResult->isInvalid()) {
181
                return $FwsResult;
182
            }
183
184
            $domain .= $this->lexer->token['value'];
185
            $this->lexer->moveNext();
186
            if ($this->lexer->token['type'] === EmailLexer::S_SP) {
187
                return new InvalidEmail(new CharNotAllowed(), $this->lexer->token['type']);
188
            }
189
190
        } while (null !== $this->lexer->token['type']);
191
192
        $this->domainPart = $domain;
193
        return new ValidEmail();
194
    }
195
196
    private function checkNotAllowedChars(array $token) : Result
197
    {
198
        $notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true];
199
        if (isset($notAllowed[$token['type']])) {
200
            return new InvalidEmail(new CharNotAllowed(), $token['value']);
201
        }
202
        return new ValidEmail();
203
    }
204
205
    /**
206
     * @return Result
207
     */
208
    protected function parseDomainLiteral() : Result
209
    {
210
211
        try {
212
            $this->lexer->find(EmailLexer::S_CLOSEBRACKET);
213
        } catch (\RuntimeException $e) {
214
            return new InvalidEmail(new ExpectingDomainLiteralClose(), $this->lexer->token['value']);
215
        }
216
217
        $domainLiteralParser = new DomainLiteralParser($this->lexer);
218
        $result = $domainLiteralParser->parse();
219
        $this->warnings = array_merge($this->warnings, $domainLiteralParser->getWarnings());
220
        return $result;
221
    }
222
223
    /**
224
     * @return InvalidEmail|ValidEmail
225
     */
226
    protected function checkDomainPartExceptions(array $prev)
227
    {
228
        $invalidDomainTokens = array(
229
            EmailLexer::S_DQUOTE => true,
230
            EmailLexer::S_SEMICOLON => true,
231
            EmailLexer::S_GREATERTHAN => true,
232
            EmailLexer::S_LOWERTHAN => true,
233
        );
234
235 View Code Duplication
        if (isset($invalidDomainTokens[$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...
236
            return new InvalidEmail(new ExpectingATEXT('Invalid token in domain'), $this->lexer->token['value']);
237
        }
238
239
        if ($this->lexer->token['type'] === EmailLexer::S_COMMA) {
240
            return new InvalidEmail(new CommaInDomain(), $this->lexer->token['value']);
241
        }
242
243
        if ($this->lexer->token['type'] === EmailLexer::S_AT) {
244
            return new InvalidEmail(new ConsecutiveAt(), $this->lexer->token['value']);
245
        }
246
247
        if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) {
248
            return new InvalidEmail(new ExpectingATEXT('OPENBRACKET not after AT'), $this->lexer->token['value']);
249
        }
250
251
        if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
252
            return new InvalidEmail(new DomainHyphened('Hypen found near DOT'), $this->lexer->token['value']);
253
        }
254
255
        if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
256
            && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
257
            return new InvalidEmail(new ExpectingATEXT('Escaping following "ATOM"'), $this->lexer->token['value']);
258
        }
259
260
        return new ValidEmail();
261
    }
262
263
    protected function checkLabelLength(array $prev)
264
    {
265
        if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
266
            $prev['type'] === EmailLexer::GENERIC &&
267
            strlen($prev['value']) > 63
268
        ) {
269
            $this->warnings[LabelTooLong::CODE] = new LabelTooLong();
270
        }
271
    }
272
273
    protected function addTLDWarnings()
274
    {
275
        if ($this->warnings[DomainLiteral::CODE]) {
276
            $this->warnings[TLD::CODE] = new TLD();
277
        }
278
    }
279
}