Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Message-id validator #290

Merged
merged 14 commits into from
Mar 6, 2021
Merged
197 changes: 99 additions & 98 deletions composer.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/EmailLexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class EmailLexer extends AbstractLexer
*
* @var array
*
* @psalm-suppress NonInvariantDocblockPropertyType
* @psalm-var array{value:string, type:null|int, position:int}
* @psalm-suppress NonInvariantDocblockPropertyType
*/
Expand Down
93 changes: 21 additions & 72 deletions src/EmailParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,12 @@
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
*/
Expand All @@ -30,81 +24,54 @@ class EmailParser
* @var string
*/
protected $localPart = '';
/**
* @var EmailLexer
*/
protected $lexer;

public function __construct(EmailLexer $lexer)
{
$this->lexer = $lexer;
}

public function parse(string $str) : Result
{
$this->lexer->setInput($str);
$result = parent::parse($str);

$this->addLongEmailWarning($this->localPart, $this->domainPart);

return $result;
}

protected function preLeftParsing(): Result
{
if (!$this->hasAtToken()) {
return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]);
}
return new ValidEmail();
}

$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"]);
}

$this->addLongEmailWarning($this->localPart, $this->domainPart);
protected function parseLeftFromAt(): Result
{
return $this->processLocalPart();
}

return new ValidEmail();
protected function parseRightFromAt(): 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;
}

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;
Expand All @@ -115,25 +82,7 @@ public function getLocalPart() : string
return $this->localPart;
}

/**
* @return bool
*/
protected function hasAtToken() : bool
{
$this->lexer->moveNext();
$this->lexer->moveNext();
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
return false;
}

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();
Expand Down
93 changes: 93 additions & 0 deletions src/MessageIDParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace Egulias\EmailValidator;

use Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Result\Result;
use Egulias\EmailValidator\Parser\IDLeftPart;
use Egulias\EmailValidator\Parser\IDRightPart;
use Egulias\EmailValidator\Result\ValidEmail;
use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Warning\EmailTooLong;
use Egulias\EmailValidator\Result\Reason\NoLocalPart;

class MessageIDParser extends Parser
{

const EMAILID_MAX_LENGTH = 254;

/**
* @var string
*/
protected $idLeft = '';

/**
* @var string
*/
protected $idRight = '';

public function parse(string $str) : Result
{
$result = parent::parse($str);

$this->addLongEmailWarning($this->idLeft, $this->idRight);

return $result;
}

protected function preLeftParsing(): Result
{
if (!$this->hasAtToken()) {
return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]);
}
return new ValidEmail();
}

protected function parseLeftFromAt(): Result
{
return $this->processIDLeft();
}

protected function parseRightFromAt(): Result
{
return $this->processIDRight();
}

private function processIDLeft() : Result
{
$localPartParser = new IDLeftPart($this->lexer);
$localPartResult = $localPartParser->parse();
$this->idLeft = $localPartParser->localPart();
$this->warnings = array_merge($localPartParser->getWarnings(), $this->warnings);

return $localPartResult;
}

private function processIDRight() : Result
{
$domainPartParser = new IDRightPart($this->lexer);
$domainPartResult = $domainPartParser->parse();
$this->idRight = $domainPartParser->domainPart();
$this->warnings = array_merge($domainPartParser->getWarnings(), $this->warnings);

return $domainPartResult;
}

public function getLeftPart() : string
{
return $this->idLeft;
}

public function getRightPart() : string
{
return $this->idRight;
}

private function addLongEmailWarning(string $localPart, string $parsedDomainPart) : void
{
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAILID_MAX_LENGTH) {
$this->warnings[EmailTooLong::CODE] = new EmailTooLong();
}
}
}
78 changes: 78 additions & 0 deletions src/Parser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Egulias\EmailValidator;

use Egulias\EmailValidator\Result\Result;
use Egulias\EmailValidator\Result\ValidEmail;
use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;

abstract class Parser
{
/**
* @var Warning\Warning[]
*/
protected $warnings = [];

/**
* @var EmailLexer
*/
protected $lexer;

/**
* id-left "@" id-right
*/
abstract protected function parseRightFromAt() : Result;
abstract protected function parseLeftFromAt() : Result;
abstract protected function preLeftParsing() : Result;


public function __construct(EmailLexer $lexer)
{
$this->lexer = $lexer;
}

public function parse(string $str) : Result
{
$this->lexer->setInput($str);

if ($this->lexer->hasInvalidTokens()) {
return new InvalidEmail(new ExpectingATEXT("Invalid tokens found"), $this->lexer->token["value"]);
}

$preParsingResult = $this->preLeftParsing();
if ($preParsingResult->isInvalid()) {
return $preParsingResult;
}

$localPartResult = $this->parseLeftFromAt();

if ($localPartResult->isInvalid()) {
return $localPartResult;
}

$domainPartResult = $this->parseRightFromAt();

if ($domainPartResult->isInvalid()) {
return $domainPartResult;
}

return new ValidEmail();
}

/**
* @return Warning\Warning[]
*/
public function getWarnings() : array
{
return $this->warnings;
}

protected function hasAtToken() : bool
{
$this->lexer->moveNext();
$this->lexer->moveNext();

return $this->lexer->token['type'] !== EmailLexer::S_AT;
}
}
2 changes: 1 addition & 1 deletion src/Parser/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use Egulias\EmailValidator\Result\Reason\UnOpenedComment;
use Egulias\EmailValidator\Warning\Comment as WarningComment;

class Comment extends Parser
class Comment extends PartParser
{
/**
* @var int
Expand Down
2 changes: 1 addition & 1 deletion src/Parser/DomainLiteral.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
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
{
Expand Down
Loading