Chris@18: lexer = $lexer; Chris@18: } Chris@18: Chris@18: public function getWarnings() Chris@18: { Chris@18: return $this->warnings; Chris@18: } Chris@18: Chris@18: abstract public function parse($str); Chris@18: Chris@18: /** @return int */ Chris@18: public function getOpenedParenthesis() Chris@18: { Chris@18: return $this->openedParenthesis; Chris@18: } Chris@18: Chris@18: /** Chris@18: * validateQuotedPair Chris@18: */ Chris@18: protected function validateQuotedPair() Chris@18: { Chris@18: if (!($this->lexer->token['type'] === EmailLexer::INVALID Chris@18: || $this->lexer->token['type'] === EmailLexer::C_DEL)) { Chris@18: throw new ExpectedQPair(); Chris@18: } Chris@18: Chris@18: $this->warnings[QuotedPart::CODE] = Chris@18: new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']); Chris@18: } Chris@18: Chris@18: protected function parseComments() Chris@18: { Chris@18: $this->openedParenthesis = 1; Chris@18: $this->isUnclosedComment(); Chris@18: $this->warnings[Comment::CODE] = new Comment(); Chris@18: while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { Chris@18: if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) { Chris@18: $this->openedParenthesis++; Chris@18: } Chris@18: $this->warnEscaping(); Chris@18: $this->lexer->moveNext(); Chris@18: } Chris@18: Chris@18: $this->lexer->moveNext(); Chris@18: if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) { Chris@18: throw new ExpectingATEXT(); Chris@18: } Chris@18: Chris@18: if ($this->lexer->isNextToken(EmailLexer::S_AT)) { Chris@18: $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); Chris@18: } Chris@18: } Chris@18: Chris@18: protected function isUnclosedComment() Chris@18: { Chris@18: try { Chris@18: $this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS); Chris@18: return true; Chris@18: } catch (\RuntimeException $e) { Chris@18: throw new UnclosedComment(); Chris@18: } Chris@18: } Chris@18: Chris@18: protected function parseFWS() Chris@18: { Chris@18: $previous = $this->lexer->getPrevious(); Chris@18: Chris@18: $this->checkCRLFInFWS(); Chris@18: Chris@18: if ($this->lexer->token['type'] === EmailLexer::S_CR) { Chris@18: throw new CRNoLF(); Chris@18: } Chris@18: Chris@18: if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) { Chris@18: throw new AtextAfterCFWS(); Chris@18: } Chris@18: Chris@18: if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) { Chris@18: throw new ExpectingCTEXT(); Chris@18: } Chris@18: Chris@18: if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) { Chris@18: $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); Chris@18: } else { Chris@18: $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); Chris@18: } Chris@18: } Chris@18: Chris@18: protected function checkConsecutiveDots() Chris@18: { Chris@18: if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) { Chris@18: throw new ConsecutiveDot(); Chris@18: } Chris@18: } Chris@18: Chris@18: protected function isFWS() Chris@18: { Chris@18: if ($this->escaped()) { Chris@18: return false; Chris@18: } Chris@18: Chris@18: if ($this->lexer->token['type'] === EmailLexer::S_SP || Chris@18: $this->lexer->token['type'] === EmailLexer::S_HTAB || Chris@18: $this->lexer->token['type'] === EmailLexer::S_CR || Chris@18: $this->lexer->token['type'] === EmailLexer::S_LF || Chris@18: $this->lexer->token['type'] === EmailLexer::CRLF Chris@18: ) { Chris@18: return true; Chris@18: } Chris@18: Chris@18: return false; Chris@18: } Chris@18: Chris@18: protected function escaped() Chris@18: { Chris@18: $previous = $this->lexer->getPrevious(); Chris@18: Chris@18: if ($previous['type'] === EmailLexer::S_BACKSLASH Chris@18: && Chris@18: $this->lexer->token['type'] !== EmailLexer::GENERIC Chris@18: ) { Chris@18: return true; Chris@18: } Chris@18: Chris@18: return false; Chris@18: } Chris@18: Chris@18: protected function warnEscaping() Chris@18: { Chris@18: if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) { Chris@18: return false; Chris@18: } Chris@18: Chris@18: if ($this->lexer->isNextToken(EmailLexer::GENERIC)) { Chris@18: throw new ExpectingATEXT(); Chris@18: } Chris@18: Chris@18: if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) { Chris@18: return false; Chris@18: } Chris@18: Chris@18: $this->warnings[QuotedPart::CODE] = Chris@18: new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']); Chris@18: return true; Chris@18: Chris@18: } Chris@18: Chris@18: protected function checkDQUOTE($hasClosingQuote) Chris@18: { Chris@18: if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) { Chris@18: return $hasClosingQuote; Chris@18: } Chris@18: if ($hasClosingQuote) { Chris@18: return $hasClosingQuote; Chris@18: } Chris@18: $previous = $this->lexer->getPrevious(); Chris@18: if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) { Chris@18: throw new ExpectingATEXT(); Chris@18: } Chris@18: Chris@18: try { Chris@18: $this->lexer->find(EmailLexer::S_DQUOTE); Chris@18: $hasClosingQuote = true; Chris@18: } catch (\Exception $e) { Chris@18: throw new UnclosedQuotedString(); Chris@18: } Chris@18: $this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']); Chris@18: Chris@18: return $hasClosingQuote; Chris@18: } Chris@18: Chris@18: protected function checkCRLFInFWS() Chris@18: { Chris@18: if ($this->lexer->token['type'] !== EmailLexer::CRLF) { Chris@18: return; Chris@18: } Chris@18: Chris@18: if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { Chris@18: throw new CRLFX2(); Chris@18: } Chris@18: Chris@18: if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { Chris@18: throw new CRLFAtTheEnd(); Chris@18: } Chris@18: } Chris@18: }