mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-09-12 02:56:50 +00:00
[WIP] - Chopping things down to make them more readable
This commit is contained in:
Generated
+39
-39
@@ -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": [
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user