Passed
Push — master ( c4b8d1...e834ee )
by Eduardo Gulias
02:09
created

EmailValidator/Parser/Parser.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Egulias\EmailValidator\Parser;
4
5
use Egulias\EmailValidator\EmailLexer;
6
use Egulias\EmailValidator\Exception\AtextAfterCFWS;
7
use Egulias\EmailValidator\Exception\ConsecutiveDot;
8
use Egulias\EmailValidator\Exception\CRLFAtTheEnd;
9
use Egulias\EmailValidator\Exception\CRLFX2;
10
use Egulias\EmailValidator\Exception\CRNoLF;
11
use Egulias\EmailValidator\Exception\ExpectingQPair;
12
use Egulias\EmailValidator\Exception\ExpectingATEXT;
13
use Egulias\EmailValidator\Exception\ExpectingCTEXT;
14
use Egulias\EmailValidator\Exception\UnclosedComment;
15
use Egulias\EmailValidator\Exception\UnclosedQuotedString;
16
use Egulias\EmailValidator\Warning\CFWSNearAt;
17
use Egulias\EmailValidator\Warning\CFWSWithFWS;
18
use Egulias\EmailValidator\Warning\Comment;
19
use Egulias\EmailValidator\Warning\QuotedPart;
20
use Egulias\EmailValidator\Warning\QuotedString;
21
22
abstract class Parser
23
{
24
    /**
25
     * @var \Egulias\EmailValidator\Warning\Warning[]
26
     */
27
    protected $warnings = [];
28
29
    /**
30
     * @var EmailLexer
31
     */
32
    protected $lexer;
33
34
    /**
35
     * @var int
36
     */
37
    protected $openedParenthesis = 0;
38
39 130
    public function __construct(EmailLexer $lexer)
40
    {
41 130
        $this->lexer = $lexer;
42 130
    }
43
44
    /**
45
     * @return \Egulias\EmailValidator\Warning\Warning[]
46
     */
47 52
    public function getWarnings()
48
    {
49 52
        return $this->warnings;
50
    }
51
52
    /**
53
     * @param string $str
54
     */
55
    abstract public function parse($str);
56
57
    /** @return int */
58 6
    public function getOpenedParenthesis()
59
    {
60 6
        return $this->openedParenthesis;
61
    }
62
63
    /**
64
     * validateQuotedPair
65
     */
66
    protected function validateQuotedPair()
67
    {
68 View Code Duplication
        if (!($this->lexer->token['type'] === EmailLexer::INVALID
69
            || $this->lexer->token['type'] === EmailLexer::C_DEL)) {
70
            throw new ExpectingQPair();
71
        }
72
73
        $this->warnings[QuotedPart::CODE] =
74
            new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
75
    }
76
77 10
    protected function parseComments()
78
    {
79 10
        $this->openedParenthesis = 1;
80 10
        $this->isUnclosedComment();
81 6
        $this->warnings[Comment::CODE] = new Comment();
82 6
        while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
83 6
            if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
84 1
                $this->openedParenthesis++;
85 1
            }
86 6
            $this->warnEscaping();
87 6
            $this->lexer->moveNext();
88 6
        }
89
90 6
        $this->lexer->moveNext();
91 6
        if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) {
92
            throw new ExpectingATEXT();
93
        }
94
95 6
        if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
96 1
            $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
97 1
        }
98 6
    }
99
100
    /**
101
     * @return bool
102
     */
103 13
    protected function isUnclosedComment()
104
    {
105
        try {
106 13
            $this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
107 7
            return true;
108 6
        } catch (\RuntimeException $e) {
109 6
            throw new UnclosedComment();
110
        }
111
    }
112
113 16
    protected function parseFWS()
114
    {
115 16
        $previous = $this->lexer->getPrevious();
116
117 16
        $this->checkCRLFInFWS();
118
119 16
        if ($this->lexer->token['type'] === EmailLexer::S_CR) {
120 2
            throw new CRNoLF();
121
        }
122
123 14 View Code Duplication
        if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type']  !== EmailLexer::S_AT) {
0 ignored issues
show
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...
124 7
            throw new AtextAfterCFWS();
125
        }
126
127 10 View Code Duplication
        if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
128
            throw new ExpectingCTEXT();
129
        }
130
131 10
        if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type']  === EmailLexer::S_AT) {
132 3
            $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
133 3
        } else {
134 8
            $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
135
        }
136 10
    }
137
138 115
    protected function checkConsecutiveDots()
139
    {
140 115 View Code Duplication
        if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
141 3
            throw new ConsecutiveDot();
142
        }
143 115
    }
144
145
    /**
146
     * @return bool
147
     */
148 109
    protected function isFWS()
149
    {
150 109
        if ($this->escaped()) {
151 1
            return false;
152
        }
153
154 109
        if ($this->lexer->token['type'] === EmailLexer::S_SP ||
155 109
            $this->lexer->token['type'] === EmailLexer::S_HTAB ||
156 109
            $this->lexer->token['type'] === EmailLexer::S_CR ||
157 109
            $this->lexer->token['type'] === EmailLexer::S_LF ||
158 109
            $this->lexer->token['type'] === EmailLexer::CRLF
159 109
        ) {
160 16
            return true;
161
        }
162
163 109
        return false;
164
    }
165
166
    /**
167
     * @return bool
168
     */
169 116
    protected function escaped()
170
    {
171 116
        $previous = $this->lexer->getPrevious();
172
173 116
        if ($previous['type'] === EmailLexer::S_BACKSLASH
174 116
            &&
175 3
            $this->lexer->token['type'] !== EmailLexer::GENERIC
176 116
        ) {
177 2
            return true;
178
        }
179
180 116
        return false;
181
    }
182
183
    /**
184
     * @return bool
185
     */
186 115
    protected function warnEscaping()
187
    {
188 115
        if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
189 110
            return false;
190
        }
191
192 7
        if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
193 6
            throw new ExpectingATEXT();
194
        }
195
196 1
        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
197
            return false;
198
        }
199
200 1
        $this->warnings[QuotedPart::CODE] =
201 1
            new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
202 1
        return true;
203
204
    }
205
206
    /**
207
     * @param bool $hasClosingQuote
208
     *
209
     * @return bool
210
     */
211 127
    protected function checkDQUOTE($hasClosingQuote)
212
    {
213 127
        if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) {
214 106
            return $hasClosingQuote;
215
        }
216 22
        if ($hasClosingQuote) {
217
            return $hasClosingQuote;
218
        }
219 22
        $previous = $this->lexer->getPrevious();
220 22 View Code Duplication
        if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
221 1
            throw new ExpectingATEXT();
222
        }
223
224
        try {
225 21
            $this->lexer->find(EmailLexer::S_DQUOTE);
226 20
            $hasClosingQuote = true;
227 21
        } catch (\Exception $e) {
228 1
            throw new UnclosedQuotedString();
229
        }
230 20
        $this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']);
231
232 20
        return $hasClosingQuote;
233
    }
234
235 16
    protected function checkCRLFInFWS()
236
    {
237 16
        if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
238 16
            return;
239
        }
240
241
        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
242
            throw new CRLFX2();
243
        }
244
245
        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
246
            throw new CRLFAtTheEnd();
247
        }
248
    }
249
}
250