Passed
Push — master ( 2f38a4...cfa3d4 )
by Eduardo Gulias
02:09
created

src/Parser/Parser.php (2 issues)

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 array
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
    public function __construct(EmailLexer $lexer)
40
    {
41
        $this->lexer = $lexer;
42
    }
43
44
    /**
45
     * @return \Egulias\EmailValidator\Warning\Warning[]
46
     */
47
    public function getWarnings()
48
    {
49
        return $this->warnings;
50
    }
51
52
    /**
53
     * @param string $str
54
     */
55
    abstract public function parse($str);
56
57
    /** @return int */
58
    public function getOpenedParenthesis()
59
    {
60
        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
    protected function parseComments()
78
    {
79
        $this->openedParenthesis = 1;
80
        $this->isUnclosedComment();
81
        $this->warnings[Comment::CODE] = new Comment();
82
        while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
83
            if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
84
                $this->openedParenthesis++;
85
            }
86
            $this->warnEscaping();
87
            $this->lexer->moveNext();
88
        }
89
90
        $this->lexer->moveNext();
91
        if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) {
92
            throw new ExpectingATEXT();
93
        }
94
95
        if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
96
            $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
97
        }
98
    }
99
100
    /**
101
     * @return bool
102
     */
103
    protected function isUnclosedComment()
104
    {
105
        try {
106
            $this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
107
            return true;
108
        } catch (\RuntimeException $e) {
109
            throw new UnclosedComment();
110
        }
111
    }
112
113
    protected function parseFWS()
114
    {
115
        $previous = $this->lexer->getPrevious();
116
117
        $this->checkCRLFInFWS();
118
119
        if ($this->lexer->token['type'] === EmailLexer::S_CR) {
120
            throw new CRNoLF();
121
        }
122
123
        if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type']  !== EmailLexer::S_AT) {
124
            throw new AtextAfterCFWS();
125
        }
126
127 View Code Duplication
        if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
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...
128
            throw new ExpectingCTEXT();
129
        }
130
131
        if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type']  === EmailLexer::S_AT) {
132
            $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
133
        } else {
134
            $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
135
        }
136
    }
137
138
    protected function checkConsecutiveDots()
139
    {
140 View Code Duplication
        if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
141
            throw new ConsecutiveDot();
142
        }
143
    }
144
145
    /**
146
     * @return bool
147
     */
148
    protected function isFWS()
149
    {
150
        if ($this->escaped()) {
151
            return false;
152
        }
153
154
        if ($this->lexer->token['type'] === EmailLexer::S_SP ||
155
            $this->lexer->token['type'] === EmailLexer::S_HTAB ||
156
            $this->lexer->token['type'] === EmailLexer::S_CR ||
157
            $this->lexer->token['type'] === EmailLexer::S_LF ||
158
            $this->lexer->token['type'] === EmailLexer::CRLF
159
        ) {
160
            return true;
161
        }
162
163
        return false;
164
    }
165
166
    /**
167
     * @return bool
168
     */
169
    protected function escaped()
170
    {
171
        $previous = $this->lexer->getPrevious();
172
173
        if ($previous && $previous['type'] === EmailLexer::S_BACKSLASH
174
            &&
175
            $this->lexer->token['type'] !== EmailLexer::GENERIC
176
        ) {
177
            return true;
178
        }
179
180
        return false;
181
    }
182
183
    /**
184
     * @return bool
185
     */
186
    protected function warnEscaping()
187
    {
188
        if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
189
            return false;
190
        }
191
192
        if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
193
            throw new ExpectingATEXT();
194
        }
195
196
        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
197
            return false;
198
        }
199
200
        $this->warnings[QuotedPart::CODE] =
201
            new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
202
        return true;
203
204
    }
205
206
    /**
207
     * @param bool $hasClosingQuote
208
     *
209
     * @return bool
210
     */
211
    protected function checkDQUOTE($hasClosingQuote)
212
    {
213
        if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) {
214
            return $hasClosingQuote;
215
        }
216
        if ($hasClosingQuote) {
217
            return $hasClosingQuote;
218
        }
219
        $previous = $this->lexer->getPrevious();
220 View Code Duplication
        if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
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...
221
            throw new ExpectingATEXT();
222
        }
223
224
        try {
225
            $this->lexer->find(EmailLexer::S_DQUOTE);
226
            $hasClosingQuote = true;
227
        } catch (\Exception $e) {
228
            throw new UnclosedQuotedString();
229
        }
230
        $this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']);
231
232
        return $hasClosingQuote;
233
    }
234
235
    protected function checkCRLFInFWS()
236
    {
237
        if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
238
            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