Compare commits

...

16 Commits

Author SHA1 Message Date
Sebastiaan Provost 5642614492 Backporting the changes made in #139 (#140)
* Backporting the changes made in https://github.com/egulias/EmailValidator/pull/139 so this functionality is also accessible in the 1.2.x releases.
This fix allows for more compliance with the DNS RFC. Explanation here: https://github.com/egulias/EmailValidator/pull/139/commits/08bac068c26edd210daa259a2819d7dfd7438091
2017-02-03 23:48:59 +01:00
Chris McCafferty b8bb147f46 Added EmailValidatorInterface (#120) 2016-07-03 23:52:18 +02:00
Eduardo Gulias Davis cbfe3ee6f7 Added test proof (#110) 2016-05-15 12:06:34 +02:00
Christian Flothmann 4b59310c1a fix version constraints (#109) 2016-05-15 11:16:58 +02:00
Eduardo Gulias Davis f463fbe542 Changed invalid dns domains and backslash in domain (#107)
Fixes #91 and #74 for v1.2.x
2016-05-15 01:10:54 +02:00
Eduardo Gulias Davis afc6adc0b9 Merge pull request #89 from natechicago/minor-edits
Adding documentation and consolidating some code for readability
2016-03-22 09:34:08 +01:00
natechicago 58e44fb064 Adding documentation, and making a few minor code changes for readability. 2016-03-15 18:57:49 -05:00
Eduardo Gulias Davis 04c6cdf987 Merge pull request #84 from egulias/82-brackets
#82 orphan brackets
2015-11-11 04:28:32 +01:00
Eduardo Gulias Davis cc9defe645 fixes #82 2015-11-11 04:24:15 +01:00
Eduardo Gulias Davis 50aaa0b18f Control tests for .com.xx domains 2015-11-11 03:59:45 +01:00
Andrei Sozonov de448a30fa opened parenthesis converted to class field 2015-10-12 00:34:23 +02:00
Andrei Sozonov a7b385301d #80 validate number of closing parenthesis 2015-10-12 00:34:23 +02:00
Eduardo Gulias Davis f6447f8b99 Merge pull request #77 from alexpott/patch-1
Remove duplicated tokens
2015-07-16 08:29:32 +02:00
alexpott d4001715b2 Update EmailLexer.php
Removing duplicate array keys
2015-07-15 15:39:42 +01:00
Eduardo Gulias Davis 4b7fcf6796 Merge pull request #75 from xabbuh/patch-1
update installation instructions
2015-06-29 23:49:12 +02:00
Christian Flothmann 2a1145ffbf update installation instructions
Given that Composer will install the latest version of a package
matching the stability restrictions that apply to project, users can
get an undesired version of package when they don't specify a version
constraint.
2015-06-26 19:35:00 +02:00
11 changed files with 387 additions and 664 deletions
+5 -6
View File
@@ -12,15 +12,17 @@ php:
env: env:
global: global:
- deps=no - deps=high
matrix: matrix:
fast_finish: true fast_finish: true
include: include:
- php: 5.3 - php: 5.3
env: deps=low env: deps=low
- php: 5.6 - php: 5.4
env: deps=high env: deps=no
- php: 5.5
env: deps=no
install: install:
- if [ "$deps" = "no" ]; then composer install; fi - if [ "$deps" = "no" ]; then composer install; fi
@@ -31,6 +33,3 @@ script:
- mkdir -p build/logs - mkdir -p build/logs
- phpunit --coverage-clover build/logs/clover.xml - phpunit --coverage-clover build/logs/clover.xml
after_script:
- php vendor/bin/coveralls
+1 -1
View File
@@ -9,7 +9,7 @@ With the help of
Run the command below to install via Composer Run the command below to install via Composer
```shell ```shell
composer require egulias/email-validator composer require egulias/email-validator "~1.2"
``` ```
##Usage## ##Usage##
+2 -3
View File
@@ -15,11 +15,10 @@
}, },
"require": { "require": {
"php": ">= 5.3.3", "php": ">= 5.3.3",
"doctrine/lexer": "~1.0,>=1.0.1" "doctrine/lexer": "^1.0.1"
}, },
"require-dev" : { "require-dev" : {
"satooshi/php-coveralls": "dev-master", "phpunit/phpunit": "^4.8.24"
"phpunit/phpunit": "~4.4"
}, },
"autoload": { "autoload": {
"psr-0": { "psr-0": {
Generated
+163 -583
View File
File diff suppressed because it is too large Load Diff
@@ -67,8 +67,6 @@ class EmailLexer extends AbstractLexer
"\n" => self::S_LF, "\n" => self::S_LF,
"\r\n" => self::CRLF, "\r\n" => self::CRLF,
'IPv6' => self::S_IPV6TAG, 'IPv6' => self::S_IPV6TAG,
'<' => self::S_LOWERTHAN,
'>' => self::S_GREATERTHAN,
'{' => self::S_OPENQBRACKET, '{' => self::S_OPENQBRACKET,
'}' => self::S_CLOSEQBRACKET, '}' => self::S_CLOSEQBRACKET,
'' => self::S_EMPTY, '' => self::S_EMPTY,
+76 -36
View File
@@ -7,49 +7,52 @@ namespace Egulias\EmailValidator;
* *
* @author Eduardo Gulias Davis <me@egulias.com> * @author Eduardo Gulias Davis <me@egulias.com>
*/ */
class EmailValidator class EmailValidator implements EmailValidatorInterface
{ {
/**
* Critical validation errors used to indicate that
* an email address is invalid:
*/
const ERR_CONSECUTIVEATS = 128; const ERR_CONSECUTIVEATS = 128;
const ERR_EXPECTING_DTEXT = 129; const ERR_EXPECTING_DTEXT = 129;
const ERR_NOLOCALPART = 130; const ERR_NOLOCALPART = 130;
const ERR_NODOMAIN = 131; const ERR_NODOMAIN = 131;
const ERR_CONSECUTIVEDOTS = 132; const ERR_CONSECUTIVEDOTS = 132;
const ERR_ATEXT_AFTER_CFWS = 133; 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_QPAIR = 136;
const ERR_EXPECTING_ATEXT = 137; const ERR_EXPECTING_ATEXT = 137;
const ERR_EXPECTING_QTEXT = 138;
const ERR_EXPECTING_CTEXT = 139; const ERR_EXPECTING_CTEXT = 139;
const ERR_BACKSLASHEND = 140;
const ERR_DOT_START = 141; const ERR_DOT_START = 141;
const ERR_DOT_END = 142; const ERR_DOT_END = 142;
const ERR_DOMAINHYPHENSTART = 143;
const ERR_DOMAINHYPHENEND = 144; const ERR_DOMAINHYPHENEND = 144;
const ERR_UNCLOSEDQUOTEDSTR = 145; const ERR_UNCLOSEDQUOTEDSTR = 145;
const ERR_UNCLOSEDCOMMENT = 146; const ERR_UNCLOSEDCOMMENT = 146;
const ERR_UNCLOSEDDOMLIT = 147;
const ERR_FWS_CRLF_X2 = 148; const ERR_FWS_CRLF_X2 = 148;
const ERR_FWS_CRLF_END = 149; const ERR_FWS_CRLF_END = 149;
const ERR_CR_NO_LF = 150; const ERR_CR_NO_LF = 150;
const ERR_DEPREC_REACHED = 151; const ERR_DEPREC_REACHED = 151;
const ERR_UNOPENEDCOMMENT = 152;
const ERR_ATEXT_AFTER_QS = 134; // not in use
const ERR_ATEXT_AFTER_DOMLIT = 135; // not in use
const ERR_EXPECTING_QTEXT = 138; // not in use
const ERR_BACKSLASHEND = 140; // not in use
const ERR_DOMAINHYPHENSTART = 143; // not in use
const ERR_UNCLOSEDDOMLIT = 147; // not in use
/**
* Informational validation warnings regarding unusual or
* deprecated features found in an email address:
*/
// Address is valid for SMTP (RFC-5321), but has unusual elements.
const RFC5321_TLD = 9; const RFC5321_TLD = 9;
const RFC5321_TLDNUMERIC = 10;
const RFC5321_QUOTEDSTRING = 11; const RFC5321_QUOTEDSTRING = 11;
const RFC5321_ADDRESSLITERAL = 12; const RFC5321_ADDRESSLITERAL = 12;
const RFC5321_IPV6DEPRECATED = 13; const RFC5321_IPV6DEPRECATED = 13;
const CFWS_COMMENT = 17; const RFC5321_TLDNUMERIC = 10; // not in use
const CFWS_FWS = 18; // Address is only valid according to the broad
const DEPREC_LOCALPART = 33; // definition of RFC-5322. It is otherwise invalid.
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_LOCAL_TOOLONG = 64;
const RFC5322_LABEL_TOOLONG = 63; const RFC5322_LABEL_TOOLONG = 63;
const RFC5322_DOMAIN = 65;
const RFC5322_TOOLONG = 66; const RFC5322_TOOLONG = 66;
const RFC5322_DOMAIN_TOOLONG = 255; const RFC5322_DOMAIN_TOOLONG = 255;
const RFC5322_DOMAINLITERAL = 70; const RFC5322_DOMAINLITERAL = 70;
@@ -60,12 +63,48 @@ class EmailValidator
const RFC5322_IPV6_MAXGRPS = 75; const RFC5322_IPV6_MAXGRPS = 75;
const RFC5322_IPV6_COLONSTRT = 76; const RFC5322_IPV6_COLONSTRT = 76;
const RFC5322_IPV6_COLONEND = 77; const RFC5322_IPV6_COLONEND = 77;
const RFC5322_DOMAIN = 65; // not in use
// Address contains deprecated elements, but may
// still be valid in restricted contexts.
const DEPREC_QP = 36;
const DEPREC_COMMENT = 37;
const DEPREC_CFWS_NEAR_AT = 49;
const DEPREC_LOCALPART = 33; // not in use
const DEPREC_FWS = 34; // not in use
const DEPREC_QTEXT = 35; // not in use
const DEPREC_CTEXT = 38; // not in use
// Address is valid within the message,
// but cannot be used unmodified in the envelope.
const CFWS_COMMENT = 17;
const CFWS_FWS = 18;
// Hostname DNS checks were unsuccessful.
const DNSWARN_NO_MX_RECORD = 5; const DNSWARN_NO_MX_RECORD = 5;
const DNSWARN_NO_RECORD = 6; const DNSWARN_NO_RECORD = 6;
/**
* @var EmailParser
*/
protected $parser; protected $parser;
/**
* Contains any informational warnings regarding unusual/deprecated
* features that were encountered during validation.
*
* @var array
*/
protected $warnings = array(); protected $warnings = array();
/**
* If a critical validation problem is encountered, this will be
* set to the value of one of this class's ERR_* constants.
*
* @var int
*/
protected $error; protected $error;
/**
* @var int
*/
protected $threshold = 255; protected $threshold = 255;
public function __construct() public function __construct()
@@ -73,6 +112,9 @@ class EmailValidator
$this->parser = new EmailParser(new EmailLexer()); $this->parser = new EmailParser(new EmailLexer());
} }
/**
* {@inheritdoc}
*/
public function isValid($email, $checkDNS = false, $strict = false) public function isValid($email, $checkDNS = false, $strict = false)
{ {
try { try {
@@ -84,22 +126,18 @@ class EmailValidator
return false; return false;
} }
$dns = true; $dnsProblemExists = ($checkDNS ? !$this->checkDNS() : false);
if ($checkDNS) {
$dns = $this->checkDNS();
}
if ($this->hasWarnings() && ((int) max($this->warnings) > $this->threshold)) { if ($this->hasWarnings() && ((int) max($this->warnings) > $this->threshold)) {
$this->error = self::ERR_DEPREC_REACHED; $this->error = self::ERR_DEPREC_REACHED;
return false; return false;
} }
return !$strict || (!$this->hasWarnings() && $dns); return ($strict ? (!$this->hasWarnings() && !$dnsProblemExists) : true);
} }
/** /**
* @return boolean * {@inheritdoc}
*/ */
public function hasWarnings() public function hasWarnings()
{ {
@@ -107,7 +145,7 @@ class EmailValidator
} }
/** /**
* @return array * {@inheritdoc}
*/ */
public function getWarnings() public function getWarnings()
{ {
@@ -115,7 +153,7 @@ class EmailValidator
} }
/** /**
* @return string * {@inheritdoc}
*/ */
public function getError() public function getError()
{ {
@@ -123,9 +161,7 @@ class EmailValidator
} }
/** /**
* @param int $threshold * {@inheritdoc}
*
* @return EmailValidator
*/ */
public function setThreshold($threshold) public function setThreshold($threshold)
{ {
@@ -135,26 +171,30 @@ class EmailValidator
} }
/** /**
* @return int * {@inheritdoc}
*/ */
public function getThreshold() public function getThreshold()
{ {
return $this->threshold; return $this->threshold;
} }
/**
* @return bool Whether or not an MX record exists for the
* email address's host name.
*/
protected function checkDNS() protected function checkDNS()
{ {
$checked = true; $host = $this->parser->getParsedDomainPart();
$host = rtrim($host, '.') . '.';
$result = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX'); $mxRecordExists = checkdnsrr($host, 'MX');
if (!$result) { if (!$mxRecordExists) {
$this->warnings[] = self::DNSWARN_NO_RECORD; $this->warnings[] = self::DNSWARN_NO_RECORD;
$checked = false;
$this->addTLDWarnings(); $this->addTLDWarnings();
} }
return $checked; return $mxRecordExists;
} }
protected function addTLDWarnings() protected function addTLDWarnings()
@@ -0,0 +1,62 @@
<?php
namespace Egulias\EmailValidator;
/**
* EmailValidatorInterface
*
* @author Chris McCafferty <cilefen@gmail.com>
*/
interface EmailValidatorInterface
{
/**
* Validates an email address against the following standards:
*
* RFC-5321: Simple Mail Transfer Protocol
* RFC-5322: Internet Message Format
* RFC-6530: Overview and Framework for Internationalized Email
* RFC-6531: SMTP Extension for Internationalized Email
* RFC-6532: Internationalized Email Headers
* RFC-1123 section 2.1: Requirements for Internet Hosts -- Application and Support
* RFC-4291 section 2.2: IP Version 6 Addressing Architecture
*
* @param string $email The email address to validate.
* @param bool $checkDNS Whether or not the email address's hostname should
* be confirmed with a DNS lookup. This only comes
* into play if strict mode is also enabled.
* @param bool $strict If this is true, and any informational warnings
* were raised during validation, the email address
* will be considered invalid. Additionally, if
* $checkDNS is true and the DNS lookup failed,
* the email address will be considered invalid.
* @return bool
*/
public function isValid($email, $checkDNS = false, $strict = false);
/**
* @return bool
*/
public function hasWarnings();
/**
* @return array
*/
public function getWarnings();
/**
* @return string
*/
public function getError();
/**
* @param int $threshold The acceptable number of deprecation warnings.
*
* @return EmailValidator
*/
public function setThreshold($threshold);
/**
* @return int
*/
public function getThreshold();
}
@@ -4,7 +4,6 @@
namespace Egulias\EmailValidator\Parser; namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer; use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Parser\Parser;
use Egulias\EmailValidator\EmailValidator; use Egulias\EmailValidator\EmailValidator;
class DomainPart extends Parser class DomainPart extends Parser
@@ -103,23 +102,34 @@ class DomainPart extends Parser
protected function doParseDomainPart() protected function doParseDomainPart()
{ {
$domain = ''; $domain = '';
$openedParenthesis = 0;
$openBrackets = false;
do { do {
$prev = $this->lexer->getPrevious(); $prev = $this->lexer->getPrevious();
if ($this->lexer->token['type'] === EmailLexer::S_SLASH) { $this->checkNotAllowedChars($this->lexer->token);
throw new \InvalidArgumentException('ERR_DOMAIN_CHAR_NOT_ALLOWED');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments(); $this->parseComments();
$openedParenthesis += $this->getOpenedParenthesis();
$this->lexer->moveNext(); $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 \InvalidArgumentException('ERR_UNOPENEDCOMMENT');
} else {
$openedParenthesis--;
}
} }
$this->checkConsecutiveDots(); $this->checkConsecutiveDots();
$this->checkDomainPartExceptions($prev); $this->checkDomainPartExceptions($prev);
if ($this->hasBrackets()) { if ($openBrackets = $this->hasBrackets($openBrackets)) {
$this->parseDomainLiteral(); $this->parseDomainLiteral();
} }
@@ -136,6 +146,14 @@ class DomainPart extends Parser
return $domain; return $domain;
} }
private function checkNotAllowedChars($token)
{
$notAllowed = array(EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true);
if (isset($notAllowed[$token['type']])) {
throw new \InvalidArgumentException('ERR_DOMAIN_CHAR_NOT_ALLOWED');
}
}
protected function parseDomainLiteral() protected function parseDomainLiteral()
{ {
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) { if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
@@ -180,7 +198,7 @@ class DomainPart extends Parser
} }
if ($this->lexer->isNextToken(EmailLexer::S_CR)) { if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
throw new \InvalidArgumentException("ERR_CR_NO_LF"); throw new \InvalidArgumentException('ERR_CR_NO_LF');
} }
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) { if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT; $this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
@@ -276,8 +294,12 @@ class DomainPart extends Parser
} }
} }
protected function hasBrackets() protected function hasBrackets($openBrackets)
{ {
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEBRACKET && !$openBrackets) {
throw new \InvalidArgumentException('ERR_EXPECTING_OPENBRACKET');
}
if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) { if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) {
return false; return false;
} }
@@ -4,7 +4,6 @@ namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer; use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\EmailValidator; use Egulias\EmailValidator\EmailValidator;
use \InvalidArgumentException;
class LocalPart extends Parser class LocalPart extends Parser
{ {
@@ -12,9 +11,9 @@ class LocalPart extends Parser
{ {
$parseDQuote = true; $parseDQuote = true;
$closingQuote = false; $closingQuote = false;
$openedParenthesis = 0;
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) { while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) { if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
throw new \InvalidArgumentException('ERR_DOT_START'); throw new \InvalidArgumentException('ERR_DOT_START');
} }
@@ -26,12 +25,19 @@ class LocalPart extends Parser
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments(); $this->parseComments();
$openedParenthesis += $this->getOpenedParenthesis();
}
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
if ($openedParenthesis === 0) {
throw new \InvalidArgumentException('ERR_UNOPENEDCOMMENT');
} else {
$openedParenthesis--;
}
} }
$this->checkConsecutiveDots(); $this->checkConsecutiveDots();
if ( if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
$this->lexer->token['type'] === EmailLexer::S_DOT &&
$this->lexer->isNextToken(EmailLexer::S_AT) $this->lexer->isNextToken(EmailLexer::S_AT)
) { ) {
throw new \InvalidArgumentException('ERR_DOT_END'); throw new \InvalidArgumentException('ERR_DOT_END');
@@ -82,7 +88,7 @@ class LocalPart extends Parser
$this->lexer->moveNext(); $this->lexer->moveNext();
if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) { if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
throw new InvalidArgumentException("ERR_EXPECTED_ATEXT"); throw new \InvalidArgumentException('ERR_EXPECTED_ATEXT');
} }
} }
@@ -90,12 +96,12 @@ class LocalPart extends Parser
if ($prev['type'] === EmailLexer::S_BACKSLASH) { if ($prev['type'] === EmailLexer::S_BACKSLASH) {
if (!$this->checkDQUOTE(false)) { 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) { 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; return $parseAgain;
+17 -10
View File
@@ -9,6 +9,7 @@ abstract class Parser
{ {
protected $warnings = array(); protected $warnings = array();
protected $lexer; protected $lexer;
protected $openedParenthesis = 0;
public function __construct(EmailLexer $lexer) public function __construct(EmailLexer $lexer)
{ {
@@ -20,7 +21,13 @@ abstract class Parser
return $this->warnings; return $this->warnings;
} }
abstract function parse($str); abstract public function parse($str);
/** @return int */
public function getOpenedParenthesis()
{
return $this->openedParenthesis;
}
/** /**
* validateQuotedPair * validateQuotedPair
@@ -35,15 +42,15 @@ abstract class Parser
$this->warnings[] = EmailValidator::DEPREC_QP; $this->warnings[] = EmailValidator::DEPREC_QP;
} }
/**
* @return string the the comment
* @throws \InvalidArgumentException
*/
protected function parseComments() protected function parseComments()
{ {
$this->openedParenthesis = 1;
$this->isUnclosedComment(); $this->isUnclosedComment();
$this->warnings[] = EmailValidator::CFWS_COMMENT; $this->warnings[] = EmailValidator::CFWS_COMMENT;
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
$this->openedParenthesis++;
}
$this->warnEscaping(); $this->warnEscaping();
$this->lexer->moveNext(); $this->lexer->moveNext();
} }
@@ -75,11 +82,11 @@ abstract class Parser
$this->checkCRLFInFWS(); $this->checkCRLFInFWS();
if ($this->lexer->token['type'] === EmailLexer::S_CR) { if ($this->lexer->token['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException("ERR_CR_NO_LF"); throw new \InvalidArgumentException('ERR_CR_NO_LF');
} }
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) { if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
throw new \InvalidArgumentException("ERR_ATEXT_AFTER_CFWS"); throw new \InvalidArgumentException('ERR_ATEXT_AFTER_CFWS');
} }
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) { if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
@@ -160,7 +167,7 @@ abstract class Parser
return $hasClosingQuote; return $hasClosingQuote;
} }
$previous = $this->lexer->getPrevious(); $previous = $this->lexer->getPrevious();
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) { if ($previous['type'] === EmailLexer::GENERIC && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT'); throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
} }
@@ -181,10 +188,10 @@ abstract class Parser
return; return;
} }
if ($this->lexer->isNextToken(EmailLexer::CRLF)) { if ($this->lexer->isNextToken(EmailLexer::CRLF)) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_X2"); throw new \InvalidArgumentException('ERR_FWS_CRLF_X2');
} }
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_END"); throw new \InvalidArgumentException('ERR_FWS_CRLF_END');
} }
} }
} }
@@ -63,6 +63,8 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
array('test@email^'), array('test@email^'),
array('test@email%'), array('test@email%'),
array('test@email$'), array('test@email$'),
array('test@email.com.au'),
array('1500111@профи-инвест.рф'),
); );
} }
@@ -87,6 +89,7 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
array('.example@localhost'), array('.example@localhost'),
array('ex\ample@localhost'), array('ex\ample@localhost'),
array('example@local\host'), array('example@local\host'),
array('example@localhost\\'),
array('example@localhost.'), array('example@localhost.'),
array('user name@example.com'), array('user name@example.com'),
array('username@ example . com'), array('username@ example . com'),
@@ -123,6 +126,8 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
array('test@email>'), array('test@email>'),
array('test@email<'), array('test@email<'),
array('test@email{'), array('test@email{'),
array('test@email.com]'),
array('test@ema[il.com'),
); );
} }
@@ -152,6 +157,11 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
array(EmailValidator::ERR_DOT_END, 'example@localhost.'), array(EmailValidator::ERR_DOT_END, 'example@localhost.'),
array(EmailValidator::ERR_DOT_END, 'example.@example.co.uk'), array(EmailValidator::ERR_DOT_END, 'example.@example.co.uk'),
array(EmailValidator::ERR_UNCLOSEDCOMMENT, '(example@localhost'), array(EmailValidator::ERR_UNCLOSEDCOMMENT, '(example@localhost'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'comment)example@localhost'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example(comment))@localhost'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@comment)localhost'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@localhost(comment))'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@(comment))example.com'),
array(EmailValidator::ERR_UNCLOSEDQUOTEDSTR, '"example@localhost'), array(EmailValidator::ERR_UNCLOSEDQUOTEDSTR, '"example@localhost'),
array(EmailValidator::ERR_EXPECTING_ATEXT, 'exa"mple@localhost'), array(EmailValidator::ERR_EXPECTING_ATEXT, 'exa"mple@localhost'),
//This was the original. But atext is not allowed after \n //This was the original. But atext is not allowed after \n
@@ -193,21 +203,21 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
EmailValidator::DEPREC_CFWS_NEAR_AT, EmailValidator::DEPREC_CFWS_NEAR_AT,
EmailValidator::DNSWARN_NO_RECORD EmailValidator::DNSWARN_NO_RECORD
), ),
'example @example.co.uk' 'example @invalid.example.com'
), ),
array( array(
array( array(
EmailValidator::DEPREC_CFWS_NEAR_AT, EmailValidator::DEPREC_CFWS_NEAR_AT,
EmailValidator::DNSWARN_NO_RECORD EmailValidator::DNSWARN_NO_RECORD
), ),
'example@ example.co.uk' 'example@ invalid.example.com'
), ),
array( array(
array( array(
EmailValidator::CFWS_COMMENT, EmailValidator::CFWS_COMMENT,
EmailValidator::DNSWARN_NO_RECORD EmailValidator::DNSWARN_NO_RECORD
), ),
'example@example(examplecomment).co.uk' 'example@invalid.example(examplecomment).com'
), ),
array( array(
array( array(
@@ -215,7 +225,7 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
EmailValidator::DEPREC_CFWS_NEAR_AT, EmailValidator::DEPREC_CFWS_NEAR_AT,
EmailValidator::DNSWARN_NO_RECORD, EmailValidator::DNSWARN_NO_RECORD,
), ),
'example(examplecomment)@example.co.uk' 'example(examplecomment)@invalid.example.com'
), ),
array( array(
array( array(
@@ -223,7 +233,7 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
EmailValidator::CFWS_FWS, EmailValidator::CFWS_FWS,
EmailValidator::DNSWARN_NO_RECORD, EmailValidator::DNSWARN_NO_RECORD,
), ),
"\"\t\"@example.co.uk" "\"\t\"@invalid.example.com"
), ),
array( array(
array( array(
@@ -231,7 +241,7 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
EmailValidator::CFWS_FWS, EmailValidator::CFWS_FWS,
EmailValidator::DNSWARN_NO_RECORD EmailValidator::DNSWARN_NO_RECORD
), ),
"\"\r\"@example.co.uk" "\"\r\"@invalid.example.com"
), ),
array( array(
array( array(
@@ -323,14 +333,14 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
EmailValidator::RFC5321_QUOTEDSTRING, EmailValidator::RFC5321_QUOTEDSTRING,
EmailValidator::DNSWARN_NO_RECORD EmailValidator::DNSWARN_NO_RECORD
), ),
'"example"@example.co.uk' '"example"@invalid.example.com'
), ),
array( array(
array( array(
EmailValidator::RFC5322_LOCAL_TOOLONG, EmailValidator::RFC5322_LOCAL_TOOLONG,
EmailValidator::DNSWARN_NO_RECORD EmailValidator::DNSWARN_NO_RECORD
), ),
'too_long_localpart_too_long_localpart_too_long_localpart_too_long_localpart@example.co.uk' 'too_long_localpart_too_long_localpart_too_long_localpart_too_long_localpart@invalid.example.com'
), ),
array( array(
array( array(