mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-08-30 03:59:20 +00:00
Merge branch 'master' into 60-custom-exceptions
Conflicts: src/Egulias/EmailValidator/Parser/DomainPart.php src/Egulias/EmailValidator/Parser/LocalPart.php src/Egulias/EmailValidator/Parser/Parser.php tests/egulias/Tests/EmailValidator/EmailValidatorTest.php
This commit is contained in:
+1
-3
@@ -3,8 +3,6 @@ sudo: false
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
@@ -17,7 +15,7 @@ env:
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- php: 5.3
|
||||
- php: 5.5
|
||||
env: deps=low
|
||||
- php: 5.6
|
||||
env: deps=high
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">= 5.3.3",
|
||||
"php": ">= 5.5",
|
||||
"doctrine/lexer": "~1.0,>=1.0.1"
|
||||
},
|
||||
"require-dev" : {
|
||||
|
||||
Generated
+3
-3
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "9426f5a00cf0f71d83c287a7c0ff392b",
|
||||
"hash": "eaf5eb6f0b3811e996fb205760c9dd3f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "doctrine/lexer",
|
||||
@@ -1492,7 +1492,7 @@
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">= 5.3.3"
|
||||
"php": ">= 5.5"
|
||||
},
|
||||
"platform-dev": []
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,102 @@
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Parser\DomainPart;
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
/**
|
||||
* EmailParser
|
||||
*
|
||||
* @author Eduardo Gulias Davis <me@egulias.com>
|
||||
*/
|
||||
class EmailParser
|
||||
{
|
||||
const EMAIL_MAX_LENGTH = 254;
|
||||
protected warnings = [];
|
||||
protected domainPart = "";
|
||||
protected localPart = "";
|
||||
protected lexer;
|
||||
protected localPartParser;
|
||||
protected domainPartParser;
|
||||
public function __construct(<EmailLexer> lexer) -> void
|
||||
{
|
||||
let this->lexer = lexer;
|
||||
let this->localPartParser = new LocalPart(this->lexer);
|
||||
let this->domainPartParser = new DomainPart(this->lexer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $str
|
||||
* @return array
|
||||
*/
|
||||
public function parse(str) -> array
|
||||
{
|
||||
var tmpArray384eacd3233973d48b46f6665fe87aa5;
|
||||
|
||||
this->lexer->setInput(str);
|
||||
|
||||
if !this->hasAtToken() {
|
||||
throw new \InvalidArgumentException("ERR_NOLOCALPART");
|
||||
}
|
||||
this->localPartParser->parse(str);
|
||||
this->domainPartParser->parse(str);
|
||||
this->setParts(str);
|
||||
|
||||
if this->lexer->hasInvalidTokens() {
|
||||
throw new \InvalidArgumentException("ERR_INVALID_ATEXT");
|
||||
}
|
||||
let tmpArray384eacd3233973d48b46f6665fe87aa5 = ["local" : this->localPart, "domain" : this->domainPart];
|
||||
return tmpArray384eacd3233973d48b46f6665fe87aa5;
|
||||
}
|
||||
|
||||
public function getWarnings()
|
||||
{
|
||||
var localPartWarnings, domainPartWarnings;
|
||||
|
||||
let localPartWarnings = this->localPartParser->getWarnings();
|
||||
let domainPartWarnings = this->domainPartParser->getWarnings();
|
||||
let this->warnings = array_merge(localPartWarnings, domainPartWarnings);
|
||||
this->addLongEmailWarning(this->localPart, this->domainPart);
|
||||
|
||||
return this->warnings;
|
||||
}
|
||||
|
||||
public function getParsedDomainPart()
|
||||
{
|
||||
|
||||
return this->domainPart;
|
||||
}
|
||||
|
||||
protected function setParts(email) -> void
|
||||
{
|
||||
var parts;
|
||||
|
||||
let parts = explode("@", email);
|
||||
let this->domainPart = this->domainPartParser->getDomainPart();
|
||||
let this->localPart = parts[0];
|
||||
}
|
||||
|
||||
protected function hasAtToken()
|
||||
{
|
||||
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(string localPart, string parsedDomainPart) -> void
|
||||
{
|
||||
|
||||
if strlen(localPart . "@" . parsedDomainPart) > self::EMAIL_MAX_LENGTH {
|
||||
let this->warnings[] = EmailValidator::RFC5322_TOOLONG;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
/**
|
||||
* EmailValidator
|
||||
*
|
||||
* @author Eduardo Gulias Davis <me@egulias.com>
|
||||
*/
|
||||
class EmailValidator
|
||||
{
|
||||
const ERR_CONSECUTIVEATS = 128;
|
||||
const ERR_EXPECTING_DTEXT = 129;
|
||||
const ERR_NOLOCALPART = 130;
|
||||
const ERR_NODOMAIN = 131;
|
||||
const ERR_CONSECUTIVEDOTS = 132;
|
||||
const ERR_ATEXT_AFTER_CFWS = 133;
|
||||
const ERR_ATEXT_AFTER_QS = 134;
|
||||
const ERR_ATEXT_AFTER_DOMLIT = 135;
|
||||
const ERR_EXPECTING_QPAIR = 136;
|
||||
const ERR_EXPECTING_ATEXT = 137;
|
||||
const ERR_EXPECTING_QTEXT = 138;
|
||||
const ERR_EXPECTING_CTEXT = 139;
|
||||
const ERR_BACKSLASHEND = 140;
|
||||
const ERR_DOT_START = 141;
|
||||
const ERR_DOT_END = 142;
|
||||
const ERR_DOMAINHYPHENSTART = 143;
|
||||
const ERR_DOMAINHYPHENEND = 144;
|
||||
const ERR_UNCLOSEDQUOTEDSTR = 145;
|
||||
const ERR_UNCLOSEDCOMMENT = 146;
|
||||
const ERR_UNCLOSEDDOMLIT = 147;
|
||||
const ERR_FWS_CRLF_X2 = 148;
|
||||
const ERR_FWS_CRLF_END = 149;
|
||||
const ERR_CR_NO_LF = 150;
|
||||
const ERR_DEPREC_REACHED = 151;
|
||||
const RFC5321_TLD = 9;
|
||||
const RFC5321_TLDNUMERIC = 10;
|
||||
const RFC5321_QUOTEDSTRING = 11;
|
||||
const RFC5321_ADDRESSLITERAL = 12;
|
||||
const RFC5321_IPV6DEPRECATED = 13;
|
||||
const CFWS_COMMENT = 17;
|
||||
const CFWS_FWS = 18;
|
||||
const DEPREC_LOCALPART = 33;
|
||||
const DEPREC_FWS = 34;
|
||||
const DEPREC_QTEXT = 35;
|
||||
const DEPREC_QP = 36;
|
||||
const DEPREC_COMMENT = 37;
|
||||
const DEPREC_CTEXT = 38;
|
||||
const DEPREC_CFWS_NEAR_AT = 49;
|
||||
const RFC5322_LOCAL_TOOLONG = 64;
|
||||
const RFC5322_LABEL_TOOLONG = 63;
|
||||
const RFC5322_DOMAIN = 65;
|
||||
const RFC5322_TOOLONG = 66;
|
||||
const RFC5322_DOMAIN_TOOLONG = 255;
|
||||
const RFC5322_DOMAINLITERAL = 70;
|
||||
const RFC5322_DOMLIT_OBSDTEXT = 71;
|
||||
const RFC5322_IPV6_GRPCOUNT = 72;
|
||||
const RFC5322_IPV6_2X2XCOLON = 73;
|
||||
const RFC5322_IPV6_BADCHAR = 74;
|
||||
const RFC5322_IPV6_MAXGRPS = 75;
|
||||
const RFC5322_IPV6_COLONSTRT = 76;
|
||||
const RFC5322_IPV6_COLONEND = 77;
|
||||
const DNSWARN_NO_MX_RECORD = 5;
|
||||
const DNSWARN_NO_RECORD = 6;
|
||||
protected parser;
|
||||
protected warnings = [];
|
||||
protected error;
|
||||
protected threshold = 255;
|
||||
public function __construct() -> void
|
||||
{
|
||||
let this->parser = new EmailParser(new EmailLexer());
|
||||
}
|
||||
|
||||
public function isValid(email, checkDNS = false, strict = false)
|
||||
{
|
||||
var e, rClass, dns, error;
|
||||
|
||||
try {
|
||||
this->parser->parse((string) email);
|
||||
let this->warnings = this->parser->getWarnings();
|
||||
} catch \Exception, e {
|
||||
let rClass = new \ReflectionClass(this);
|
||||
let this->error = rClass->getConstant(e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
let dns = true;
|
||||
|
||||
if checkDNS {
|
||||
let dns = this->checkDNS();
|
||||
}
|
||||
|
||||
if this->hasWarnings() && (int) max(this->warnings) > this->threshold {
|
||||
let this->error = self::ERR_DEPREC_REACHED;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return !strict || !this->hasWarnings() && dns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasWarnings() -> boolean
|
||||
{
|
||||
|
||||
return !empty(this->warnings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getWarnings() -> array
|
||||
{
|
||||
|
||||
return this->warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getError() -> string
|
||||
{
|
||||
|
||||
return this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $threshold
|
||||
*
|
||||
* @return EmailValidator
|
||||
*/
|
||||
public function setThreshold(int threshold)
|
||||
{
|
||||
let this->threshold = (int) threshold;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getThreshold() -> int
|
||||
{
|
||||
|
||||
return this->threshold;
|
||||
}
|
||||
|
||||
protected function checkDNS()
|
||||
{
|
||||
var checked, result;
|
||||
|
||||
let checked = true;
|
||||
let result = checkdnsrr(trim(this->parser->getParsedDomainPart()), "MX");
|
||||
|
||||
if !result {
|
||||
let this->warnings[] = self::DNSWARN_NO_RECORD;
|
||||
let checked = false;
|
||||
this->addTLDWarnings();
|
||||
}
|
||||
|
||||
return checked;
|
||||
}
|
||||
|
||||
protected function addTLDWarnings() -> void
|
||||
{
|
||||
|
||||
if !in_array(self::DNSWARN_NO_RECORD, this->warnings) && !in_array(self::DNSWARN_NO_MX_RECORD, this->warnings) && in_array(self::RFC5322_DOMAINLITERAL, this->warnings) {
|
||||
let this->warnings[] = self::RFC5321_TLD;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Parser\Parser;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
class DomainPart extends Parser
|
||||
{
|
||||
const DOMAIN_MAX_LENGTH = 254;
|
||||
protected domainPart = "";
|
||||
public function parse(domainPart) -> void
|
||||
{
|
||||
var domain, prev, length;
|
||||
|
||||
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_HYPHEN {
|
||||
throw new \InvalidArgumentException("ERR_DOMAINHYPHENEND");
|
||||
}
|
||||
|
||||
if this->lexer->token["type"] === EmailLexer::S_OPENPARENTHESIS {
|
||||
let this->warnings[] = EmailValidator::DEPREC_COMMENT;
|
||||
this->parseDomainComments();
|
||||
}
|
||||
let domain = this->doParseDomainPart();
|
||||
let prev = this->lexer->getPrevious();
|
||||
let length = strlen(domain);
|
||||
|
||||
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 > self::DOMAIN_MAX_LENGTH {
|
||||
let this->warnings[] = EmailValidator::RFC5322_DOMAIN_TOOLONG;
|
||||
}
|
||||
|
||||
if prev["type"] === EmailLexer::S_CR {
|
||||
throw new \InvalidArgumentException("ERR_FWS_CRLF_END");
|
||||
}
|
||||
let this->domainPart = domain;
|
||||
}
|
||||
|
||||
public function getDomainPart()
|
||||
{
|
||||
|
||||
return this->domainPart;
|
||||
}
|
||||
|
||||
public function checkIPV6Tag(addressLiteral, maxGroups = 8)
|
||||
{
|
||||
var prev, IPv6, matchesIP, groupCount, colons;
|
||||
|
||||
let prev = this->lexer->getPrevious();
|
||||
|
||||
if prev["type"] === EmailLexer::S_COLON {
|
||||
let this->warnings[] = EmailValidator::RFC5322_IPV6_COLONEND;
|
||||
}
|
||||
let IPv6 = substr(addressLiteral, 5);
|
||||
//Daniel Marschall's new IPv6 testing strategy
|
||||
let matchesIP = explode(":", IPv6);
|
||||
let groupCount = count(matchesIP);
|
||||
let colons = strpos(IPv6, "::");
|
||||
|
||||
if count(preg_grep("/^[0-9A-Fa-f]{0,4}$/", matchesIP, PREG_GREP_INVERT)) !== 0 {
|
||||
let this->warnings[] = EmailValidator::RFC5322_IPV6_BADCHAR;
|
||||
}
|
||||
|
||||
if colons === false {
|
||||
// We need exactly the right number of groups
|
||||
|
||||
if groupCount !== maxGroups {
|
||||
let this->warnings[] = EmailValidator::RFC5322_IPV6_GRPCOUNT;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if colons !== strrpos(IPv6, "::") {
|
||||
let 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
|
||||
let maxGroups++;
|
||||
}
|
||||
|
||||
if groupCount > maxGroups {
|
||||
let this->warnings[] = EmailValidator::RFC5322_IPV6_MAXGRPS;
|
||||
} elseif groupCount === maxGroups {
|
||||
let this->warnings[] = EmailValidator::RFC5321_IPV6DEPRECATED;
|
||||
}
|
||||
}
|
||||
|
||||
protected function doParseDomainPart()
|
||||
{
|
||||
var domain, prev;
|
||||
|
||||
let domain = "";
|
||||
do {
|
||||
let prev = this->lexer->getPrevious();
|
||||
|
||||
if this->lexer->token["type"] === EmailLexer::S_SLASH {
|
||||
throw new \InvalidArgumentException("ERR_DOMAIN_CHAR_NOT_ALLOWED");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
let domain .= this->lexer->token["value"];
|
||||
this->lexer->moveNext();
|
||||
} while (this->lexer->token);
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
protected function parseDomainLiteral()
|
||||
{
|
||||
var lexer;
|
||||
|
||||
|
||||
if this->lexer->isNextToken(EmailLexer::S_COLON) {
|
||||
let this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
|
||||
}
|
||||
|
||||
if this->lexer->isNextToken(EmailLexer::S_IPV6TAG) {
|
||||
let lexer = clone this->lexer;
|
||||
lexer->moveNext();
|
||||
|
||||
if lexer->isNextToken(EmailLexer::S_DOUBLECOLON) {
|
||||
let this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
|
||||
}
|
||||
}
|
||||
|
||||
return this->doParseDomainLiteral();
|
||||
}
|
||||
|
||||
protected function doParseDomainLiteral()
|
||||
{
|
||||
var IPv6TAG, addressLiteral, tmpArray660bd629014c38ffb14ea5f4192457b6, tmpArray9934a9b75899ddf8df794ea12f1b354a;
|
||||
|
||||
let IPv6TAG = false;
|
||||
let 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 {
|
||||
let this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
|
||||
}
|
||||
let tmpArray660bd629014c38ffb14ea5f4192457b6 = [EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET];
|
||||
if this->lexer->isNextTokenAny(tmpArray660bd629014c38ffb14ea5f4192457b6) {
|
||||
throw new \InvalidArgumentException("ERR_EXPECTING_DTEXT");
|
||||
}
|
||||
let tmpArray9934a9b75899ddf8df794ea12f1b354a = [EmailLexer::S_HTAB, EmailLexer::S_SP, this->lexer->token["type"] === EmailLexer::CRLF];
|
||||
if this->lexer->isNextTokenAny(tmpArray9934a9b75899ddf8df794ea12f1b354a) {
|
||||
let 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 {
|
||||
let this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
|
||||
let addressLiteral .= this->lexer->token["value"];
|
||||
this->lexer->moveNext();
|
||||
this->validateQuotedPair();
|
||||
}
|
||||
|
||||
if this->lexer->token["type"] === EmailLexer::S_IPV6TAG {
|
||||
let IPv6TAG = true;
|
||||
}
|
||||
|
||||
if this->lexer->token["type"] === EmailLexer::S_CLOSEQBRACKET {
|
||||
break;
|
||||
}
|
||||
let addressLiteral .= this->lexer->token["value"];
|
||||
} while (this->lexer->moveNext());
|
||||
let addressLiteral = str_replace("[", "", addressLiteral);
|
||||
let addressLiteral = this->checkIPV4Tag(addressLiteral);
|
||||
|
||||
if addressLiteral === false {
|
||||
|
||||
return addressLiteral;
|
||||
}
|
||||
|
||||
if !IPv6TAG {
|
||||
let this->warnings[] = EmailValidator::RFC5322_DOMAINLITERAL;
|
||||
|
||||
return addressLiteral;
|
||||
}
|
||||
let this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
|
||||
this->checkIPV6Tag(addressLiteral);
|
||||
|
||||
return addressLiteral;
|
||||
}
|
||||
|
||||
protected function checkIPV4Tag(addressLiteral)
|
||||
{
|
||||
var matchesIP, index;
|
||||
|
||||
|
||||
let matchesIP = [];
|
||||
// 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 {
|
||||
let index = strrpos(addressLiteral, matchesIP[0]);
|
||||
|
||||
if index === 0 {
|
||||
let this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
|
||||
|
||||
return false;
|
||||
}
|
||||
// Convert IPv4 part to IPv6 format for further testing
|
||||
let addressLiteral = substr(addressLiteral, 0, index) . "0:0";
|
||||
}
|
||||
|
||||
return addressLiteral;
|
||||
}
|
||||
|
||||
protected function checkDomainPartExceptions(prev) -> void
|
||||
{
|
||||
var invalidDomainTokens;
|
||||
|
||||
|
||||
let invalidDomainTokens = [EmailLexer::S_DQUOTE : true, EmailLexer::S_SEMICOLON : true, EmailLexer::S_GREATERTHAN : true, EmailLexer::S_LOWERTHAN : true];
|
||||
|
||||
if isset var tmpArray;
|
||||
this->lexer->token["type"];
|
||||
invalidDomainTokens[this->lexer->token] {
|
||||
throw new \InvalidArgumentException("ERR_EXPECTING_ATEXT");
|
||||
}
|
||||
|
||||
if this->lexer->token["type"] === EmailLexer::S_COMMA {
|
||||
throw new \InvalidArgumentException("ERR_COMMA_IN_DOMAIN");
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
var e;
|
||||
|
||||
|
||||
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) -> void
|
||||
{
|
||||
|
||||
if this->lexer->token["type"] === EmailLexer::S_DOT && prev["type"] === EmailLexer::GENERIC && strlen(prev["value"]) > 63 {
|
||||
let this->warnings[] = EmailValidator::RFC5322_LABEL_TOOLONG;
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseDomainComments() -> void
|
||||
{
|
||||
this->isUnclosedComment();
|
||||
|
||||
while (!this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
|
||||
this->warnEscaping();
|
||||
this->lexer->moveNext();
|
||||
|
||||
}
|
||||
this->lexer->moveNext();
|
||||
|
||||
if this->lexer->isNextToken(EmailLexer::S_DOT) {
|
||||
throw new \InvalidArgumentException("ERR_EXPECTING_ATEXT");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use InvalidArgumentException;
|
||||
class LocalPart extends Parser
|
||||
{
|
||||
public function parse(localPart) -> void
|
||||
{
|
||||
var parseDQuote, closingQuote, prev;
|
||||
|
||||
let parseDQuote = true;
|
||||
let 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");
|
||||
}
|
||||
let closingQuote = this->checkDQUOTE(closingQuote);
|
||||
|
||||
if closingQuote && parseDQuote {
|
||||
let parseDQuote = this->parseDoubleQuote();
|
||||
}
|
||||
|
||||
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();
|
||||
this->isInvalidToken(this->lexer->token, closingQuote);
|
||||
|
||||
if this->isFWS() {
|
||||
this->parseFWS();
|
||||
}
|
||||
this->lexer->moveNext();
|
||||
|
||||
}
|
||||
let prev = this->lexer->getPrevious();
|
||||
|
||||
if strlen(prev["value"]) > EmailValidator::RFC5322_LOCAL_TOOLONG {
|
||||
let this->warnings[] = EmailValidator::RFC5322_LOCAL_TOOLONG;
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseDoubleQuote()
|
||||
{
|
||||
var parseAgain, special, invalid, setSpecialsWarning, prev;
|
||||
|
||||
let parseAgain = true;
|
||||
|
||||
let special = [EmailLexer::S_CR : true, EmailLexer::S_HTAB : true, EmailLexer::S_LF : true];
|
||||
|
||||
let invalid = [EmailLexer::C_NUL : true, EmailLexer::S_HTAB : true, EmailLexer::S_CR : true, EmailLexer::S_LF : true];
|
||||
let setSpecialsWarning = true;
|
||||
this->lexer->moveNext();
|
||||
|
||||
while (this->lexer->token["type"] !== EmailLexer::S_DQUOTE && this->lexer->token) {
|
||||
let parseAgain = false;
|
||||
|
||||
if isset var tmpArray;
|
||||
this->lexer->token["type"];
|
||||
special[this->lexer->token] && setSpecialsWarning {
|
||||
let this->warnings[] = EmailValidator::CFWS_FWS;
|
||||
let setSpecialsWarning = false;
|
||||
}
|
||||
this->lexer->moveNext();
|
||||
|
||||
if !this->escaped() && isset var tmpArray;
|
||||
this->lexer->token["type"];
|
||||
invalid[this->lexer->token] {
|
||||
throw new InvalidArgumentException("ERR_EXPECTED_ATEXT");
|
||||
}
|
||||
|
||||
}
|
||||
let prev = this->lexer->getPrevious();
|
||||
|
||||
if prev["type"] === EmailLexer::S_BACKSLASH {
|
||||
|
||||
if !this->checkDQUOTE(false) {
|
||||
throw new \InvalidArgumentException("ERR_UNCLOSED_DQUOTE");
|
||||
}
|
||||
}
|
||||
|
||||
if !this->lexer->isNextToken(EmailLexer::S_AT) && prev["type"] !== EmailLexer::S_BACKSLASH {
|
||||
throw new \InvalidArgumentException("ERR_EXPECED_AT");
|
||||
}
|
||||
|
||||
return parseAgain;
|
||||
}
|
||||
|
||||
protected function isInvalidToken(token, closingQuote) -> void
|
||||
{
|
||||
var forbidden;
|
||||
|
||||
|
||||
let forbidden = [EmailLexer::S_COMMA, EmailLexer::S_CLOSEBRACKET, EmailLexer::S_OPENBRACKET, EmailLexer::S_GREATERTHAN, EmailLexer::S_LOWERTHAN, EmailLexer::S_COLON, EmailLexer::S_SEMICOLON, EmailLexer::INVALID];
|
||||
|
||||
if in_array(token["type"], forbidden) && !closingQuote {
|
||||
throw new \InvalidArgumentException("ERR_EXPECTING_ATEXT");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
abstract class Parser
|
||||
{
|
||||
protected warnings = [];
|
||||
protected lexer;
|
||||
public function __construct(<EmailLexer> lexer) -> void
|
||||
{
|
||||
let this->lexer = lexer;
|
||||
}
|
||||
|
||||
public function getWarnings()
|
||||
{
|
||||
|
||||
return this->warnings;
|
||||
}
|
||||
|
||||
abstract function parse(str) -> void;
|
||||
|
||||
/**
|
||||
* validateQuotedPair
|
||||
*/
|
||||
protected function validateQuotedPair() -> void
|
||||
{
|
||||
|
||||
if !(this->lexer->token["type"] === EmailLexer::INVALID || this->lexer->token["type"] === EmailLexer::C_DEL) {
|
||||
throw new \InvalidArgumentException("ERR_EXPECTING_QPAIR");
|
||||
}
|
||||
let this->warnings[] = EmailValidator::DEPREC_QP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the the comment
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function parseComments() -> string
|
||||
{
|
||||
var tmpArray63f0c3c6be7adf1793a82cc46795d34e;
|
||||
|
||||
this->isUnclosedComment();
|
||||
let this->warnings[] = EmailValidator::CFWS_COMMENT;
|
||||
|
||||
while (!this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
|
||||
this->warnEscaping();
|
||||
this->lexer->moveNext();
|
||||
|
||||
}
|
||||
this->lexer->moveNext();
|
||||
let tmpArray63f0c3c6be7adf1793a82cc46795d34e = [EmailLexer::GENERIC, EmailLexer::S_EMPTY];
|
||||
if this->lexer->isNextTokenAny(tmpArray63f0c3c6be7adf1793a82cc46795d34e) {
|
||||
throw new \InvalidArgumentException("ERR_EXPECTING_ATEXT");
|
||||
}
|
||||
|
||||
if this->lexer->isNextToken(EmailLexer::S_AT) {
|
||||
let this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
|
||||
}
|
||||
}
|
||||
|
||||
protected function isUnclosedComment()
|
||||
{
|
||||
var e;
|
||||
|
||||
try {
|
||||
this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
|
||||
|
||||
return true;
|
||||
} catch \RuntimeException, e {
|
||||
throw new \InvalidArgumentException("ERR_UNCLOSEDCOMMENT");
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseFWS() -> void
|
||||
{
|
||||
var previous;
|
||||
|
||||
let 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 {
|
||||
let this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
|
||||
} else {
|
||||
let this->warnings[] = EmailValidator::CFWS_FWS;
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkConsecutiveDots() -> void
|
||||
{
|
||||
|
||||
if this->lexer->token["type"] === EmailLexer::S_DOT && this->lexer->isNextToken(EmailLexer::S_DOT) {
|
||||
throw new \InvalidArgumentException("ERR_CONSECUTIVEDOTS");
|
||||
}
|
||||
}
|
||||
|
||||
protected function isFWS()
|
||||
{
|
||||
|
||||
if this->escaped() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 escaped()
|
||||
{
|
||||
var previous;
|
||||
|
||||
let previous = this->lexer->getPrevious();
|
||||
|
||||
if previous["type"] === EmailLexer::S_BACKSLASH && this->lexer->token["type"] !== EmailLexer::GENERIC {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function warnEscaping()
|
||||
{
|
||||
var tmpArray612d23a3c73245450bf1b63fe4fddaa8;
|
||||
|
||||
|
||||
if this->lexer->token["type"] !== EmailLexer::S_BACKSLASH {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if this->lexer->isNextToken(EmailLexer::GENERIC) {
|
||||
throw new \InvalidArgumentException("ERR_EXPECTING_ATEXT");
|
||||
}
|
||||
let tmpArray612d23a3c73245450bf1b63fe4fddaa8 = [EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL];
|
||||
if !this->lexer->isNextTokenAny(tmpArray612d23a3c73245450bf1b63fe4fddaa8) {
|
||||
|
||||
return false;
|
||||
}
|
||||
let this->warnings[] = EmailValidator::DEPREC_QP;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkDQUOTE(hasClosingQuote)
|
||||
{
|
||||
var previous, e;
|
||||
|
||||
|
||||
if this->lexer->token["type"] !== EmailLexer::S_DQUOTE {
|
||||
|
||||
return hasClosingQuote;
|
||||
}
|
||||
|
||||
if hasClosingQuote {
|
||||
|
||||
return hasClosingQuote;
|
||||
}
|
||||
let previous = this->lexer->getPrevious();
|
||||
|
||||
if this->lexer->isNextToken(EmailLexer::GENERIC) && previous["type"] === EmailLexer::GENERIC {
|
||||
throw new \InvalidArgumentException("ERR_EXPECTING_ATEXT");
|
||||
}
|
||||
let this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING;
|
||||
try {
|
||||
this->lexer->find(EmailLexer::S_DQUOTE);
|
||||
let hasClosingQuote = true;
|
||||
} catch \Exception, e {
|
||||
throw new \InvalidArgumentException("ERR_UNCLOSEDQUOTEDSTR");
|
||||
}
|
||||
|
||||
return hasClosingQuote;
|
||||
}
|
||||
|
||||
protected function checkCRLFInFWS()
|
||||
{
|
||||
var tmpArray1ece19b1ca3512633175a37153944cbe;
|
||||
|
||||
|
||||
if this->lexer->token["type"] !== EmailLexer::CRLF {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if this->lexer->isNextToken(EmailLexer::CRLF) {
|
||||
throw new \InvalidArgumentException("ERR_FWS_CRLF_X2");
|
||||
}
|
||||
let tmpArray1ece19b1ca3512633175a37153944cbe = [EmailLexer::S_SP, EmailLexer::S_HTAB];
|
||||
if !this->lexer->isNextTokenAny(tmpArray1ece19b1ca3512633175a37153944cbe) {
|
||||
throw new \InvalidArgumentException("ERR_FWS_CRLF_END");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use Egulias\EmailValidator\InvalidEmail;
|
||||
class EmailValidator
|
||||
{
|
||||
const ERR_DEPREC_REACHED = 151;
|
||||
const ERR_UNOPENEDCOMMENT = 152;
|
||||
const RFC5321_TLD = 9;
|
||||
const RFC5321_TLDNUMERIC = 10;
|
||||
const RFC5321_QUOTEDSTRING = 11;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Exception;
|
||||
|
||||
use Egulias\EmailValidator\InvalidEmail;
|
||||
|
||||
class UnopenedComment extends InvalidEmail
|
||||
{
|
||||
const CODE = 152;
|
||||
const REASON = "No opening comment token found";
|
||||
}
|
||||
@@ -10,12 +10,11 @@ use Egulias\EmailValidator\Exception\CRNoLF;
|
||||
use Egulias\EmailValidator\Exception\DomainHyphened;
|
||||
use Egulias\EmailValidator\Exception\DotAtEnd;
|
||||
use Egulias\EmailValidator\Exception\DotAtStart;
|
||||
use Egulias\EmailValidator\Exception\ExpectedQPair;
|
||||
use Egulias\EmailValidator\Exception\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Exception\ExpectingDTEXT;
|
||||
use Egulias\EmailValidator\Exception\NoDomainPart;
|
||||
use Egulias\EmailValidator\Parser\Parser;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Exception\UnopenedComment;
|
||||
|
||||
class DomainPart extends Parser
|
||||
{
|
||||
@@ -113,8 +112,8 @@ class DomainPart extends Parser
|
||||
protected function doParseDomainPart()
|
||||
{
|
||||
$domain = '';
|
||||
$openedParenthesis = 0;
|
||||
do {
|
||||
|
||||
$prev = $this->lexer->getPrevious();
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_SLASH) {
|
||||
@@ -123,7 +122,19 @@ class DomainPart extends Parser
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->parseComments();
|
||||
$openedParenthesis += $this->getOpenedParenthesis();
|
||||
$this->lexer->moveNext();
|
||||
$tmpPrev = $this->lexer->getPrevious();
|
||||
if ($tmpPrev['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
||||
$openedParenthesis--;
|
||||
}
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
||||
if ($openedParenthesis === 0) {
|
||||
throw new UnopenedComment();
|
||||
} else {
|
||||
$openedParenthesis--;
|
||||
}
|
||||
}
|
||||
|
||||
$this->checkConsecutiveDots();
|
||||
@@ -192,6 +203,7 @@ class DomainPart extends Parser
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
|
||||
throw new CRNoLF();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
|
||||
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
|
||||
$addressLiteral .= $this->lexer->token['value'];
|
||||
|
||||
@@ -7,7 +7,7 @@ use Egulias\EmailValidator\Exception\DotAtStart;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Exception\ExpectingATEXT;
|
||||
use \InvalidArgumentException;
|
||||
use Egulias\EmailValidator\Exception\UnopenedComment;
|
||||
|
||||
class LocalPart extends Parser
|
||||
{
|
||||
@@ -15,9 +15,9 @@ class LocalPart extends Parser
|
||||
{
|
||||
$parseDQuote = true;
|
||||
$closingQuote = false;
|
||||
$openedParenthesis = 0;
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
|
||||
throw new DotAtStart();
|
||||
}
|
||||
@@ -29,12 +29,19 @@ class LocalPart extends Parser
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->parseComments();
|
||||
$openedParenthesis += $this->getOpenedParenthesis();
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
||||
if ($openedParenthesis === 0) {
|
||||
throw new UnopenedComment();
|
||||
} else {
|
||||
$openedParenthesis--;
|
||||
}
|
||||
}
|
||||
|
||||
$this->checkConsecutiveDots();
|
||||
|
||||
if (
|
||||
$this->lexer->token['type'] === EmailLexer::S_DOT &&
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
|
||||
$this->lexer->isNextToken(EmailLexer::S_AT)
|
||||
) {
|
||||
throw new DotAtEnd();
|
||||
@@ -85,7 +92,7 @@ class LocalPart extends Parser
|
||||
$this->lexer->moveNext();
|
||||
|
||||
if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
|
||||
throw new InvalidArgumentException("ERR_EXPECTED_ATEXT");
|
||||
throw new \InvalidArgumentException('ERR_EXPECTED_ATEXT');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +100,12 @@ class LocalPart extends Parser
|
||||
|
||||
if ($prev['type'] === EmailLexer::S_BACKSLASH) {
|
||||
if (!$this->checkDQUOTE(false)) {
|
||||
throw new \InvalidArgumentException("ERR_UNCLOSED_DQUOTE");
|
||||
throw new \InvalidArgumentException('ERR_UNCLOSED_DQUOTE');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
|
||||
throw new \InvalidArgumentException("ERR_EXPECED_AT");
|
||||
throw new \InvalidArgumentException('ERR_EXPECED_AT');
|
||||
}
|
||||
|
||||
return $parseAgain;
|
||||
|
||||
@@ -19,6 +19,7 @@ abstract class Parser
|
||||
{
|
||||
protected $warnings = array();
|
||||
protected $lexer;
|
||||
protected $openedParenthesis = 0;
|
||||
|
||||
public function __construct(EmailLexer $lexer)
|
||||
{
|
||||
@@ -30,7 +31,13 @@ abstract class Parser
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
abstract function parse($str);
|
||||
abstract public function parse($str);
|
||||
|
||||
/** @return int */
|
||||
public function getOpenedParenthesis()
|
||||
{
|
||||
return $this->openedParenthesis;
|
||||
}
|
||||
|
||||
/**
|
||||
* validateQuotedPair
|
||||
@@ -45,15 +52,15 @@ abstract class Parser
|
||||
$this->warnings[] = EmailValidator::DEPREC_QP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the the comment
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function parseComments()
|
||||
{
|
||||
$this->openedParenthesis = 1;
|
||||
$this->isUnclosedComment();
|
||||
$this->warnings[] = EmailValidator::CFWS_COMMENT;
|
||||
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
|
||||
$this->openedParenthesis++;
|
||||
}
|
||||
$this->warnEscaping();
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
@@ -190,9 +197,11 @@ abstract class Parser
|
||||
if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
|
||||
return;
|
||||
}
|
||||
if ($this->lexer->isNextToken(EmailLexer::CRLF)) {
|
||||
|
||||
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
|
||||
throw new CRLFX2();
|
||||
}
|
||||
|
||||
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
|
||||
throw new CRLFAtTheEnd();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use Egulias\EmailValidator\Exception\NoDomainPart;
|
||||
use Egulias\EmailValidator\Exception\NoLocalPart;
|
||||
use Egulias\EmailValidator\Exception\UnclosedComment;
|
||||
use Egulias\EmailValidator\Exception\UnclosedQuotedString;
|
||||
use Egulias\EmailValidator\Exception\UnopenedComment;
|
||||
|
||||
class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
@@ -167,6 +168,12 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
array(UnclosedComment::CODE, '(example@localhost'),
|
||||
array(UnclosedQuotedString::CODE, '"example@localhost'),
|
||||
array(ExpectingATEXT::CODE, 'exa"mple@localhost'),
|
||||
array(UnclosedComment::CODE, '(example@localhost'),
|
||||
array(UnopenedComment::CODE, 'comment)example@localhost'),
|
||||
array(UnopenedComment::CODE, 'example(comment))@localhost'),
|
||||
array(UnopenedComment::CODE, 'example@comment)localhost'),
|
||||
array(UnopenedComment::CODE, 'example@localhost(comment))'),
|
||||
array(UnopenedComment::CODE, '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"),
|
||||
array(AtextAfterCFWS::CODE, "exampl\ne@example.co.uk"),
|
||||
|
||||
Reference in New Issue
Block a user