FWS parser

This commit is contained in:
Eduardo Gulias Davis
2020-05-23 16:21:45 +02:00
parent 3145070028
commit 302b98bf41
8 changed files with 198 additions and 5 deletions
@@ -0,0 +1,92 @@
<?php
namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Warning\CFWSNearAt;
use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Warning\CFWSWithFWS;
use Egulias\EmailValidator\Result\Reason\CRNoLF;
use Egulias\EmailValidator\Result\Reason\AtextAfterCFWS;
use Egulias\EmailValidator\Result\Reason\CRLFAtTheEnd;
use Egulias\EmailValidator\Result\Reason\CRLFX2;
use Egulias\EmailValidator\Result\Reason\ExpectingCTEXT;
use Egulias\EmailValidator\Result\ValidEmail;
class FoldingWhiteSpace extends Parser
{
public function parse($str)
{
if (!$this->isFWS()) {
return new ValidEmail();
}
$previous = $this->lexer->getPrevious();
$this->checkCRLFInFWS();
if ($this->lexer->token['type'] === EmailLexer::S_CR) {
return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']);
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
return new InvalidEmail(new AtextAfterCFWS(), $this->lexer->token['value']);
}
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
return new InvalidEmail(new ExpectingCTEXT(), $this->lexer->token['value']);
}
if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
$this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
} else {
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
}
return new ValidEmail();
}
protected function checkCRLFInFWS()
{
if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
return new ValidEmail();
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
return new InvalidEmail(new CRLFX2(), $this->lexer->token['value']);
}
//this has no coverage. Condition is repeated from above one
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
return new InvalidEmail(new CRLFAtTheEnd(), $this->lexer->token['value']);
}
}
/**
* @return bool
*/
protected function isFWS()
{
if ($this->escaped()) {
return false;
}
return $this->lexer->token['type'] === EmailLexer::S_SP ||
$this->lexer->token['type'] === EmailLexer::S_HTAB ||
$this->lexer->token['type'] === EmailLexer::S_CR ||
$this->lexer->token['type'] === EmailLexer::S_LF ||
$this->lexer->token['type'] === EmailLexer::CRLF;
}
/**
* @return bool
*/
protected function escaped()
{
$previous = $this->lexer->getPrevious();
return $previous && $previous['type'] === EmailLexer::S_BACKSLASH
&&
$this->lexer->token['type'] !== EmailLexer::GENERIC;
}
}
+18 -2
View File
@@ -26,9 +26,12 @@ class LocalPart extends Parser
EmailLexer::INVALID => EmailLexer::INVALID
);
private $foldingWS;
public function parse($localPart) : Result
{
$totalLength = 0;
$this->foldingWS = new FoldingWhiteSpace($this->lexer);
while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) {
if ($this->hasDotAtStart()) {
@@ -73,8 +76,9 @@ class LocalPart extends Parser
return new InvalidEmail(new ReasonExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
}
if ($this->isFWS()) {
$this->parseFWS();
$resultFWS = $this->parseLocalFWS();
if($resultFWS->isInvalid()) {
return $resultFWS;
}
$totalLength += strlen($this->lexer->token['value']);
@@ -88,6 +92,18 @@ class LocalPart extends Parser
return new ValidEmail();
}
protected function parseLocalFWS() : Result
{
$resultFWS = $this->foldingWS->parse('remove');
if ($resultFWS->isValid()) {
$warns = $this->foldingWS->getWarnings();
foreach ($warns as $code => $dWarning) {
$this->warnings[$code] = $dWarning;
}
}
return $resultFWS;
}
protected function hasDotAtStart() : bool
{
return $this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type'];
@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class AtextAfterCFWS implements Reason
{
public function code() : int
{
return 133;
}
public function description() : string
{
return 'ATEXT found after CFWS';
}
}
@@ -0,0 +1,19 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CRLFAtTheEnd implements Reason
{
const CODE = 149;
const REASON = "CRLF at the end";
public function code() : int
{
return 149;
}
public function description() : string
{
return 'CRLF at the end';
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CRLFX2 implements Reason
{
public function code() : int
{
return 148;
}
public function description() : string
{
return 'CR LF tokens found twice';
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CRNoLF implements Reason
{
public function code() : int
{
return 150;
}
public function description() : string
{
return 'Missing LF after CR';
}
}
@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class ExpectingCTEXT implements Reason
{
public function code() : int
{
return 139;
}
public function description() : string
{
return 'Expecting CTEXT';
}
}
@@ -36,6 +36,7 @@ use Egulias\EmailValidator\Exception\ExpectingDTEXT;
use Egulias\EmailValidator\Validation\RFCValidation;
use Egulias\EmailValidator\Exception\UnclosedComment;
use Egulias\EmailValidator\Exception\UnopenedComment as ExceptionUnopenedComment;
use Egulias\EmailValidator\Result\Reason\AtextAfterCFWS as ReasonAtextAfterCFWS;
use Egulias\EmailValidator\Result\Reason\ConsecutiveDot as ReasonConsecutiveDot;
use Egulias\EmailValidator\Result\Reason\DotAtEnd as ReasonDotAtEnd;
use Egulias\EmailValidator\Result\Reason\NoLocalPart;
@@ -44,6 +45,7 @@ use Egulias\EmailValidator\Result\Reason\DotAtStart as ReasonDotAtStart;
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT as ReasonExpectingATEXT;
use Egulias\EmailValidator\Result\Reason\UnclosedComment as ReasonUnclosedComment;
use Egulias\EmailValidator\Result\Reason\UnclosedQuotedString;
use Egulias\EmailValidator\Result\Reason\CRNoLF as ReasonCRNoLF;
class RFCValidationTest extends TestCase
{
@@ -217,12 +219,12 @@ class RFCValidationTest extends TestCase
[new ExceptionUnopenedComment, 'example@(comment))example.com'],
//This was the original. But atext is not allowed after \n
//array(EmailValidator::ERR_EXPECTING_ATEXT, "exampl\ne@example.co.uk"),
[new AtextAfterCFWS(), "exampl\ne@example.co.uk"],
[new InvalidEmail(new ReasonAtextAfterCFWS(), "\n"), "exampl\ne@example.co.uk"],
[new ExpectingDTEXT(), "example@[[]"],
[new AtextAfterCFWS(), "exampl\te@example.co.uk"],
[new InvalidEmail(new ReasonAtextAfterCFWS(), "\t"), "exampl\te@example.co.uk"],
[new CRNoLF(), "example@exa\rmple.co.uk"],
[new CRNoLF(), "example@[\r]"],
[new CRNoLF(), "exam\rple@example.co.uk"],
[new InvalidEmail(new ReasonCRNoLF(), "\r"), "exam\rple@example.co.uk"],
];
}