Passed
Push — 3.x ( 5cc047...007bfd )
by Eduardo Gulias
03:20
created

LocalPart   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Test Coverage

Coverage 98.53%

Importance

Changes 0
Metric Value
eloc 71
dl 0
loc 148
ccs 67
cts 68
cp 0.9853
rs 9.92
c 0
b 0
f 0
wmc 31

8 Methods

Rating   Name   Duplication   Size   Complexity  
A parseDoubleQuote() 0 7 1
A parseLocalFWS() 0 8 2
A validateTokens() 0 6 2
A parseComments() 0 9 2
A localPart() 0 3 1
A validateEscaping() 0 16 4
C parse() 0 63 17
A hasDotAtStart() 0 3 2
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\LocalTooLong;
10
use Egulias\EmailValidator\Result\Reason\DotAtEnd;
11
use Egulias\EmailValidator\Result\Reason\DotAtStart;
12
use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
13
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
14
use Egulias\EmailValidator\Parser\CommentStrategy\LocalComment;
15
16
class LocalPart extends PartParser
17
{
18
    const INVALID_TOKENS = [
19
        EmailLexer::S_COMMA => EmailLexer::S_COMMA,
20
        EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
21
        EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
22
        EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
23
        EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
24
        EmailLexer::S_COLON => EmailLexer::S_COLON,
25
        EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
26
        EmailLexer::INVALID => EmailLexer::INVALID
27
    ];
28
29
    /**
30
     * @var string
31
     */
32
    private $localPart = '';
33
34
35 186
    public function parse() : Result
36
    {
37 186
        $this->lexer->startRecording();
38
39 186
        while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) {
40 186
            if ($this->hasDotAtStart()) {
41 2
                return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
42
            }
43
44 184
            if ($this->lexer->token['type'] === EmailLexer::S_DQUOTE) {
45 23
                $dquoteParsingResult = $this->parseDoubleQuote();
46
47
                //Invalid double quote parsing
48 23
                if($dquoteParsingResult->isInvalid()) {
49 10
                    return $dquoteParsingResult;
50
                }
51
            }
52
53 175
            if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS || 
54 175
                $this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
55 9
                $commentsResult = $this->parseComments();
56
57
                //Invalid comment parsing
58 9
                if($commentsResult->isInvalid()) {
59 6
                    return $commentsResult;
60
                }
61
            }
62
63 173
            if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
64 1
                return new InvalidEmail(new ConsecutiveDot(), $this->lexer->token['value']);
65
            }
66
67 173
            if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
68 173
                $this->lexer->isNextToken(EmailLexer::S_AT)
69
            ) {
70 2
                return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
71
            }
72
73 173
            $resultEscaping = $this->validateEscaping();
74 173
            if ($resultEscaping->isInvalid()) {
75 6
                return $resultEscaping;
76
            }
77
78 168
            $resultToken = $this->validateTokens(false);
79 168
            if ($resultToken->isInvalid()) {
80 4
                return $resultToken;
81
            }
82
83 167
            $resultFWS = $this->parseLocalFWS();
84 167
            if($resultFWS->isInvalid()) {
85 7
                return $resultFWS;
86
            }
87
88 166
            $this->lexer->moveNext();
89
        }
90
91 148
        $this->lexer->stopRecording();
92 148
        $this->localPart = rtrim($this->lexer->getAccumulatedValues(), '@');
93 148
        if (strlen($this->localPart) > LocalTooLong::LOCAL_PART_LENGTH) {
94 2
            $this->warnings[LocalTooLong::CODE] = new LocalTooLong();
95
        }
96
97 148
        return new ValidEmail();
98
    }
99
100 168
    protected function validateTokens(bool $hasComments) : Result
0 ignored issues
show
Unused Code introduced by
The parameter $hasComments is not used and could be removed. ( Ignorable by Annotation )

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

100
    protected function validateTokens(/** @scrutinizer ignore-unused */ bool $hasComments) : Result

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
101
    {
102 168
        if (isset(self::INVALID_TOKENS[$this->lexer->token['type']])) {
103 4
            return new InvalidEmail(new ExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
104
        }
105 167
        return new ValidEmail();
106
    }
107
108 186
    public function localPart() : string
109
    {
110 186
        return $this->localPart;
111
    }
112
113 167
    private function parseLocalFWS() : Result 
114
    {
115 167
        $foldingWS = new FoldingWhiteSpace($this->lexer);
116 167
        $resultFWS = $foldingWS->parse();
117 167
        if ($resultFWS->isValid()) {
118 166
            $this->warnings = array_merge($this->warnings, $foldingWS->getWarnings());
119
        }
120 167
        return $resultFWS;
121
    }
122
123 186
    private function hasDotAtStart() : bool
124
    {
125 186
            return $this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type'];
126
    }
127
128 23
    private function parseDoubleQuote() : Result
129
    {
130 23
        $dquoteParser = new DoubleQuote($this->lexer);
131 23
        $parseAgain = $dquoteParser->parse();
132 23
        $this->warnings = array_merge($this->warnings, $dquoteParser->getWarnings());
133
134 23
        return $parseAgain;
135
    }
136
137 8
    protected function parseComments(): Result
138
    {
139 8
        $commentParser = new Comment($this->lexer, new LocalComment());
140 8
        $result = $commentParser->parse();
141 8
        $this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
142 8
        if($result->isInvalid()) {
143 5
            return $result;
144
        }
145 3
        return $result;
146
    }
147
148 173
    private function validateEscaping() : Result
149
    {
150
        //Backslash found
151 173
        if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
152 168
            return new ValidEmail();
153
        }
154
155 7
        if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
156 6
            return new InvalidEmail(new ExpectingATEXT('Found ATOM after escaping'), $this->lexer->token['value']);
157
        }
158
159 1
        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
160
            return new ValidEmail();
161
        }
162
163 1
        return new ValidEmail();
164
    }
165
}