[WIP] - Chopping things down to make them more readable

This commit is contained in:
Eduardo Gulias Davis
2014-06-17 12:03:30 +02:00
parent 045ebd2f8e
commit 8381eb3399
6 changed files with 575 additions and 500 deletions
Generated
+39 -39
View File
@@ -61,16 +61,16 @@
"packages-dev": [
{
"name": "guzzle/guzzle",
"version": "v3.9.0",
"version": "v3.9.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle3.git",
"reference": "e4d12d7d6acccf75a5e504114e2df9195e494108"
"reference": "92d9934f2fca1da15178c91239576ae26e505e60"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle3/zipball/e4d12d7d6acccf75a5e504114e2df9195e494108",
"reference": "e4d12d7d6acccf75a5e504114e2df9195e494108",
"url": "https://api.github.com/repos/guzzle/guzzle3/zipball/92d9934f2fca1da15178c91239576ae26e505e60",
"reference": "92d9934f2fca1da15178c91239576ae26e505e60",
"shasum": ""
},
"require": {
@@ -149,7 +149,7 @@
"rest",
"web service"
],
"time": "2014-04-23 21:43:40"
"time": "2014-05-07 17:04:22"
},
{
"name": "psr/log",
@@ -195,12 +195,12 @@
"source": {
"type": "git",
"url": "https://github.com/satooshi/php-coveralls.git",
"reference": "9fce7133a1d79b19f38a8fc975009703468172f0"
"reference": "b7271847c84d160f5b0aae83e45c225e8ffc96f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/9fce7133a1d79b19f38a8fc975009703468172f0",
"reference": "9fce7133a1d79b19f38a8fc975009703468172f0",
"url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/b7271847c84d160f5b0aae83e45c225e8ffc96f4",
"reference": "b7271847c84d160f5b0aae83e45c225e8ffc96f4",
"shasum": ""
},
"require": {
@@ -262,21 +262,21 @@
"github",
"test"
],
"time": "2014-03-27 16:05:26"
"time": "2014-05-14 13:09:37"
},
{
"name": "symfony/config",
"version": "v2.4.3",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Config",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
"reference": "e98693981385224bd31667e56204f311d9e0f98e"
"reference": "2effc67af6f21a0d267210b72d0b0b691d113528"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/e98693981385224bd31667e56204f311d9e0f98e",
"reference": "e98693981385224bd31667e56204f311d9e0f98e",
"url": "https://api.github.com/repos/symfony/Config/zipball/2effc67af6f21a0d267210b72d0b0b691d113528",
"reference": "2effc67af6f21a0d267210b72d0b0b691d113528",
"shasum": ""
},
"require": {
@@ -312,21 +312,21 @@
],
"description": "Symfony Config Component",
"homepage": "http://symfony.com",
"time": "2014-03-26 11:35:33"
"time": "2014-04-22 08:11:06"
},
{
"name": "symfony/console",
"version": "v2.4.3",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "ef20f1f58d7f693ee888353962bd2db336e3bbcb"
"reference": "24f723436e62598c9dddee2a8573d6992504dc5d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/ef20f1f58d7f693ee888353962bd2db336e3bbcb",
"reference": "ef20f1f58d7f693ee888353962bd2db336e3bbcb",
"url": "https://api.github.com/repos/symfony/Console/zipball/24f723436e62598c9dddee2a8573d6992504dc5d",
"reference": "24f723436e62598c9dddee2a8573d6992504dc5d",
"shasum": ""
},
"require": {
@@ -367,21 +367,21 @@
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com",
"time": "2014-03-01 17:35:04"
"time": "2014-05-14 21:48:29"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.4.3",
"version": "v2.4.5",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "4708b8cd41984a5ba29fe7dd40716f7f761ac501"
"reference": "e539602e5455aa086c0e81e604745af7789e4d8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/4708b8cd41984a5ba29fe7dd40716f7f761ac501",
"reference": "4708b8cd41984a5ba29fe7dd40716f7f761ac501",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e539602e5455aa086c0e81e604745af7789e4d8a",
"reference": "e539602e5455aa086c0e81e604745af7789e4d8a",
"shasum": ""
},
"require": {
@@ -423,21 +423,21 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2014-02-11 13:52:09"
"time": "2014-04-16 10:34:31"
},
{
"name": "symfony/filesystem",
"version": "v2.4.3",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "b717952487176a993e9b12a08b87beae25ae419c"
"reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/b717952487176a993e9b12a08b87beae25ae419c",
"reference": "b717952487176a993e9b12a08b87beae25ae419c",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/a3af8294bcce4a7c1b2892363b0c9d8109affad4",
"reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4",
"shasum": ""
},
"require": {
@@ -472,21 +472,21 @@
],
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com",
"time": "2014-03-26 11:55:03"
"time": "2014-04-16 10:34:31"
},
{
"name": "symfony/stopwatch",
"version": "v2.4.3",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Stopwatch",
"source": {
"type": "git",
"url": "https://github.com/symfony/Stopwatch.git",
"reference": "bffad325e36a3e71fba6d5dcce6e2f4b4637b91a"
"reference": "343bcc0360f2c22f371884b8f6a9fee8d1aa431a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/bffad325e36a3e71fba6d5dcce6e2f4b4637b91a",
"reference": "bffad325e36a3e71fba6d5dcce6e2f4b4637b91a",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/343bcc0360f2c22f371884b8f6a9fee8d1aa431a",
"reference": "343bcc0360f2c22f371884b8f6a9fee8d1aa431a",
"shasum": ""
},
"require": {
@@ -521,21 +521,21 @@
],
"description": "Symfony Stopwatch Component",
"homepage": "http://symfony.com",
"time": "2014-02-11 13:52:09"
"time": "2014-04-18 20:37:09"
},
{
"name": "symfony/yaml",
"version": "v2.4.3",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "77a41c2835ab7cfe8bf6d15e25d3af8f3eb3bacd"
"reference": "fd22bb88c3a6f73c898b39bec185a9e211b06265"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/77a41c2835ab7cfe8bf6d15e25d3af8f3eb3bacd",
"reference": "77a41c2835ab7cfe8bf6d15e25d3af8f3eb3bacd",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/fd22bb88c3a6f73c898b39bec185a9e211b06265",
"reference": "fd22bb88c3a6f73c898b39bec185a9e211b06265",
"shasum": ""
},
"require": {
@@ -570,7 +570,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2014-03-12 18:29:58"
"time": "2014-05-12 09:27:48"
}
],
"aliases": [
+31 -460
View File
@@ -2,6 +2,9 @@
namespace Egulias\EmailValidator;
use Egulias\EmailValidator\Parser\DomainPart;
use Egulias\EmailValidator\Parser\LocalPart;
/**
* EmailParser
*
@@ -9,47 +12,47 @@ namespace Egulias\EmailValidator;
*/
class EmailParser
{
protected $warnings = array();
protected $domainPart = '';
protected $lexer;
protected $localPartParser;
protected $domainPartParser;
public function __construct(EmailLexer $lexer)
{
$this->lexer = $lexer;
$this->localPartParser = new LocalPart($this->lexer);
$this->domainPartParser = new DomainPart($this->lexer);
}
/**
* @param $str
* @return array
* @throws \InvalidArgumentException
*/
public function parse($str)
{
$this->lexer->setInput($str);
$this->lexer->moveNext();
$this->lexer->moveNext();
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
if (!$this->hasAtToken()) {
throw new \InvalidArgumentException('ERR_NOLOCALPART');
}
$this->parseLocalPart();
$this->parseDomainPart();
$this->localPartParser->parse($str);
$this->domainPartParser->parse($str);
$this->domainPart = $this->domainPartParser->getDomainPart($str);
$parts = explode('@', $str);
if (strlen($parts[0] . '@' . $this->domainPart) > 254) {
$this->warnings[] = EmailValidator::RFC5322_TOOLONG;
}
$this->longEmailWarning($parts[0], $this->domainPart);
return array('local' => $parts[0], 'domain' => $this->domainPart);
}
/**
* getWarnings
*
* @return array
*/
public function getWarnings()
{
if (!$this->warnings) {
$localPartWarnings = $this->localPartParser->getWarnings();
$domainPartWarnings = $this->domainPartParser->getWarnings();
$this->warnings = array_merge($localPartWarnings, $domainPartWarnings);
}
return $this->warnings;
}
@@ -58,453 +61,21 @@ class EmailParser
return $this->domainPart;
}
/**
* parseDomainPart
*
*/
private function parseDomainPart()
protected function hasAtToken()
{
$this->lexer->moveNext();
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
throw new \InvalidArgumentException('ERR_DOT_START');
}
if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) {
throw new \InvalidArgumentException('ERR_NODOMAIN');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->warnings[] = EmailValidator::DEPREC_COMMENT;
$this->parseComments();
}
$domain = $this->doParseDomainPart();
$prev = $this->lexer->getPrevious();
$length = strlen($prev['value']);
if ($prev['type'] === EmailLexer::S_DOT) {
throw new \InvalidArgumentException('ERR_DOT_END');
}
if ($prev['type'] === EmailLexer::S_HYPHEN) {
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
}
if ($length > 254) {
$this->warnings[] = EmailValidator::RFC5322_DOMAIN_TOOLONG;
}
if ($prev['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException('ERR_FWS_CRLF_END');
}
$this->domainPart = $domain;
}
private function parseDomainLiteral()
{
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
$lexer = clone $this->lexer;
$lexer->moveNext();
if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
}
return $this->doParseDomainLiteral();
}
/**
* validateQuotedPair
*/
private function validateQuotedPair()
{
if (!($this->lexer->token['type'] === EmailLexer::INVALID
|| $this->lexer->token['type'] === EmailLexer::C_DEL)) {
throw new \InvalidArgumentException('ERR_EXPECTING_QPAIR');
}
$this->warnings[] = EmailValidator::DEPREC_QP;
}
private function parseLocalPart()
{
$closingQuote = false;
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
throw new \InvalidArgumentException('ERR_DOT_START');
}
$closingQuote = $this->checkDQUOTE($closingQuote);
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments();
}
$this->checkConsecutiveDots();
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_AT)) {
throw new \InvalidArgumentException('ERR_DOT_END');
}
$this->warnEscaping();
if ($this->lexer->isNextTokenAny(
array(
EmailLexer::INVALID, EmailLexer::S_LOWERTHAN, EmailLexer::S_GREATERTHAN
)
)
) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->isFWS()) {
$this->parseFWS();
}
$this->lexer->moveNext();
}
$prev = $this->lexer->getPrevious();
if (strlen($prev['value']) > EmailValidator::RFC5322_LOCAL_TOOLONG) {
$this->warnings[] = EmailValidator::RFC5322_LOCAL_TOOLONG;
}
}
/**
* @return string the the comment
* @throws \InvalidArgumentException
*/
private function parseComments()
{
$this->warnings[] = EmailValidator::CFWS_COMMENT;
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
try {
$this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_UNCLOSEDCOMMENT');
}
$this->warnEscaping();
$this->lexer->moveNext();
}
$this->lexer->moveNext();
if ($this->lexer->isNextToken(EmailLexer::GENERIC) || EmailLexer::S_EMPTY === $this->lexer->peek()) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
}
}
private function parseFWS()
{
$previous = $this->lexer->getPrevious();
$this->checkCRLFInFWS();
if ($this->lexer->token['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException("ERR_CR_NO_LF");
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
throw new \InvalidArgumentException("ERR_ATEXT_AFTER_CFWS");
}
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new \InvalidArgumentException('ERR_EXPECTING_CTEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
} else {
$this->warnings[] = EmailValidator::CFWS_FWS;
}
}
private function checkConsecutiveDots()
{
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new \InvalidArgumentException('ERR_CONSECUTIVEDOTS');
}
}
private function isFWS()
{
if ($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 true;
}
return false;
}
private function warnEscaping()
{
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
return false;
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
return false;
}
$this->warnings[] = EmailValidator::DEPREC_QP;
return true;
}
private function checkDQUOTE($hasClosingQuote)
{
if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) {
return $hasClosingQuote;
}
if ($hasClosingQuote) {
return $hasClosingQuote;
}
$previous = $this->lexer->getPrevious();
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
$this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING;
try {
$this->lexer->find(EmailLexer::S_DQUOTE);
$hasClosingQuote = true;
} catch (\Exception $e) {
throw new \InvalidArgumentException('ERR_UNCLOSEDQUOTEDSTR');
}
return $hasClosingQuote;
}
private function checkCRLFInFWS()
{
if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
return;
}
if ($this->lexer->isNextToken(EmailLexer::CRLF)) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_X2");
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_END");
}
}
private function doParseDomainPart()
{
$domain = '';
do {
$prev = $this->lexer->getPrevious();
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments();
$this->lexer->moveNext();
}
$this->checkConsecutiveDots();
$this->checkDomainPartExceptions($prev);
if ($this->hasBrackets()) {
$this->parseDomainLiteral();
}
$this->checkLabelLength($prev);
if ($this->isFWS()) {
$this->parseFWS();
}
$domain .= $this->lexer->token['value'];
$this->lexer->moveNext();
} while ($this->lexer->token);
return $domain;
}
private function doParseDomainLiteral()
{
$IPv6TAG = false;
$addressLiteral = '';
do {
if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->token['type'] === EmailLexer::INVALID ||
$this->lexer->token['type'] === EmailLexer::C_DEL ||
$this->lexer->token['type'] === EmailLexer::S_LF
) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
}
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) {
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->isNextTokenAny(
array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
)) {
$this->warnings[] = EmailValidator::CFWS_FWS;
$this->parseFWS();
}
if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
throw new \InvalidArgumentException("ERR_CR_NO_LF");
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
$addressLiteral .= $this->lexer->token['value'];
$this->lexer->moveNext();
$this->validateQuotedPair();
}
if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
$IPv6TAG = true;
}
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) {
break;
}
$addressLiteral .= $this->lexer->token['value'];
} while ($this->lexer->moveNext());
$addressLiteral = str_replace('[', '', $addressLiteral);
$addressLiteral = $this->checkIPV4Tag($addressLiteral);
if (false === $addressLiteral) {
return $addressLiteral;
}
if (!$IPv6TAG) {
$this->warnings[] = EmailValidator::RFC5322_DOMAINLITERAL;
return $addressLiteral;
}
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
$this->checkIPV6Tag($addressLiteral);
return $addressLiteral;
}
private function checkIPV4Tag($addressLiteral)
{
$matchesIP = array();
// Extract IPv4 part from the end of the address-literal (if there is one)
if (preg_match(
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
$addressLiteral,
$matchesIP
) > 0
) {
$index = strrpos($addressLiteral, $matchesIP[0]);
if ($index === 0) {
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
return false;
}
// Convert IPv4 part to IPv6 format for further testing
$addressLiteral = substr($addressLiteral, 0, $index) . '0:0';
}
return $addressLiteral;
}
public function checkIPV6Tag($addressLiteral, $maxGroups = 8)
{
$prev = $this->lexer->getPrevious();
if ($prev['type'] === EmailLexer::S_COLON) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONEND;
}
$IPv6 = substr($addressLiteral, 5);
//Daniel Marschall's new IPv6 testing strategy
$matchesIP = explode(':', $IPv6);
$groupCount = count($matchesIP);
$colons = strpos($IPv6, '::');
if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_BADCHAR;
}
if ($colons === false) {
// We need exactly the right number of groups
if ($groupCount !== $maxGroups) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_GRPCOUNT;
}
return;
}
if ($colons !== strrpos($IPv6, '::')) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_2X2XCOLON;
return;
}
if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
// RFC 4291 allows :: at the start or end of an address
//with 7 other groups in addition
++$maxGroups;
}
if ($groupCount > $maxGroups) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_MAXGRPS;
} elseif ($groupCount === $maxGroups) {
$this->warnings[] = EmailValidator::RFC5321_IPV6DEPRECATED;
}
}
private function checkLabelLength($prev)
{
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
$prev['type'] === EmailLexer::GENERIC &&
strlen($prev['value']) > 63
) {
$this->warnings[] = EmailValidator::RFC5322_LABEL_TOOLONG;
}
}
private function hasBrackets()
{
if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) {
return false;
}
try {
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_EXPECTING_DOMLIT_CLOSE');
}
return true;
}
private function checkDomainPartExceptions($prev)
{
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
throw new \InvalidArgumentException('ERR_CONSECUTIVEATS');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
return false;
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
&& $this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
return true;
}
protected function longEmailWarning($localPart, $parsedDomainPart)
{
if (strlen($localPart . '@' . $parsedDomainPart) >254) {
$this->warnings[] = EmailValidator::RFC5322_TOOLONG;
}
}
}
@@ -2,6 +2,8 @@
namespace Egulias\EmailValidator;
use Egulias\EmailValidator\Parser\LocalPart;
/**
* EmailValidator
*
@@ -166,7 +168,7 @@ class EmailValidator
return $checked;
}
public function addTLDWarnings()
protected function addTLDWarnings()
{
if (!in_array(self::DNSWARN_NO_RECORD, $this->warnings) &&
!in_array(self::DNSWARN_NO_MX_RECORD, $this->warnings) &&
+165
View File
@@ -0,0 +1,165 @@
<?php
namespace Egulias\EmailValidator;
abstract class Parser
{
protected $warnings = array();
protected $lexer;
public function __construct(EmailLexer $lexer)
{
$this->lexer = $lexer;
}
public function getWarnings()
{
return $this->warnings;
}
abstract function parse($str);
/**
* validateQuotedPair
*/
protected function validateQuotedPair()
{
if (!($this->lexer->token['type'] === EmailLexer::INVALID
|| $this->lexer->token['type'] === EmailLexer::C_DEL)) {
throw new \InvalidArgumentException('ERR_EXPECTING_QPAIR');
}
$this->warnings[] = EmailValidator::DEPREC_QP;
}
/**
* @return string the the comment
* @throws \InvalidArgumentException
*/
protected function parseComments()
{
$this->warnings[] = EmailValidator::CFWS_COMMENT;
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
try {
$this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_UNCLOSEDCOMMENT');
}
$this->warnEscaping();
$this->lexer->moveNext();
}
$this->lexer->moveNext();
if ($this->lexer->isNextToken(EmailLexer::GENERIC) || EmailLexer::S_EMPTY === $this->lexer->peek()) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
}
}
protected function parseFWS()
{
$previous = $this->lexer->getPrevious();
$this->checkCRLFInFWS();
if ($this->lexer->token['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException("ERR_CR_NO_LF");
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
throw new \InvalidArgumentException("ERR_ATEXT_AFTER_CFWS");
}
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new \InvalidArgumentException('ERR_EXPECTING_CTEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
} else {
$this->warnings[] = EmailValidator::CFWS_FWS;
}
}
protected function checkConsecutiveDots()
{
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new \InvalidArgumentException('ERR_CONSECUTIVEDOTS');
}
}
protected function isFWS()
{
if ($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 true;
}
return false;
}
protected function warnEscaping()
{
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
return false;
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
return false;
}
$this->warnings[] = EmailValidator::DEPREC_QP;
return true;
}
protected function checkDQUOTE($hasClosingQuote)
{
if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) {
return $hasClosingQuote;
}
if ($hasClosingQuote) {
return $hasClosingQuote;
}
$previous = $this->lexer->getPrevious();
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
$this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING;
try {
$this->lexer->find(EmailLexer::S_DQUOTE);
$hasClosingQuote = true;
} catch (\Exception $e) {
throw new \InvalidArgumentException('ERR_UNCLOSEDQUOTEDSTR');
}
return $hasClosingQuote;
}
protected function checkCRLFInFWS()
{
if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
return;
}
if ($this->lexer->isNextToken(EmailLexer::CRLF)) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_X2");
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_END");
}
}
}
@@ -0,0 +1,277 @@
<?php
namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailValidator;
class DomainPart extends Parser
{
protected $domainPart = '';
public function parse($domainPart)
{
$this->lexer->moveNext();
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
throw new \InvalidArgumentException('ERR_DOT_START');
}
if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) {
throw new \InvalidArgumentException('ERR_NODOMAIN');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->warnings[] = EmailValidator::DEPREC_COMMENT;
$this->parseComments();
}
$domain = $this->doParseDomainPart();
$prev = $this->lexer->getPrevious();
$length = strlen($prev['value']);
if ($prev['type'] === EmailLexer::S_DOT) {
throw new \InvalidArgumentException('ERR_DOT_END');
}
if ($prev['type'] === EmailLexer::S_HYPHEN) {
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
}
if ($length > 254) {
$this->warnings[] = EmailValidator::RFC5322_DOMAIN_TOOLONG;
}
if ($prev['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException('ERR_FWS_CRLF_END');
}
$this->domainPart = $domain;
}
public function getDomainPart()
{
return $this->domainPart;
}
protected function doParseDomainPart()
{
$domain = '';
do {
$prev = $this->lexer->getPrevious();
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments();
$this->lexer->moveNext();
}
$this->checkConsecutiveDots();
$this->checkDomainPartExceptions($prev);
if ($this->hasBrackets()) {
$this->parseDomainLiteral();
}
$this->checkLabelLength($prev);
if ($this->isFWS()) {
$this->parseFWS();
}
$domain .= $this->lexer->token['value'];
$this->lexer->moveNext();
} while ($this->lexer->token);
return $domain;
}
protected function parseDomainLiteral()
{
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
$lexer = clone $this->lexer;
$lexer->moveNext();
if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
}
return $this->doParseDomainLiteral();
}
protected function doParseDomainLiteral()
{
$IPv6TAG = false;
$addressLiteral = '';
do {
if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->token['type'] === EmailLexer::INVALID ||
$this->lexer->token['type'] === EmailLexer::C_DEL ||
$this->lexer->token['type'] === EmailLexer::S_LF
) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
}
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) {
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->isNextTokenAny(
array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
)) {
$this->warnings[] = EmailValidator::CFWS_FWS;
$this->parseFWS();
}
if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
throw new \InvalidArgumentException("ERR_CR_NO_LF");
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
$addressLiteral .= $this->lexer->token['value'];
$this->lexer->moveNext();
$this->validateQuotedPair();
}
if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
$IPv6TAG = true;
}
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) {
break;
}
$addressLiteral .= $this->lexer->token['value'];
} while ($this->lexer->moveNext());
$addressLiteral = str_replace('[', '', $addressLiteral);
$addressLiteral = $this->checkIPV4Tag($addressLiteral);
if (false === $addressLiteral) {
return $addressLiteral;
}
if (!$IPv6TAG) {
$this->warnings[] = EmailValidator::RFC5322_DOMAINLITERAL;
return $addressLiteral;
}
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
$this->checkIPV6Tag($addressLiteral);
return $addressLiteral;
}
protected function checkIPV4Tag($addressLiteral)
{
$matchesIP = array();
// Extract IPv4 part from the end of the address-literal (if there is one)
if (preg_match(
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
$addressLiteral,
$matchesIP
) > 0
) {
$index = strrpos($addressLiteral, $matchesIP[0]);
if ($index === 0) {
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
return false;
}
// Convert IPv4 part to IPv6 format for further testing
$addressLiteral = substr($addressLiteral, 0, $index) . '0:0';
}
return $addressLiteral;
}
public function checkIPV6Tag($addressLiteral, $maxGroups = 8)
{
$prev = $this->lexer->getPrevious();
if ($prev['type'] === EmailLexer::S_COLON) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONEND;
}
$IPv6 = substr($addressLiteral, 5);
//Daniel Marschall's new IPv6 testing strategy
$matchesIP = explode(':', $IPv6);
$groupCount = count($matchesIP);
$colons = strpos($IPv6, '::');
if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_BADCHAR;
}
if ($colons === false) {
// We need exactly the right number of groups
if ($groupCount !== $maxGroups) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_GRPCOUNT;
}
return;
}
if ($colons !== strrpos($IPv6, '::')) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_2X2XCOLON;
return;
}
if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
// RFC 4291 allows :: at the start or end of an address
//with 7 other groups in addition
++$maxGroups;
}
if ($groupCount > $maxGroups) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_MAXGRPS;
} elseif ($groupCount === $maxGroups) {
$this->warnings[] = EmailValidator::RFC5321_IPV6DEPRECATED;
}
}
protected function checkDomainPartExceptions($prev)
{
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
throw new \InvalidArgumentException('ERR_CONSECUTIVEATS');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
&& $this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
}
protected function hasBrackets()
{
if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) {
return false;
}
try {
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_EXPECTING_DOMLIT_CLOSE');
}
return true;
}
protected function checkLabelLength($prev)
{
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
$prev['type'] === EmailLexer::GENERIC &&
strlen($prev['value']) > 63
) {
$this->warnings[] = EmailValidator::RFC5322_LABEL_TOOLONG;
}
}
}
@@ -0,0 +1,60 @@
<?php
namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailValidator;
/**
* Class LocalPartParser
* @author Eduardo Gulias Davis <eduardo.gulias@bodaclick.com>
@copyright 2014 Bodaclick */
class LocalPart extends Parser
{
public function parse($localPart)
{
$closingQuote = false;
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
throw new \InvalidArgumentException('ERR_DOT_START');
}
$closingQuote = $this->checkDQUOTE($closingQuote);
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments();
}
$this->checkConsecutiveDots();
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_AT)) {
throw new \InvalidArgumentException('ERR_DOT_END');
}
$this->warnEscaping();
if ($this->lexer->isNextTokenAny(
array(
EmailLexer::INVALID, EmailLexer::S_LOWERTHAN, EmailLexer::S_GREATERTHAN
)
)
) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->isFWS()) {
$this->parseFWS();
}
$this->lexer->moveNext();
}
$prev = $this->lexer->getPrevious();
if (strlen($prev['value']) > EmailValidator::RFC5322_LOCAL_TOOLONG) {
$this->warnings[] = EmailValidator::RFC5322_LOCAL_TOOLONG;
}
}
}