mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-08-24 00:28:26 +00:00
FWS parser
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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"],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user