From b80f568f7538fa086fbe1d2b1793efb95d84a2ad Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Sun, 21 Feb 2021 22:06:38 +0100 Subject: [PATCH] Improved EmailParser to remove leaked logic. User of lexer recorder within parsers. --- src/EmailParser.php | 83 +++++++++++---------------------------- src/Parser.php | 62 +++++++++++++++++++++++++++++ src/Parser/DomainPart.php | 22 ++++++++--- src/Parser/LocalPart.php | 16 ++++++-- 4 files changed, 115 insertions(+), 68 deletions(-) create mode 100644 src/Parser.php diff --git a/src/EmailParser.php b/src/EmailParser.php index 9b30c03..d2d750d 100644 --- a/src/EmailParser.php +++ b/src/EmailParser.php @@ -9,18 +9,12 @@ use Egulias\EmailValidator\Parser\DomainPart; use Egulias\EmailValidator\Result\ValidEmail; use Egulias\EmailValidator\Result\InvalidEmail; use Egulias\EmailValidator\Warning\EmailTooLong; -use Egulias\EmailValidator\Result\Reason\ExpectingATEXT; use Egulias\EmailValidator\Result\Reason\NoLocalPart; -class EmailParser +class EmailParser extends Parser { const EMAIL_MAX_LENGTH = 254; - /** - * @var array - */ - protected $warnings = []; - /** * @var string */ @@ -30,10 +24,6 @@ class EmailParser * @var string */ protected $localPart = ''; - /** - * @var EmailLexer - */ - protected $lexer; public function __construct(EmailLexer $lexer) { @@ -42,40 +32,36 @@ class EmailParser public function parse(string $str) : Result { - $this->lexer->setInput($str); - - if (!$this->hasAtToken()) { - return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]); - } - - $localPartResult = $this->processLocalPart(); - - if ($localPartResult->isInvalid()) { - return $localPartResult; - } - - $domainPartResult = $this->processDomainPart(); - - if ($domainPartResult->isInvalid()) { - return $domainPartResult; - } - - if ($this->lexer->hasInvalidTokens()) { - return new InvalidEmail(new ExpectingATEXT("Invalid tokens found"), $this->lexer->token["value"]); - } + $result = parent::parse($str); $this->addLongEmailWarning($this->localPart, $this->domainPart); + return $result; + } + + protected function preRightParsing(): Result + { + if (!$this->hasAtToken()) { + return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]); + } return new ValidEmail(); } + protected function parseRightFromAt(): Result + { + return $this->processLocalPart(); + } + + protected function parseLeftFromAt(): Result + { + return $this->processDomainPart(); + } + private function processLocalPart() : Result { - $this->lexer->startRecording(); $localPartParser = new LocalPart($this->lexer); $localPartResult = $localPartParser->parse(); - $this->lexer->stopRecording(); - $this->localPart = rtrim($this->lexer->getAccumulatedValues(), '@'); + $this->localPart = $localPartParser->localPart(); $this->warnings = array_merge($localPartParser->getWarnings(), $this->warnings); return $localPartResult; @@ -83,28 +69,14 @@ class EmailParser private function processDomainPart() : Result { - $this->lexer->clearRecorded(); - $this->lexer->startRecording(); $domainPartParser = new DomainPart($this->lexer); $domainPartResult = $domainPartParser->parse(); - $this->lexer->stopRecording(); - $this->domainPart = $this->lexer->getAccumulatedValues(); + $this->domainPart = $domainPartParser->domainPart(); $this->warnings = array_merge($domainPartParser->getWarnings(), $this->warnings); return $domainPartResult; } - /** - * @return Warning\Warning[] - */ - public function getWarnings() : array - { - return $this->warnings; - } - - /** - * @return string - */ public function getDomainPart() : string { return $this->domainPart; @@ -115,10 +87,7 @@ class EmailParser return $this->localPart; } - /** - * @return bool - */ - protected function hasAtToken() : bool + private function hasAtToken() : bool { $this->lexer->moveNext(); $this->lexer->moveNext(); @@ -129,11 +98,7 @@ class EmailParser return true; } - /** - * @param string $localPart - * @param string $parsedDomainPart - */ - protected function addLongEmailWarning($localPart, $parsedDomainPart) : void + private function addLongEmailWarning(string $localPart, string $parsedDomainPart) : void { if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) { $this->warnings[EmailTooLong::CODE] = new EmailTooLong(); diff --git a/src/Parser.php b/src/Parser.php new file mode 100644 index 0000000..4993831 --- /dev/null +++ b/src/Parser.php @@ -0,0 +1,62 @@ +lexer->setInput($str); + + if ($this->lexer->hasInvalidTokens()) { + return new InvalidEmail(new ExpectingATEXT("Invalid tokens found"), $this->lexer->token["value"]); + } + + $preParsingResult = $this->preRightParsing(); + if ($preParsingResult->isInvalid()) { + return $preParsingResult; + } + + $localPartResult = $this->parseRightFromAt(); + + if ($localPartResult->isInvalid()) { + return $localPartResult; + } + + $domainPartResult = $this->parseLeftFromAt(); + + if ($domainPartResult->isInvalid()) { + return $domainPartResult; + } + + return new ValidEmail(); + } + + /** + * @return Warning\Warning[] + */ + public function getWarnings() : array + { + return $this->warnings; + } +} \ No newline at end of file diff --git a/src/Parser/DomainPart.php b/src/Parser/DomainPart.php index 99239b6..6688c40 100644 --- a/src/Parser/DomainPart.php +++ b/src/Parser/DomainPart.php @@ -39,6 +39,9 @@ class DomainPart extends Parser public function parse() : Result { + $this->lexer->clearRecorded(); + $this->lexer->startRecording(); + $this->lexer->moveNext(); $domainChecks = $this->performDomainStartChecks(); @@ -49,20 +52,22 @@ class DomainPart extends Parser if ($this->lexer->token['type'] === EmailLexer::S_AT) { return new InvalidEmail(new ConsecutiveAt(), $this->lexer->token['value']); } - $domain = $this->doParseDomainPart(); - if ($domain->isInvalid()) { - return $domain; - } - $length = strlen($this->domainPart); + $result = $this->doParseDomainPart(); + if ($result->isInvalid()) { + return $result; + } $end = $this->checkEndOfDomain(); if ($end->isInvalid()) { return $end; } + $this->lexer->stopRecording(); + $this->domainPart = $this->lexer->getAccumulatedValues(); + + $length = strlen($this->domainPart); if ($length > self::DOMAIN_MAX_LENGTH) { - //$this->warnings[DomainTooLong::CODE] = new DomainTooLong(); return new InvalidEmail(new DomainTooLong(), $this->lexer->token['value']); } @@ -312,4 +317,9 @@ class DomainPart extends Parser $this->warnings[TLD::CODE] = new TLD(); } } + + public function domainPart() : string + { + return $this->domainPart; + } } \ No newline at end of file diff --git a/src/Parser/LocalPart.php b/src/Parser/LocalPart.php index 9b0da9f..154b149 100644 --- a/src/Parser/LocalPart.php +++ b/src/Parser/LocalPart.php @@ -15,6 +15,10 @@ use Egulias\EmailValidator\Parser\CommentStrategy\LocalComment; class LocalPart extends Parser { + /** + * @var string + */ + private $localPart = ''; /** * Invalid lexer tokens for local part @@ -33,7 +37,7 @@ class LocalPart extends Parser public function parse() : Result { - $totalLength = 0; + $this->lexer->startRecording(); while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) { if ($this->hasDotAtStart()) { @@ -83,17 +87,23 @@ class LocalPart extends Parser return $resultFWS; } - $totalLength += strlen($this->lexer->token['value']); $this->lexer->moveNext(); } - if ($totalLength > LocalTooLong::LOCAL_PART_LENGTH) { + $this->lexer->stopRecording(); + $this->localPart = rtrim($this->lexer->getAccumulatedValues(), '@'); + if (strlen($this->localPart) > LocalTooLong::LOCAL_PART_LENGTH) { $this->warnings[LocalTooLong::CODE] = new LocalTooLong(); } return new ValidEmail(); } + public function localPart() : string + { + return $this->localPart; + } + private function parseLocalFWS() : Result { $foldingWS = new FoldingWhiteSpace($this->lexer);