From eb1755efbf429fce78bb4213c00b3528ec681149 Mon Sep 17 00:00:00 2001 From: Andrei Sozonov Date: Fri, 2 Oct 2015 12:42:25 +0300 Subject: [PATCH 1/2] #80 validate number of closing parenthesis --- src/Egulias/EmailValidator/EmailValidator.php | 1 + .../EmailValidator/Parser/DomainPart.php | 18 ++++++++++++---- .../EmailValidator/Parser/LocalPart.php | 21 ++++++++++++------- src/Egulias/EmailValidator/Parser/Parser.php | 20 +++++++++++------- .../EmailValidator/EmailValidatorTest.php | 5 +++++ 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/Egulias/EmailValidator/EmailValidator.php b/src/Egulias/EmailValidator/EmailValidator.php index d047dec..7e2b65f 100644 --- a/src/Egulias/EmailValidator/EmailValidator.php +++ b/src/Egulias/EmailValidator/EmailValidator.php @@ -33,6 +33,7 @@ class EmailValidator const ERR_FWS_CRLF_END = 149; const ERR_CR_NO_LF = 150; const ERR_DEPREC_REACHED = 151; + const ERR_UNOPENEDCOMMENT = 152; const RFC5321_TLD = 9; const RFC5321_TLDNUMERIC = 10; const RFC5321_QUOTEDSTRING = 11; diff --git a/src/Egulias/EmailValidator/Parser/DomainPart.php b/src/Egulias/EmailValidator/Parser/DomainPart.php index c4d0cf6..a15bf48 100644 --- a/src/Egulias/EmailValidator/Parser/DomainPart.php +++ b/src/Egulias/EmailValidator/Parser/DomainPart.php @@ -4,7 +4,6 @@ namespace Egulias\EmailValidator\Parser; use Egulias\EmailValidator\EmailLexer; -use Egulias\EmailValidator\Parser\Parser; use Egulias\EmailValidator\EmailValidator; class DomainPart extends Parser @@ -103,8 +102,8 @@ class DomainPart extends Parser protected function doParseDomainPart() { $domain = ''; + $openedParenthesis = 0; do { - $prev = $this->lexer->getPrevious(); if ($this->lexer->token['type'] === EmailLexer::S_SLASH) { @@ -112,8 +111,19 @@ class DomainPart extends Parser } if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { - $this->parseComments(); + $this->parseComments($openedParenthesis); $this->lexer->moveNext(); + $tmpPrev = $this->lexer->getPrevious(); + if ($tmpPrev['type'] === EmailLexer::S_CLOSEPARENTHESIS) { + $openedParenthesis--; + } + } + if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { + if ($openedParenthesis === 0) { + throw new \InvalidArgumentException('ERR_UNOPENEDCOMMENT'); + } else { + $openedParenthesis--; + } } $this->checkConsecutiveDots(); @@ -180,7 +190,7 @@ class DomainPart extends Parser } if ($this->lexer->isNextToken(EmailLexer::S_CR)) { - throw new \InvalidArgumentException("ERR_CR_NO_LF"); + throw new \InvalidArgumentException('ERR_CR_NO_LF'); } if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) { $this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT; diff --git a/src/Egulias/EmailValidator/Parser/LocalPart.php b/src/Egulias/EmailValidator/Parser/LocalPart.php index 2f77164..4b768d9 100644 --- a/src/Egulias/EmailValidator/Parser/LocalPart.php +++ b/src/Egulias/EmailValidator/Parser/LocalPart.php @@ -4,7 +4,6 @@ namespace Egulias\EmailValidator\Parser; use Egulias\EmailValidator\EmailLexer; use Egulias\EmailValidator\EmailValidator; -use \InvalidArgumentException; class LocalPart extends Parser { @@ -12,9 +11,9 @@ class LocalPart extends Parser { $parseDQuote = true; $closingQuote = false; + $openedParenthesis = 0; while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) { - if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) { throw new \InvalidArgumentException('ERR_DOT_START'); } @@ -25,13 +24,19 @@ class LocalPart extends Parser } if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { - $this->parseComments(); + $this->parseComments($openedParenthesis); + } + if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { + if ($openedParenthesis === 0) { + throw new \InvalidArgumentException('ERR_UNOPENEDCOMMENT'); + } else { + $openedParenthesis--; + } } $this->checkConsecutiveDots(); - if ( - $this->lexer->token['type'] === EmailLexer::S_DOT && + if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_AT) ) { throw new \InvalidArgumentException('ERR_DOT_END'); @@ -82,7 +87,7 @@ class LocalPart extends Parser $this->lexer->moveNext(); if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) { - throw new InvalidArgumentException("ERR_EXPECTED_ATEXT"); + throw new \InvalidArgumentException('ERR_EXPECTED_ATEXT'); } } @@ -90,12 +95,12 @@ class LocalPart extends Parser if ($prev['type'] === EmailLexer::S_BACKSLASH) { if (!$this->checkDQUOTE(false)) { - throw new \InvalidArgumentException("ERR_UNCLOSED_DQUOTE"); + throw new \InvalidArgumentException('ERR_UNCLOSED_DQUOTE'); } } if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) { - throw new \InvalidArgumentException("ERR_EXPECED_AT"); + throw new \InvalidArgumentException('ERR_EXPECED_AT'); } return $parseAgain; diff --git a/src/Egulias/EmailValidator/Parser/Parser.php b/src/Egulias/EmailValidator/Parser/Parser.php index b66279e..9d60d97 100644 --- a/src/Egulias/EmailValidator/Parser/Parser.php +++ b/src/Egulias/EmailValidator/Parser/Parser.php @@ -20,7 +20,7 @@ abstract class Parser return $this->warnings; } - abstract function parse($str); + abstract public function parse($str); /** * validateQuotedPair @@ -36,14 +36,18 @@ abstract class Parser } /** - * @return string the the comment - * @throws \InvalidArgumentException + * @param int $openedParenthesis + * @return string the comment */ - protected function parseComments() + protected function parseComments(&$openedParenthesis = 0) { + $openedParenthesis++; $this->isUnclosedComment(); $this->warnings[] = EmailValidator::CFWS_COMMENT; while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { + if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) { + $openedParenthesis++; + } $this->warnEscaping(); $this->lexer->moveNext(); } @@ -75,11 +79,11 @@ abstract class Parser $this->checkCRLFInFWS(); if ($this->lexer->token['type'] === EmailLexer::S_CR) { - throw new \InvalidArgumentException("ERR_CR_NO_LF"); + throw new \InvalidArgumentException('ERR_CR_NO_LF'); } if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) { - throw new \InvalidArgumentException("ERR_ATEXT_AFTER_CFWS"); + throw new \InvalidArgumentException('ERR_ATEXT_AFTER_CFWS'); } if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) { @@ -181,10 +185,10 @@ abstract class Parser return; } if ($this->lexer->isNextToken(EmailLexer::CRLF)) { - throw new \InvalidArgumentException("ERR_FWS_CRLF_X2"); + throw new \InvalidArgumentException('ERR_FWS_CRLF_X2'); } if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { - throw new \InvalidArgumentException("ERR_FWS_CRLF_END"); + throw new \InvalidArgumentException('ERR_FWS_CRLF_END'); } } } diff --git a/tests/egulias/Tests/EmailValidator/EmailValidatorTest.php b/tests/egulias/Tests/EmailValidator/EmailValidatorTest.php index 1491df7..03c08d1 100644 --- a/tests/egulias/Tests/EmailValidator/EmailValidatorTest.php +++ b/tests/egulias/Tests/EmailValidator/EmailValidatorTest.php @@ -152,6 +152,11 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase array(EmailValidator::ERR_DOT_END, 'example@localhost.'), array(EmailValidator::ERR_DOT_END, 'example.@example.co.uk'), array(EmailValidator::ERR_UNCLOSEDCOMMENT, '(example@localhost'), + array(EmailValidator::ERR_UNOPENEDCOMMENT, 'comment)example@localhost'), + array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example(comment))@localhost'), + array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@comment)localhost'), + array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@localhost(comment))'), + array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@(comment))example.com'), array(EmailValidator::ERR_UNCLOSEDQUOTEDSTR, '"example@localhost'), array(EmailValidator::ERR_EXPECTING_ATEXT, 'exa"mple@localhost'), //This was the original. But atext is not allowed after \n From c3a5dfb6812b3a87c2d92c93e5675e11b4cb8822 Mon Sep 17 00:00:00 2001 From: Andrei Sozonov Date: Tue, 6 Oct 2015 10:48:39 +0300 Subject: [PATCH 2/2] opened parenthesis converted to class field --- .../EmailValidator/Parser/DomainPart.php | 3 ++- .../EmailValidator/Parser/LocalPart.php | 3 ++- src/Egulias/EmailValidator/Parser/Parser.php | 19 +++++++++++-------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Egulias/EmailValidator/Parser/DomainPart.php b/src/Egulias/EmailValidator/Parser/DomainPart.php index a15bf48..c9b4bb7 100644 --- a/src/Egulias/EmailValidator/Parser/DomainPart.php +++ b/src/Egulias/EmailValidator/Parser/DomainPart.php @@ -111,7 +111,8 @@ class DomainPart extends Parser } if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { - $this->parseComments($openedParenthesis); + $this->parseComments(); + $openedParenthesis += $this->getOpenedParenthesis(); $this->lexer->moveNext(); $tmpPrev = $this->lexer->getPrevious(); if ($tmpPrev['type'] === EmailLexer::S_CLOSEPARENTHESIS) { diff --git a/src/Egulias/EmailValidator/Parser/LocalPart.php b/src/Egulias/EmailValidator/Parser/LocalPart.php index 4b768d9..449748f 100644 --- a/src/Egulias/EmailValidator/Parser/LocalPart.php +++ b/src/Egulias/EmailValidator/Parser/LocalPart.php @@ -24,7 +24,8 @@ class LocalPart extends Parser } if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { - $this->parseComments($openedParenthesis); + $this->parseComments(); + $openedParenthesis += $this->getOpenedParenthesis(); } if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { if ($openedParenthesis === 0) { diff --git a/src/Egulias/EmailValidator/Parser/Parser.php b/src/Egulias/EmailValidator/Parser/Parser.php index 9d60d97..8a72cd1 100644 --- a/src/Egulias/EmailValidator/Parser/Parser.php +++ b/src/Egulias/EmailValidator/Parser/Parser.php @@ -9,6 +9,7 @@ abstract class Parser { protected $warnings = array(); protected $lexer; + protected $openedParenthesis = 0; public function __construct(EmailLexer $lexer) { @@ -22,6 +23,12 @@ abstract class Parser abstract public function parse($str); + /** @return int */ + public function getOpenedParenthesis() + { + return $this->openedParenthesis; + } + /** * validateQuotedPair */ @@ -35,18 +42,14 @@ abstract class Parser $this->warnings[] = EmailValidator::DEPREC_QP; } - /** - * @param int $openedParenthesis - * @return string the comment - */ - protected function parseComments(&$openedParenthesis = 0) + protected function parseComments() { - $openedParenthesis++; + $this->openedParenthesis = 1; $this->isUnclosedComment(); $this->warnings[] = EmailValidator::CFWS_COMMENT; while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) { - $openedParenthesis++; + $this->openedParenthesis++; } $this->warnEscaping(); $this->lexer->moveNext(); @@ -164,7 +167,7 @@ abstract class Parser return $hasClosingQuote; } $previous = $this->lexer->getPrevious(); - if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) { + if ($previous['type'] === EmailLexer::GENERIC && $this->lexer->isNextToken(EmailLexer::GENERIC)) { throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT'); }