mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-08-28 02:58:29 +00:00
Message-id validator (#290)
* scafolding for MessageID validation * Improve domain valid tokens * Improved EmailParser to remove leaked logic. User of lexer recorder within parsers. * MessageIDParser passing tests. * change left for right, which is the right one * psaml errors * Better naming * comments are not allowed in IDLeft for message-id * Suppress psalm inheritance over tokens and dependencies * Update src/Parser.php Co-authored-by: Alexander M. Turek <me@derrabus.de> * Update src/Parser.php Co-authored-by: Alexander M. Turek <me@derrabus.de> * improve parser from comments Co-authored-by: Alexander M. Turek <me@derrabus.de>
This commit is contained in:
committed by
GitHub
parent
451b438902
commit
8e526a57c1
@@ -11,7 +11,7 @@ use Egulias\EmailValidator\Result\Reason\UnclosedComment;
|
||||
use Egulias\EmailValidator\Result\Reason\UnOpenedComment;
|
||||
use Egulias\EmailValidator\Warning\Comment as WarningComment;
|
||||
|
||||
class Comment extends Parser
|
||||
class Comment extends PartParser
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
|
||||
@@ -20,7 +20,7 @@ use Egulias\EmailValidator\Result\Reason\ExpectingDTEXT;
|
||||
use Egulias\EmailValidator\Result\Reason\UnusualElements;
|
||||
use Egulias\EmailValidator\Warning\DomainLiteral as WarningDomainLiteral;
|
||||
|
||||
class DomainLiteral extends Parser
|
||||
class DomainLiteral extends PartParser
|
||||
{
|
||||
public function parse() : Result
|
||||
{
|
||||
|
||||
+33
-31
@@ -22,7 +22,7 @@ use Egulias\EmailValidator\Parser\CommentStrategy\DomainComment;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingDomainLiteralClose;
|
||||
use Egulias\EmailValidator\Parser\DomainLiteral as DomainLiteralParser;
|
||||
|
||||
class DomainPart extends Parser
|
||||
class DomainPart extends PartParser
|
||||
{
|
||||
const DOMAIN_MAX_LENGTH = 253;
|
||||
const LABEL_MAX_LENGTH = 63;
|
||||
@@ -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']);
|
||||
}
|
||||
|
||||
@@ -128,14 +133,6 @@ class DomainPart extends Parser
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDomainPart()
|
||||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
|
||||
protected function parseComments(): Result
|
||||
{
|
||||
$commentParser = new Comment($this->lexer, new DomainComment());
|
||||
@@ -229,7 +226,6 @@ class DomainPart extends Parser
|
||||
*/
|
||||
protected function parseDomainLiteral() : Result
|
||||
{
|
||||
|
||||
try {
|
||||
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
|
||||
} catch (\RuntimeException $e) {
|
||||
@@ -242,22 +238,8 @@ class DomainPart extends Parser
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InvalidEmail|ValidEmail
|
||||
*/
|
||||
protected function checkDomainPartExceptions(array $prev, bool $hasComments) : Result
|
||||
{
|
||||
$validDomainTokens = array(
|
||||
EmailLexer::GENERIC => true,
|
||||
EmailLexer::S_HYPHEN => true,
|
||||
EmailLexer::S_DOT => true,
|
||||
);
|
||||
|
||||
if ($hasComments) {
|
||||
$validDomainTokens[EmailLexer::S_OPENPARENTHESIS] = true;
|
||||
$validDomainTokens[EmailLexer::S_CLOSEPARENTHESIS] = true;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET && $prev['type'] !== EmailLexer::S_AT) {
|
||||
return new InvalidEmail(new ExpectingATEXT('OPENBRACKET not after AT'), $this->lexer->token['value']);
|
||||
}
|
||||
@@ -271,6 +253,22 @@ class DomainPart extends Parser
|
||||
return new InvalidEmail(new ExpectingATEXT('Escaping following "ATOM"'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return $this->validateTokens($hasComments);
|
||||
}
|
||||
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
$validDomainTokens = array(
|
||||
EmailLexer::GENERIC => true,
|
||||
EmailLexer::S_HYPHEN => true,
|
||||
EmailLexer::S_DOT => true,
|
||||
);
|
||||
|
||||
if ($hasComments) {
|
||||
$validDomainTokens[EmailLexer::S_OPENPARENTHESIS] = true;
|
||||
$validDomainTokens[EmailLexer::S_CLOSEPARENTHESIS] = true;
|
||||
}
|
||||
|
||||
if (!isset($validDomainTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
|
||||
}
|
||||
@@ -278,7 +276,6 @@ class DomainPart extends Parser
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
|
||||
private function checkLabelLength(bool $isEndOfDomain = false) : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT || $isEndOfDomain) {
|
||||
@@ -307,4 +304,9 @@ class DomainPart extends Parser
|
||||
$this->warnings[TLD::CODE] = new TLD();
|
||||
}
|
||||
}
|
||||
|
||||
public function domainPart() : string
|
||||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Result\Reason\UnclosedQuotedString;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
|
||||
class DoubleQuote extends Parser
|
||||
class DoubleQuote extends PartParser
|
||||
{
|
||||
public function parse() : Result
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ use Egulias\EmailValidator\Result\Reason\ExpectingCTEXT;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
|
||||
class FoldingWhiteSpace extends Parser
|
||||
class FoldingWhiteSpace extends PartParser
|
||||
{
|
||||
public function parse() : Result
|
||||
{
|
||||
@@ -23,7 +23,10 @@ class FoldingWhiteSpace extends Parser
|
||||
|
||||
$previous = $this->lexer->getPrevious();
|
||||
|
||||
$this->checkCRLFInFWS();
|
||||
$resultCRLF = $this->checkCRLFInFWS();
|
||||
if ($resultCRLF->isInvalid()) {
|
||||
return $resultCRLF;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CR) {
|
||||
return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']);
|
||||
@@ -46,10 +49,7 @@ class FoldingWhiteSpace extends Parser
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InvalidEmail|ValidEmail|null
|
||||
*/
|
||||
protected function checkCRLFInFWS()
|
||||
protected function checkCRLFInFWS() : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
|
||||
return new ValidEmail();
|
||||
@@ -63,12 +63,11 @@ class FoldingWhiteSpace extends Parser
|
||||
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
|
||||
return new InvalidEmail(new CRLFAtTheEnd(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function isFWS()
|
||||
protected function isFWS() : bool
|
||||
{
|
||||
if ($this->escaped()) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\CommentsInIDRight;
|
||||
|
||||
class IDLeftPart extends LocalPart
|
||||
{
|
||||
protected function parseComments(): Result
|
||||
{
|
||||
return new InvalidEmail(new CommentsInIDRight(), $this->lexer->token['value']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
|
||||
class IDRightPart extends DomainPart
|
||||
{
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
$invalidDomainTokens = array(
|
||||
EmailLexer::S_DQUOTE => true,
|
||||
EmailLexer::S_SQUOTE => true,
|
||||
EmailLexer::S_BACKTICK => true,
|
||||
EmailLexer::S_SEMICOLON => true,
|
||||
EmailLexer::S_GREATERTHAN => true,
|
||||
EmailLexer::S_LOWERTHAN => true,
|
||||
);
|
||||
|
||||
if (isset($invalidDomainTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
}
|
||||
+35
-20
@@ -13,27 +13,17 @@ use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Parser\CommentStrategy\LocalComment;
|
||||
|
||||
class LocalPart extends Parser
|
||||
class LocalPart extends PartParser
|
||||
{
|
||||
|
||||
/**
|
||||
* Invalid lexer tokens for local part
|
||||
* @var array
|
||||
* @var string
|
||||
*/
|
||||
private $invalidTokens = array(
|
||||
EmailLexer::S_COMMA => EmailLexer::S_COMMA,
|
||||
EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
|
||||
EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
|
||||
EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
|
||||
EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
|
||||
EmailLexer::S_COLON => EmailLexer::S_COLON,
|
||||
EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
|
||||
EmailLexer::INVALID => EmailLexer::INVALID
|
||||
);
|
||||
private $localPart = '';
|
||||
|
||||
|
||||
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()) {
|
||||
@@ -74,8 +64,9 @@ class LocalPart extends Parser
|
||||
return $resultEscaping;
|
||||
}
|
||||
|
||||
if (isset($this->invalidTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
|
||||
$resultToken = $this->validateTokens(false);
|
||||
if ($resultToken->isInvalid()) {
|
||||
return $resultToken;
|
||||
}
|
||||
|
||||
$resultFWS = $this->parseLocalFWS();
|
||||
@@ -83,17 +74,41 @@ 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();
|
||||
}
|
||||
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
$invalidTokens = array(
|
||||
EmailLexer::S_COMMA => EmailLexer::S_COMMA,
|
||||
EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
|
||||
EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
|
||||
EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
|
||||
EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
|
||||
EmailLexer::S_COLON => EmailLexer::S_COLON,
|
||||
EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
|
||||
EmailLexer::INVALID => EmailLexer::INVALID
|
||||
);
|
||||
if (isset($invalidTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
public function localPart() : string
|
||||
{
|
||||
return $this->localPart;
|
||||
}
|
||||
|
||||
private function parseLocalFWS() : Result
|
||||
{
|
||||
$foldingWS = new FoldingWhiteSpace($this->lexer);
|
||||
@@ -118,7 +133,7 @@ class LocalPart extends Parser
|
||||
return $parseAgain;
|
||||
}
|
||||
|
||||
private function parseComments(): Result
|
||||
protected function parseComments(): Result
|
||||
{
|
||||
$commentParser = new Comment($this->lexer, new LocalComment());
|
||||
$result = $commentParser->parse();
|
||||
|
||||
@@ -8,7 +8,7 @@ use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
|
||||
abstract class Parser
|
||||
abstract class PartParser
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
@@ -25,6 +25,8 @@ abstract class Parser
|
||||
$this->lexer = $lexer;
|
||||
}
|
||||
|
||||
abstract public function parse() : Result;
|
||||
|
||||
/**
|
||||
* @return \Egulias\EmailValidator\Warning\Warning[]
|
||||
*/
|
||||
@@ -33,8 +35,6 @@ abstract class Parser
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
abstract public function parse() : Result;
|
||||
|
||||
protected function parseFWS() : Result
|
||||
{
|
||||
$foldingWS = new FoldingWhiteSpace($this->lexer);
|
||||
@@ -52,9 +52,6 @@ abstract class Parser
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function escaped() : bool
|
||||
{
|
||||
$previous = $this->lexer->getPrevious();
|
||||
Reference in New Issue
Block a user