mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-08-31 12:40:28 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5642614492 | |||
| b8bb147f46 | |||
| cbfe3ee6f7 | |||
| 4b59310c1a | |||
| f463fbe542 | |||
| afc6adc0b9 | |||
| 58e44fb064 | |||
| 04c6cdf987 | |||
| cc9defe645 | |||
| 50aaa0b18f | |||
| de448a30fa | |||
| a7b385301d | |||
| f6447f8b99 | |||
| d4001715b2 | |||
| 4b7fcf6796 | |||
| 2a1145ffbf |
+5
-6
@@ -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
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user