Completed
Push — 3.0.0-dev ( ac3f65...1cfe87 )
by Eduardo Gulias
02:02
created

Parser::checkDQUOTE()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 25

Duplication

Lines 5
Ratio 20 %

Importance

Changes 0
Metric Value
dl 5
loc 25
rs 8.8977
c 0
b 0
f 0
cc 6
nc 5
nop 1
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\Warning\CFWSNearAt;
16
use Egulias\EmailValidator\Warning\CFWSWithFWS;
17
use Egulias\EmailValidator\Warning\Comment;
18
use Egulias\EmailValidator\Warning\QuotedPart;
19
20
abstract class Parser
21
{
22
    /**
23
     * @var \Egulias\EmailValidator\Warning\Warning[]
24
     */
25
    protected $warnings = [];
26
27
    /**
28
     * @var EmailLexer
29
     */
30
    protected $lexer;
31
32
    /**
33
     * @var int
34
     */
35
    protected $openedParenthesis = 0;
36
37
    public function __construct(EmailLexer $lexer)
38
    {
39
        $this->lexer = $lexer;
40
    }
41
42
    /**
43
     * @return \Egulias\EmailValidator\Warning\Warning[]
44
     */
45
    public function getWarnings()
46
    {
47
        return $this->warnings;
48
    }
49
50
    /**
51
     * @param string $str
52
     */
53
    abstract public function parse($str);
54
55
    /** @return int */
56
    public function getOpenedParenthesis()
57
    {
58
        return $this->openedParenthesis;
59
    }
60
61
    /**
62
     * validateQuotedPair
63
     */
64
    protected function validateQuotedPair()
65
    {
66
        if (!($this->lexer->token['type'] === EmailLexer::INVALID
67
            || $this->lexer->token['type'] === EmailLexer::C_DEL)) {
68
            throw new ExpectingQPair();
69
        }
70
71
        $this->warnings[QuotedPart::CODE] =
72
            new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
73
    }
74
75
    protected function parseComments()
76
    {
77
        $this->openedParenthesis = 1;
78
        $this->isUnclosedComment();
79
        $this->warnings[Comment::CODE] = new Comment();
80
        while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
81
            if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
82
                $this->openedParenthesis++;
83
            }
84
            $this->warnEscaping();
85
            $this->lexer->moveNext();
86
        }
87
88
        $this->lexer->moveNext();
89
        if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) {
90
            throw new ExpectingATEXT();
91
        }
92
93
        if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
94
            $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
95
        }
96
    }
97
98
    /**
99
     * @return bool
100
     */
101
    protected function isUnclosedComment()
102
    {
103
        try {
104
            $this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
105
            return true;
106
        } catch (\RuntimeException $e) {
107
            throw new UnclosedComment();
108
        }
109
    }
110
111
    protected function parseFWS()
112
    {
113
        $previous = $this->lexer->getPrevious();
114
115
        $this->checkCRLFInFWS();
116
117
        if ($this->lexer->token['type'] === EmailLexer::S_CR) {
118
            throw new CRNoLF();
119
        }
120
121
        if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type']  !== EmailLexer::S_AT) {
122
            throw new AtextAfterCFWS();
123
        }
124
125
        if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
126
            throw new ExpectingCTEXT();
127
        }
128
129
        if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type']  === EmailLexer::S_AT) {
130
            $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
131
        } else {
132
            $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
133
        }
134
    }
135
136
    protected function checkConsecutiveDots()
137
    {
138 View Code Duplication
        if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
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...
139
            throw new ConsecutiveDot();
140
        }
141
    }
142
143
    /**
144
     * @return bool
145
     */
146
    protected function isFWS()
147
    {
148
        if ($this->escaped()) {
149
            return false;
150
        }
151
152
        if ($this->lexer->token['type'] === EmailLexer::S_SP ||
153
            $this->lexer->token['type'] === EmailLexer::S_HTAB ||
154
            $this->lexer->token['type'] === EmailLexer::S_CR ||
155
            $this->lexer->token['type'] === EmailLexer::S_LF ||
156
            $this->lexer->token['type'] === EmailLexer::CRLF
157
        ) {
158
            return true;
159
        }
160
161
        return false;
162
    }
163
164
    /**
165
     * @return bool
166
     */
167
    protected function escaped()
168
    {
169
        $previous = $this->lexer->getPrevious();
170
171
        if ($previous && $previous['type'] === EmailLexer::S_BACKSLASH
0 ignored issues
show
Bug Best Practice introduced by
The expression $previous of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
172
            &&
173
            $this->lexer->token['type'] !== EmailLexer::GENERIC
174
        ) {
175
            return true;
176
        }
177
178
        return false;
179
    }
180
181
    /**
182
     * @return bool
183
     */
184
    protected function warnEscaping()
185
    {
186
        if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
187
            return false;
188
        }
189
190
        if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
191
            throw new ExpectingATEXT();
192
        }
193
194
        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
195
            return false;
196
        }
197
198
        $this->warnings[QuotedPart::CODE] =
199
            new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
200
        return true;
201
202
    }
203
204
    protected function checkCRLFInFWS()
205
    {
206
        if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
207
            return;
208
        }
209
210
        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
211
            throw new CRLFX2();
212
        }
213
214
        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
215
            throw new CRLFAtTheEnd();
216
        }
217
    }
218
}
219