mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-09-01 21:19:25 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5631c3fe2e | |||
| c383717824 | |||
| aabed98198 | |||
| 3b0fd01aa3 | |||
| 4efe697504 | |||
| 7a4c5714f5 | |||
| 0a26375a7a | |||
| 773fe5f4a3 | |||
| 4a209951ee | |||
| a8b0bab5aa | |||
| cc0b91f311 | |||
| 8092ecaeff | |||
| f19defb373 | |||
| 9a518fb1bd | |||
| 9b184605e8 | |||
| 7a4842c60f | |||
| 687ab6a66e | |||
| 116d4054fa | |||
| af9417f765 | |||
| ab0bcfb424 | |||
| 833b3e7924 | |||
| fec1609458 | |||
| add559b99c | |||
| 19e390d863 | |||
| 5320c26b38 | |||
| 3b6ab3bcd5 | |||
| 7a64ea18af | |||
| 9103f4f99b | |||
| 3f623e9006 | |||
| 833eb65135 | |||
| e61481fefb | |||
| 518f80a0ff | |||
| 2ec7babda6 | |||
| 05367768d6 | |||
| 5c3a79217c | |||
| 749b423775 | |||
| 49494c3189 | |||
| 8b62522238 | |||
| 82fb2d6ce1 | |||
| 2933b7bbd1 | |||
| f5c48f62d3 | |||
| 43650e8566 | |||
| 3d94cc6487 | |||
| 65c8c3d7bb | |||
| 57f8ae443b | |||
| 18df863858 | |||
| 953cbdbbcf | |||
| 41cb75ec00 | |||
| 8927160d8d | |||
| 887605ad1b | |||
| 43e1825cd8 | |||
| 2f44d7b1e1 | |||
| 355cb7e604 | |||
| d6fcd2d7ca | |||
| 9be8eb420a | |||
| 20066b1c1e | |||
| 39b451bb2b | |||
| 65afaf3f2b | |||
| 82f8514d89 | |||
| 440bc61237 | |||
| a96f0822e0 |
+19
-3
@@ -1,3 +1,5 @@
|
||||
sudo: false
|
||||
|
||||
language: php
|
||||
|
||||
php:
|
||||
@@ -5,11 +7,25 @@ php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- hhvm
|
||||
|
||||
before_script:
|
||||
- wget http://getcomposer.org/composer.phar
|
||||
- php composer.phar install --dev --no-interaction
|
||||
env:
|
||||
global:
|
||||
- deps=no
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- php: 5.3
|
||||
env: deps=low
|
||||
- php: 5.6
|
||||
env: deps=high
|
||||
|
||||
install:
|
||||
- if [ "$deps" = "no" ]; then composer install; fi
|
||||
- if [ "$deps" = "low" ]; then composer update --prefer-lowest; fi
|
||||
- if [ "$deps" = "high" ]; then composer update; fi
|
||||
|
||||
script:
|
||||
- mkdir -p build/logs
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
EmailValidator [](https://travis-ci.org/egulias/EmailValidator) [](https://coveralls.io/r/egulias/EmailValidator?branch=master) [](https://insight.sensiolabs.com/projects/b18d473e-bd5a-4d88-a7b2-7aeaee0ebd7b)[](https://scrutinizer-ci.com/g/egulias/EmailValidator/?branch=master)
|
||||
#EmailValidator
|
||||
[](https://travis-ci.org/egulias/EmailValidator) [](https://coveralls.io/r/egulias/EmailValidator?branch=master) [](https://scrutinizer-ci.com/g/egulias/EmailValidator/?branch=master) [](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6)
|
||||
=============================
|
||||
With the help of
|
||||
|
||||

|
||||
##Installation##
|
||||
Install via composer. Add to your current compooser.json ```require``` key: ```"egulias/email-validator":"1.0.x-dev" ```
|
||||
|
||||
Run the command below to install via Composer
|
||||
|
||||
```shell
|
||||
composer require egulias/email-validator
|
||||
```
|
||||
|
||||
##Usage##
|
||||
|
||||
@@ -24,7 +32,7 @@ More advanced example (returns detailed diagnostic error codes):
|
||||
```php
|
||||
<?php
|
||||
|
||||
use egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
|
||||
$validator = new EmailValidator;
|
||||
$email = 'dominic@sayers.cc';
|
||||
@@ -46,6 +54,6 @@ As this is a port from another library and work, here are other people related t
|
||||
* Josepf Bielawski [@stloyd](http://github.com/stloyd): For its first re-work of Dominic's lib
|
||||
* Dominic Sayers [@dominicsayers](http://github.com/dominicsayers): The original isemail function
|
||||
|
||||
##Licence##
|
||||
Released under the MIT Licence attached with this code.
|
||||
##License##
|
||||
Released under the MIT License attached with this code.
|
||||
|
||||
|
||||
+4
-3
@@ -10,15 +10,16 @@
|
||||
],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
"dev-master": "1.3.x-dev"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">= 5.3.3",
|
||||
"doctrine/lexer": "~1.0"
|
||||
"doctrine/lexer": "~1.0,>=1.0.1"
|
||||
},
|
||||
"require-dev" : {
|
||||
"satooshi/php-coveralls": "dev-master"
|
||||
"satooshi/php-coveralls": "dev-master",
|
||||
"phpunit/phpunit": "~4.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
|
||||
Generated
+902
-110
File diff suppressed because it is too large
Load Diff
@@ -31,10 +31,13 @@ class EmailLexer extends AbstractLexer
|
||||
const S_SEMICOLON = 275;
|
||||
const S_OPENQBRACKET = 276;
|
||||
const S_CLOSEQBRACKET = 277;
|
||||
const S_SLASH = 278;
|
||||
const S_EMPTY = null;
|
||||
const GENERIC = 300;
|
||||
const CRLF = 301;
|
||||
const INVALID = 302;
|
||||
const ASCII_INVALID_FROM = 127;
|
||||
const ASCII_INVALID_TO = 199;
|
||||
|
||||
/**
|
||||
* US-ASCII visible characters not valid for atext (@link http://tools.ietf.org/html/rfc5322#section-3.2.3)
|
||||
@@ -52,6 +55,7 @@ class EmailLexer extends AbstractLexer
|
||||
';' => self::S_SEMICOLON,
|
||||
'@' => self::S_AT,
|
||||
'\\' => self::S_BACKSLASH,
|
||||
'/' => self::S_SLASH,
|
||||
',' => self::S_COMMA,
|
||||
'.' => self::S_DOT,
|
||||
'"' => self::S_DQUOTE,
|
||||
@@ -67,14 +71,29 @@ class EmailLexer extends AbstractLexer
|
||||
'>' => self::S_GREATERTHAN,
|
||||
'{' => self::S_OPENQBRACKET,
|
||||
'}' => self::S_CLOSEQBRACKET,
|
||||
'' => self::S_EMPTY
|
||||
'' => self::S_EMPTY,
|
||||
'\0' => self::C_NUL,
|
||||
);
|
||||
|
||||
protected $hasInvalidTokens = false;
|
||||
|
||||
protected $previous;
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->hasInvalidTokens = false;
|
||||
parent::reset();
|
||||
}
|
||||
|
||||
public function hasInvalidTokens()
|
||||
{
|
||||
return $this->hasInvalidTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @throws \UnexpectedValueException
|
||||
* @return boolean
|
||||
*/
|
||||
public function find($type)
|
||||
{
|
||||
@@ -84,6 +103,7 @@ class EmailLexer extends AbstractLexer
|
||||
if (!$search->lookahead) {
|
||||
throw new \UnexpectedValueException($type . ' not found');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +119,7 @@ class EmailLexer extends AbstractLexer
|
||||
/**
|
||||
* moveNext
|
||||
*
|
||||
* @return mixed
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveNext()
|
||||
{
|
||||
@@ -111,29 +131,29 @@ class EmailLexer extends AbstractLexer
|
||||
/**
|
||||
* Lexical catchable patterns.
|
||||
*
|
||||
* @return array
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getCatchablePatterns()
|
||||
{
|
||||
return array(
|
||||
'[a-zA-Z_]+[4,6]?',
|
||||
'[a-zA-Z_]+[46]?', //ASCII and domain literal
|
||||
'[^\x00-\x7F]', //UTF-8
|
||||
'[0-9]+',
|
||||
'\r\n',
|
||||
'::',
|
||||
'\s+',
|
||||
'[\x1-\x1F]+',
|
||||
'.'
|
||||
'\s+?',
|
||||
'.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical non-catchable patterns.
|
||||
*
|
||||
* @return array
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getNonCatchablePatterns()
|
||||
{
|
||||
return array('[\x7f-\xff]+');
|
||||
return array('[\xA0-\xff]+');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,14 +165,59 @@ class EmailLexer extends AbstractLexer
|
||||
*/
|
||||
protected function getType(&$value)
|
||||
{
|
||||
if (isset($this->charValue[$value])) {
|
||||
if ($this->isNullType($value)) {
|
||||
return self::C_NUL;
|
||||
}
|
||||
|
||||
if ($this->isValid($value)) {
|
||||
return $this->charValue[$value];
|
||||
}
|
||||
|
||||
if (preg_match('/[\x1-\x1F]+/', $value)) {
|
||||
if ($this->isUTF8Invalid($value)) {
|
||||
$this->hasInvalidTokens = true;
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
return self::GENERIC;
|
||||
}
|
||||
|
||||
protected function isValid($value)
|
||||
{
|
||||
if (isset($this->charValue[$value])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNullType($value)
|
||||
{
|
||||
if ($value === "\0") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isUTF8Invalid($value)
|
||||
{
|
||||
if (preg_match('/\p{Cc}+/u', $value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getModifiers()
|
||||
{
|
||||
return 'iu';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ class EmailParser
|
||||
$this->domainPartParser = new DomainPart($this->lexer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $str
|
||||
* @return array
|
||||
*/
|
||||
public function parse($str)
|
||||
{
|
||||
$this->lexer->setInput($str);
|
||||
@@ -36,11 +40,16 @@ class EmailParser
|
||||
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');
|
||||
}
|
||||
|
||||
return array('local' => $this->localPart, 'domain' => $this->domainPart);
|
||||
}
|
||||
|
||||
@@ -78,6 +87,10 @@ class EmailParser
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $localPart
|
||||
* @param string $parsedDomainPart
|
||||
*/
|
||||
protected function addLongEmailWarning($localPart, $parsedDomainPart)
|
||||
{
|
||||
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
|
||||
/**
|
||||
* EmailValidator
|
||||
*
|
||||
@@ -86,7 +84,7 @@ class EmailValidator
|
||||
return false;
|
||||
}
|
||||
|
||||
$dns = false;
|
||||
$dns = true;
|
||||
if ($checkDNS) {
|
||||
$dns = $this->checkDNS();
|
||||
}
|
||||
@@ -97,7 +95,7 @@ class EmailValidator
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($strict) ? (!$this->hasWarnings() && $dns) : true;
|
||||
return !$strict || (!$this->hasWarnings() && $dns);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,16 +144,9 @@ class EmailValidator
|
||||
|
||||
protected function checkDNS()
|
||||
{
|
||||
$checked = false;
|
||||
if (!function_exists('dns_get_record') && (
|
||||
in_array(self::DNSWARN_NO_RECORD, $this->warnings) &&
|
||||
in_array(self::DNSWARN_NO_MX_RECORD, $this->warnings)
|
||||
)) {
|
||||
return $checked;
|
||||
}
|
||||
$checked = true;
|
||||
|
||||
$result = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX');
|
||||
$checked = true;
|
||||
|
||||
if (!$result) {
|
||||
$this->warnings[] = self::DNSWARN_NO_RECORD;
|
||||
|
||||
@@ -23,6 +23,9 @@ class DomainPart extends Parser
|
||||
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) {
|
||||
$this->warnings[] = EmailValidator::DEPREC_COMMENT;
|
||||
@@ -101,8 +104,13 @@ class DomainPart extends Parser
|
||||
{
|
||||
$domain = '';
|
||||
do {
|
||||
|
||||
$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();
|
||||
@@ -235,12 +243,29 @@ class DomainPart extends Parser
|
||||
|
||||
protected function checkDomainPartExceptions($prev)
|
||||
{
|
||||
$invalidDomainTokens = array(
|
||||
EmailLexer::S_DQUOTE => true,
|
||||
EmailLexer::S_SEMICOLON => true,
|
||||
EmailLexer::S_GREATERTHAN => true,
|
||||
EmailLexer::S_LOWERTHAN => true,
|
||||
);
|
||||
|
||||
if (isset($invalidDomainTokens[$this->lexer->token['type']])) {
|
||||
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');
|
||||
}
|
||||
@@ -289,5 +314,4 @@ class DomainPart extends Parser
|
||||
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Parser\Parser;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
|
||||
use \InvalidArgumentException;
|
||||
|
||||
class LocalPart extends Parser
|
||||
{
|
||||
public function parse($localPart)
|
||||
{
|
||||
$parseDQuote = true;
|
||||
$closingQuote = false;
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
|
||||
@@ -19,6 +20,9 @@ class LocalPart extends Parser
|
||||
}
|
||||
|
||||
$closingQuote = $this->checkDQUOTE($closingQuote);
|
||||
if ($closingQuote && $parseDQuote) {
|
||||
$parseDQuote = $this->parseDoubleQuote();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->parseComments();
|
||||
@@ -26,20 +30,15 @@ class LocalPart extends Parser
|
||||
|
||||
$this->checkConsecutiveDots();
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_AT)) {
|
||||
if (
|
||||
$this->lexer->token['type'] === EmailLexer::S_DOT &&
|
||||
$this->lexer->isNextToken(EmailLexer::S_AT)
|
||||
) {
|
||||
throw new \InvalidArgumentException('ERR_DOT_END');
|
||||
}
|
||||
|
||||
$this->warnEscaping();
|
||||
|
||||
if ($this->lexer->isNextTokenAny(
|
||||
array(
|
||||
EmailLexer::INVALID, EmailLexer::S_LOWERTHAN, EmailLexer::S_GREATERTHAN
|
||||
)
|
||||
)
|
||||
) {
|
||||
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
|
||||
}
|
||||
$this->isInvalidToken($this->lexer->token, $closingQuote);
|
||||
|
||||
if ($this->isFWS()) {
|
||||
$this->parseFWS();
|
||||
@@ -53,4 +52,70 @@ class LocalPart extends Parser
|
||||
$this->warnings[] = EmailValidator::RFC5322_LOCAL_TOOLONG;
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseDoubleQuote()
|
||||
{
|
||||
$parseAgain = true;
|
||||
$special = array(
|
||||
EmailLexer::S_CR => true,
|
||||
EmailLexer::S_HTAB => true,
|
||||
EmailLexer::S_LF => true
|
||||
);
|
||||
|
||||
$invalid = array(
|
||||
EmailLexer::C_NUL => true,
|
||||
EmailLexer::S_HTAB => true,
|
||||
EmailLexer::S_CR => true,
|
||||
EmailLexer::S_LF => true
|
||||
);
|
||||
$setSpecialsWarning = true;
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) {
|
||||
$parseAgain = false;
|
||||
if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
|
||||
$this->warnings[] = EmailValidator::CFWS_FWS;
|
||||
$setSpecialsWarning = false;
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
|
||||
throw new InvalidArgumentException("ERR_EXPECTED_ATEXT");
|
||||
}
|
||||
}
|
||||
|
||||
$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)
|
||||
{
|
||||
$forbidden = array(
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,10 @@ abstract class Parser
|
||||
|
||||
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 ||
|
||||
@@ -114,6 +118,20 @@ abstract class Parser
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function escaped()
|
||||
{
|
||||
$previous = $this->lexer->getPrevious();
|
||||
|
||||
if ($previous['type'] === EmailLexer::S_BACKSLASH
|
||||
&&
|
||||
$this->lexer->token['type'] !== EmailLexer::GENERIC
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function warnEscaping()
|
||||
{
|
||||
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
|
||||
@@ -145,6 +163,7 @@ abstract class Parser
|
||||
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
|
||||
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
|
||||
}
|
||||
|
||||
$this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING;
|
||||
try {
|
||||
$this->lexer->find(EmailLexer::S_DQUOTE);
|
||||
@@ -168,4 +187,4 @@ abstract class Parser
|
||||
throw new \InvalidArgumentException("ERR_FWS_CRLF_END");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,73 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($token, $lexer->token['type']);
|
||||
}
|
||||
|
||||
public function testLexerParsesMultipleSpaces()
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
$lexer->setInput(' ');
|
||||
$lexer->moveNext();
|
||||
$lexer->moveNext();
|
||||
$this->assertEquals(EmailLexer::S_SP, $lexer->token['type']);
|
||||
$lexer->moveNext();
|
||||
$this->assertEquals(EmailLexer::S_SP, $lexer->token['type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidUTF8CharsProvider
|
||||
*/
|
||||
public function testLexerParsesInvalidUTF8($char)
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
$lexer->setInput($char);
|
||||
$lexer->moveNext();
|
||||
$lexer->moveNext();
|
||||
|
||||
$this->assertEquals(EmailLexer::INVALID, $lexer->token['type']);
|
||||
}
|
||||
|
||||
public function invalidUTF8CharsProvider()
|
||||
{
|
||||
$chars = array();
|
||||
for ($i = 0; $i < 0x100; ++$i) {
|
||||
$c = $this->utf8Chr($i);
|
||||
if (preg_match('/(?=\p{Cc})(?=[^\t\n\n\r])/u', $c) && !preg_match('/\x{0000}/u', $c)) {
|
||||
$chars[] = array($c);
|
||||
}
|
||||
}
|
||||
|
||||
return $chars;
|
||||
}
|
||||
|
||||
protected function utf8Chr($code_point)
|
||||
{
|
||||
|
||||
if ($code_point < 0 || 0x10FFFF < $code_point || (0xD800 <= $code_point && $code_point <= 0xDFFF)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($code_point < 0x80) {
|
||||
$hex[0] = $code_point;
|
||||
$ret = chr($hex[0]);
|
||||
} elseif ($code_point < 0x800) {
|
||||
$hex[0] = 0x1C0 | $code_point >> 6;
|
||||
$hex[1] = 0x80 | $code_point & 0x3F;
|
||||
$ret = chr($hex[0]).chr($hex[1]);
|
||||
} elseif ($code_point < 0x10000) {
|
||||
$hex[0] = 0xE0 | $code_point >> 12;
|
||||
$hex[1] = 0x80 | $code_point >> 6 & 0x3F;
|
||||
$hex[2] = 0x80 | $code_point & 0x3F;
|
||||
$ret = chr($hex[0]).chr($hex[1]).chr($hex[2]);
|
||||
} else {
|
||||
$hex[0] = 0xF0 | $code_point >> 18;
|
||||
$hex[1] = 0x80 | $code_point >> 12 & 0x3F;
|
||||
$hex[2] = 0x80 | $code_point >> 6 & 0x3F;
|
||||
$hex[3] = 0x80 | $code_point & 0x3F;
|
||||
$ret = chr($hex[0]).chr($hex[1]).chr($hex[2]).chr($hex[3]);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function testLexerForTab()
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
@@ -36,6 +103,25 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(EmailLexer::S_HTAB, $lexer->token['type']);
|
||||
}
|
||||
|
||||
public function testLexerForUTF8()
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
$lexer->setInput("áÇ@bar.com");
|
||||
$lexer->moveNext();
|
||||
$lexer->moveNext();
|
||||
$this->assertEquals(EmailLexer::GENERIC, $lexer->token['type']);
|
||||
$lexer->moveNext();
|
||||
$this->assertEquals(EmailLexer::GENERIC, $lexer->token['type']);
|
||||
}
|
||||
|
||||
public function testLexerSearchToken()
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
$lexer->setInput("foo\tbar");
|
||||
$lexer->moveNext();
|
||||
$this->assertTrue($lexer->find(EmailLexer::S_HTAB));
|
||||
}
|
||||
|
||||
public function getTokens()
|
||||
{
|
||||
return array(
|
||||
@@ -53,6 +139,7 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
|
||||
array("\"", EmailLexer::S_DQUOTE),
|
||||
array("-", EmailLexer::S_HYPHEN),
|
||||
array("\\", EmailLexer::S_BACKSLASH),
|
||||
array("/", EmailLexer::S_SLASH),
|
||||
array("(", EmailLexer::S_OPENPARENTHESIS),
|
||||
array(")", EmailLexer::S_CLOSEPARENTHESIS),
|
||||
array('<', EmailLexer::S_LOWERTHAN),
|
||||
@@ -66,7 +153,9 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
|
||||
array('{', EmailLexer::S_OPENQBRACKET),
|
||||
array('}', EmailLexer::S_CLOSEQBRACKET),
|
||||
array('', EmailLexer::S_EMPTY),
|
||||
array(chr(31), EmailLexer::INVALID)
|
||||
array(chr(31), EmailLexer::INVALID),
|
||||
array(chr(226), EmailLexer::GENERIC),
|
||||
array(chr(0), EmailLexer::C_NUL)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,26 +26,50 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertTrue($this->validator->isValid($email));
|
||||
}
|
||||
|
||||
public function testInvalidUTF8Email()
|
||||
{
|
||||
$validator = new EmailValidator;
|
||||
$email = "\x80\x81\x82@\x83\x84\x85.\x86\x87\x88";
|
||||
|
||||
$this->assertFalse($validator->isValid($email));
|
||||
}
|
||||
|
||||
public function getValidEmails()
|
||||
{
|
||||
return array(
|
||||
array('â@iana.org'),
|
||||
array('fabien@symfony.com'),
|
||||
array('example@example.co.uk'),
|
||||
array('fabien_potencier@example.fr'),
|
||||
array('example@localhost'),
|
||||
array('fab\'ien@symfony.com'),
|
||||
array('fab\ ien@symfony.com'),
|
||||
array('example((example))@fakedfake.co.uk'),
|
||||
array('example@faked(fake).co.uk'),
|
||||
array('fabien+@symfony.com'),
|
||||
array('инфо@письмо.рф'),
|
||||
|
||||
array('"username"@example.com'),
|
||||
array('"user,name"@example.com'),
|
||||
array('"user name"@example.com'),
|
||||
array('"user@name"@example.com'),
|
||||
array('"\a"@iana.org'),
|
||||
array('"test\ test"@iana.org'),
|
||||
array('""@iana.org'),
|
||||
array('"\""@iana.org'),
|
||||
array('müller@möller.de'),
|
||||
array('test@email*'),
|
||||
array('test@email!'),
|
||||
array('test@email&'),
|
||||
array('test@email^'),
|
||||
array('test@email%'),
|
||||
array('test@email$'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getInvalidEmails
|
||||
*/
|
||||
public function testAInvalidEmails($email)
|
||||
public function testInvalidEmails($email)
|
||||
{
|
||||
$this->assertFalse($this->validator->isValid($email));
|
||||
}
|
||||
@@ -53,9 +77,12 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
public function getInvalidEmails()
|
||||
{
|
||||
return array(
|
||||
array('test@example.com test'),
|
||||
array('user name@example.com'),
|
||||
array('user name@example.com'),
|
||||
array('example.@example.co.uk'),
|
||||
array('example@example@example.co.uk'),
|
||||
array('(fabien_potencier@example.fr)'),
|
||||
array('(test_exampel@example.fr)'),
|
||||
array('example(example)example@example.co.uk'),
|
||||
array('.example@localhost'),
|
||||
array('ex\ample@localhost'),
|
||||
@@ -65,6 +92,37 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
array('username@ example . com'),
|
||||
array('example@(fake).com'),
|
||||
array('example@(fake.com'),
|
||||
array('username@example,com'),
|
||||
array('usern,ame@example.com'),
|
||||
array('user[na]me@example.com'),
|
||||
array('"""@iana.org'),
|
||||
array('"\"@iana.org'),
|
||||
array('"test"test@iana.org'),
|
||||
array('"test""test"@iana.org'),
|
||||
array('"test"."test"@iana.org'),
|
||||
array('"test".test@iana.org'),
|
||||
array('"test"' . chr(0) . '@iana.org'),
|
||||
array('"test\"@iana.org'),
|
||||
array(chr(226) . '@iana.org'),
|
||||
array('test@' . chr(226) . '.org'),
|
||||
array('\r\ntest@iana.org'),
|
||||
array('\r\n test@iana.org'),
|
||||
array('\r\n \r\ntest@iana.org'),
|
||||
array('\r\n \r\ntest@iana.org'),
|
||||
array('\r\n \r\n test@iana.org'),
|
||||
array('test@iana.org \r\n'),
|
||||
array('test@iana.org \r\n '),
|
||||
array('test@iana.org \r\n \r\n'),
|
||||
array('test@iana.org \r\n\r\n'),
|
||||
array('test@iana.org \r\n\r\n '),
|
||||
array('test@iana/icann.org'),
|
||||
array('test@foo;bar.com'),
|
||||
array('test;123@foobar.com'),
|
||||
array('test@example..com'),
|
||||
array('email.email@email."'),
|
||||
array('test@email>'),
|
||||
array('test@email<'),
|
||||
array('test@email{'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -104,30 +162,58 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
array(EmailValidator::ERR_CR_NO_LF, "example@exa\rmple.co.uk"),
|
||||
array(EmailValidator::ERR_CR_NO_LF, "example@[\r]"),
|
||||
array(EmailValidator::ERR_CR_NO_LF, "exam\rple@example.co.uk"),
|
||||
array(EmailValidator::ERR_CR_NO_LF, "\"\r\"@localhost"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getInvalidEmailsWithWarnings
|
||||
*/
|
||||
public function testInvalidEmailsWithWarningsCheck($warnings, $email)
|
||||
public function testValidEmailsWithWarningsCheck($warnings, $email)
|
||||
{
|
||||
$this->assertTrue($this->validator->isValid($email, true));
|
||||
|
||||
$this->assertEquals($warnings, $this->validator->getWarnings());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getInvalidEmailsWithWarnings
|
||||
*/
|
||||
public function testInvalidEmailsWithDnsCheckAndStrictMode($warnings, $email)
|
||||
{
|
||||
$this->assertFalse($this->validator->isValid($email, true, true));
|
||||
|
||||
$this->assertEquals($warnings, $this->validator->getWarnings());
|
||||
}
|
||||
|
||||
public function getInvalidEmailsWithWarnings()
|
||||
{
|
||||
return array(
|
||||
array(array( EmailValidator::DEPREC_CFWS_NEAR_AT,), 'example @example.co.uk'),
|
||||
array(array( EmailValidator::DEPREC_CFWS_NEAR_AT,), 'example@ example.co.uk'),
|
||||
array(array( EmailValidator::CFWS_COMMENT,), 'example@example(examplecomment).co.uk'),
|
||||
array(
|
||||
array(
|
||||
EmailValidator::DEPREC_CFWS_NEAR_AT,
|
||||
EmailValidator::DNSWARN_NO_RECORD
|
||||
),
|
||||
'example @example.co.uk'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
EmailValidator::DEPREC_CFWS_NEAR_AT,
|
||||
EmailValidator::DNSWARN_NO_RECORD
|
||||
),
|
||||
'example@ example.co.uk'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
EmailValidator::CFWS_COMMENT,
|
||||
EmailValidator::DNSWARN_NO_RECORD
|
||||
),
|
||||
'example@example(examplecomment).co.uk'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
EmailValidator::CFWS_COMMENT,
|
||||
EmailValidator::DEPREC_CFWS_NEAR_AT,
|
||||
EmailValidator::DNSWARN_NO_RECORD,
|
||||
),
|
||||
'example(examplecomment)@example.co.uk'
|
||||
),
|
||||
@@ -135,9 +221,18 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
array(
|
||||
EmailValidator::RFC5321_QUOTEDSTRING,
|
||||
EmailValidator::CFWS_FWS,
|
||||
EmailValidator::DNSWARN_NO_RECORD,
|
||||
),
|
||||
"\"\t\"@example.co.uk"
|
||||
),
|
||||
array(
|
||||
array(
|
||||
EmailValidator::RFC5321_QUOTEDSTRING,
|
||||
EmailValidator::CFWS_FWS,
|
||||
EmailValidator::DNSWARN_NO_RECORD
|
||||
),
|
||||
"\"\r\"@example.co.uk"
|
||||
),
|
||||
array(
|
||||
array(
|
||||
EmailValidator::RFC5321_ADDRESSLITERAL,
|
||||
@@ -226,12 +321,14 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
array(
|
||||
array(
|
||||
EmailValidator::RFC5321_QUOTEDSTRING,
|
||||
EmailValidator::DNSWARN_NO_RECORD
|
||||
),
|
||||
'"example"@example.co.uk'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
EmailValidator::RFC5322_LOCAL_TOOLONG,
|
||||
EmailValidator::DNSWARN_NO_RECORD
|
||||
),
|
||||
'too_long_localpart_too_long_localpart_too_long_localpart_too_long_localpart@example.co.uk'
|
||||
),
|
||||
@@ -262,6 +359,17 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
'parttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'.
|
||||
'toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpar'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
EmailValidator::DNSWARN_NO_RECORD,
|
||||
),
|
||||
'test@test'
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidEmailsWithStrict()
|
||||
{
|
||||
$this->assertFalse($this->validator->isValid('"test"@test', false, true));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user