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