annotate vendor/egulias/email-validator/EmailValidator/Parser/Parser.php @ 19:fa3358dc1485 tip

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