Chris@18
|
1 <?php
|
Chris@18
|
2
|
Chris@18
|
3 namespace Egulias\EmailValidator\Parser;
|
Chris@18
|
4
|
Chris@18
|
5 use Egulias\EmailValidator\Exception\DotAtEnd;
|
Chris@18
|
6 use Egulias\EmailValidator\Exception\DotAtStart;
|
Chris@18
|
7 use Egulias\EmailValidator\EmailLexer;
|
Chris@18
|
8 use Egulias\EmailValidator\EmailValidator;
|
Chris@18
|
9 use Egulias\EmailValidator\Exception\ExpectingAT;
|
Chris@18
|
10 use Egulias\EmailValidator\Exception\ExpectingATEXT;
|
Chris@18
|
11 use Egulias\EmailValidator\Exception\UnclosedQuotedString;
|
Chris@18
|
12 use Egulias\EmailValidator\Exception\UnopenedComment;
|
Chris@18
|
13 use Egulias\EmailValidator\Warning\CFWSWithFWS;
|
Chris@18
|
14 use Egulias\EmailValidator\Warning\LocalTooLong;
|
Chris@18
|
15
|
Chris@18
|
16 class LocalPart extends Parser
|
Chris@18
|
17 {
|
Chris@18
|
18 public function parse($localPart)
|
Chris@18
|
19 {
|
Chris@18
|
20 $parseDQuote = true;
|
Chris@18
|
21 $closingQuote = false;
|
Chris@18
|
22 $openedParenthesis = 0;
|
Chris@18
|
23
|
Chris@18
|
24 while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
|
Chris@18
|
25 if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
|
Chris@18
|
26 throw new DotAtStart();
|
Chris@18
|
27 }
|
Chris@18
|
28
|
Chris@18
|
29 $closingQuote = $this->checkDQUOTE($closingQuote);
|
Chris@18
|
30 if ($closingQuote && $parseDQuote) {
|
Chris@18
|
31 $parseDQuote = $this->parseDoubleQuote();
|
Chris@18
|
32 }
|
Chris@18
|
33
|
Chris@18
|
34 if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
Chris@18
|
35 $this->parseComments();
|
Chris@18
|
36 $openedParenthesis += $this->getOpenedParenthesis();
|
Chris@18
|
37 }
|
Chris@18
|
38 if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
Chris@18
|
39 if ($openedParenthesis === 0) {
|
Chris@18
|
40 throw new UnopenedComment();
|
Chris@18
|
41 } else {
|
Chris@18
|
42 $openedParenthesis--;
|
Chris@18
|
43 }
|
Chris@18
|
44 }
|
Chris@18
|
45
|
Chris@18
|
46 $this->checkConsecutiveDots();
|
Chris@18
|
47
|
Chris@18
|
48 if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
|
Chris@18
|
49 $this->lexer->isNextToken(EmailLexer::S_AT)
|
Chris@18
|
50 ) {
|
Chris@18
|
51 throw new DotAtEnd();
|
Chris@18
|
52 }
|
Chris@18
|
53
|
Chris@18
|
54 $this->warnEscaping();
|
Chris@18
|
55 $this->isInvalidToken($this->lexer->token, $closingQuote);
|
Chris@18
|
56
|
Chris@18
|
57 if ($this->isFWS()) {
|
Chris@18
|
58 $this->parseFWS();
|
Chris@18
|
59 }
|
Chris@18
|
60
|
Chris@18
|
61 $this->lexer->moveNext();
|
Chris@18
|
62 }
|
Chris@18
|
63
|
Chris@18
|
64 $prev = $this->lexer->getPrevious();
|
Chris@18
|
65 if (strlen($prev['value']) > LocalTooLong::LOCAL_PART_LENGTH) {
|
Chris@18
|
66 $this->warnings[LocalTooLong::CODE] = new LocalTooLong();
|
Chris@18
|
67 }
|
Chris@18
|
68 }
|
Chris@18
|
69
|
Chris@18
|
70 protected function parseDoubleQuote()
|
Chris@18
|
71 {
|
Chris@18
|
72 $parseAgain = true;
|
Chris@18
|
73 $special = array(
|
Chris@18
|
74 EmailLexer::S_CR => true,
|
Chris@18
|
75 EmailLexer::S_HTAB => true,
|
Chris@18
|
76 EmailLexer::S_LF => true
|
Chris@18
|
77 );
|
Chris@18
|
78
|
Chris@18
|
79 $invalid = array(
|
Chris@18
|
80 EmailLexer::C_NUL => true,
|
Chris@18
|
81 EmailLexer::S_HTAB => true,
|
Chris@18
|
82 EmailLexer::S_CR => true,
|
Chris@18
|
83 EmailLexer::S_LF => true
|
Chris@18
|
84 );
|
Chris@18
|
85 $setSpecialsWarning = true;
|
Chris@18
|
86
|
Chris@18
|
87 $this->lexer->moveNext();
|
Chris@18
|
88
|
Chris@18
|
89 while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) {
|
Chris@18
|
90 $parseAgain = false;
|
Chris@18
|
91 if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
|
Chris@18
|
92 $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
|
Chris@18
|
93 $setSpecialsWarning = false;
|
Chris@18
|
94 }
|
Chris@18
|
95 if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) {
|
Chris@18
|
96 $this->lexer->moveNext();
|
Chris@18
|
97 }
|
Chris@18
|
98
|
Chris@18
|
99 $this->lexer->moveNext();
|
Chris@18
|
100
|
Chris@18
|
101 if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
|
Chris@18
|
102 throw new ExpectingATEXT();
|
Chris@18
|
103 }
|
Chris@18
|
104 }
|
Chris@18
|
105
|
Chris@18
|
106 $prev = $this->lexer->getPrevious();
|
Chris@18
|
107
|
Chris@18
|
108 if ($prev['type'] === EmailLexer::S_BACKSLASH) {
|
Chris@18
|
109 if (!$this->checkDQUOTE(false)) {
|
Chris@18
|
110 throw new UnclosedQuotedString();
|
Chris@18
|
111 }
|
Chris@18
|
112 }
|
Chris@18
|
113
|
Chris@18
|
114 if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
|
Chris@18
|
115 throw new ExpectingAT();
|
Chris@18
|
116 }
|
Chris@18
|
117
|
Chris@18
|
118 return $parseAgain;
|
Chris@18
|
119 }
|
Chris@18
|
120
|
Chris@18
|
121 protected function isInvalidToken($token, $closingQuote)
|
Chris@18
|
122 {
|
Chris@18
|
123 $forbidden = array(
|
Chris@18
|
124 EmailLexer::S_COMMA,
|
Chris@18
|
125 EmailLexer::S_CLOSEBRACKET,
|
Chris@18
|
126 EmailLexer::S_OPENBRACKET,
|
Chris@18
|
127 EmailLexer::S_GREATERTHAN,
|
Chris@18
|
128 EmailLexer::S_LOWERTHAN,
|
Chris@18
|
129 EmailLexer::S_COLON,
|
Chris@18
|
130 EmailLexer::S_SEMICOLON,
|
Chris@18
|
131 EmailLexer::INVALID
|
Chris@18
|
132 );
|
Chris@18
|
133
|
Chris@18
|
134 if (in_array($token['type'], $forbidden) && !$closingQuote) {
|
Chris@18
|
135 throw new ExpectingATEXT();
|
Chris@18
|
136 }
|
Chris@18
|
137 }
|
Chris@18
|
138 }
|