Compare commits

..

37 Commits

Author SHA1 Message Date
Eduardo Gulias Davis 39b451bb2b #19 and #10 - Improvement of QuotedPart parsing 2014-09-02 00:35:48 +02:00
Eduardo Gulias Davis 65afaf3f2b Change DNS validation to default to true instead of false by @brianfreytag 2014-08-22 01:08:16 +02:00
Eduardo Gulias Davis 82f8514d89 COMMA is not allowed in domain part 2014-08-22 01:05:58 +02:00
Eduardo Gulias Davis 440bc61237 Merge pull request #17 from brianfreytag/remove_dns_requirement
Change DNS validation to default to true instead of false
2014-08-21 23:47:24 +02:00
Brian Freytag a96f0822e0 Change DNS validation to default to true instead of false
There shouldn't be a dependency on the checkDNS() method in order to
validate strict = true. We should only be concerned about the DNS if the
library is specifically told to check DNS, so we default the DNS
variable to true and then set it to false if checkDNS is set to true and
checkDNS() returns false.
2014-08-20 15:05:30 -04:00
Eduardo Gulias Davis bb530a1930 Removed class comment 2014-07-06 19:21:29 +02:00
Eduardo Gulias Davis e0691bc67d Removed not needed test 2014-07-06 19:03:12 +02:00
Eduardo Gulias Davis 07a21c63d5 #2 - Scrutinizer badge 2014-07-03 13:10:41 +02:00
Eduardo Gulias Davis 46383ef57a [WIP] - ordering classes 2014-07-03 12:19:54 +02:00
Eduardo Gulias Davis 2312070f3a [WIP] - parse comments improvement 2014-07-03 01:00:20 +02:00
Eduardo Gulias Davis f7402dd8dc [WIP] - Comments bug and comments improvemente 2014-07-03 00:55:00 +02:00
Eduardo Gulias Davis 679128b980 Uncommented test 2014-07-02 18:53:46 +02:00
Eduardo Gulias Davis 6306334f79 [WIP] - test passing for the new structure 2014-07-02 18:17:56 +02:00
Eduardo Gulias Davis 8381eb3399 [WIP] - Chopping things down to make them more readable 2014-06-17 12:03:30 +02:00
Eduardo Gulias Davis 045ebd2f8e Refactor of domain exceptions 2014-05-08 18:26:53 +02:00
Eduardo Gulias Davis 86a022cd66 Refactor of domain part exceptions 2014-05-08 18:13:17 +02:00
Eduardo Gulias Davis 1866e51905 Reducing complexity for domainLiteral 2014-05-08 18:02:14 +02:00
Eduardo Gulias Davis a80300a3ba Reducing complexity for domainPart 2014-05-08 17:53:51 +02:00
Eduardo Gulias Davis 978b27b10a Reducing complexity 2014-05-08 17:47:44 +02:00
Eduardo Gulias Davis f679da922a Refactor to methods for IPv4 and 6 tags 2014-05-08 17:33:52 +02:00
Eduardo Gulias Davis 4b62335424 Refactoring domainPart 2014-05-08 16:23:54 +02:00
Eduardo Gulias Davis 46050f9bc8 Extracted method for CRLF in FWS 2014-05-08 13:30:27 +02:00
Eduardo Gulias Davis c3f2d33935 Extracted method 2014-05-08 13:16:08 +02:00
Eduardo Gulias Davis 73b80f1383 Small if refactor 2014-05-08 12:51:25 +02:00
Eduardo Gulias Davis 77ea3babe3 Improvements derived from scrutinizer-ci 2014-05-08 12:23:10 +02:00
Eduardo Gulias Davis 3d12808268 Improving code quality 2014-05-04 18:53:31 +02:00
Eduardo Gulias Davis c4354acc0e Adding new PHP versions on travis, cleaning old comments to a new document 2014-05-04 18:31:29 +02:00
Eduardo Gulias Davis 0964bfd101 Coverage badge 2014-04-25 11:54:11 +02:00
Eduardo Gulias Davis 8207889eaf Deps update 2014-04-25 11:44:20 +02:00
Eduardo Gulias Davis 0d75ea740d Travis small fix 2014-04-25 11:35:57 +02:00
Eduardo Gulias Davis 7bc2ec3ce0 #1 - Coveralls integration 2014-03-24 13:25:47 +01:00
Eduardo Gulias Davis f01e700ffe Using coverall.io with travis-ci 2014-03-07 12:15:00 +01:00
Eduardo Gulias Davis d3bf0dbe4b Using coverall.io 2014-03-07 12:14:03 +01:00
Eduardo Gulias Davis 35038cd507 Update LICENSE 2014-02-13 11:51:06 +01:00
Eduardo Gulias Davis f064a58611 FWS in localpart tests and FWS sorrounding '@' as invalid emails 2014-01-13 00:12:24 +01:00
Eduardo Gulias Davis 1a859493ee CS and minor changes 2014-01-12 18:13:13 +01:00
Eduardo Gulias Davis dd9541be28 Version branch update in README 2013-12-24 18:50:14 +01:00
17 changed files with 1239 additions and 610 deletions
+3
View File
@@ -0,0 +1,3 @@
service_name: travis-ci
src_dir: .
coverage_clover: build/logs/clover*.xml
-2
View File
@@ -1,4 +1,2 @@
report/
vendor/
.idea
.local.php.vimrc
+14 -3
View File
@@ -2,8 +2,19 @@ language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
before_script:
- wget http://getcomposer.org/composer.phar
- php composer.phar install
script: phpunit
- wget http://getcomposer.org/composer.phar
- php composer.phar install --dev --no-interaction
script:
- mkdir -p build/logs
- phpunit --coverage-clover build/logs/clover.xml
after_script:
- php vendor/bin/coveralls
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2013-2013 Eduardo Gulias Davis
Copyright (c) 2013 Eduardo Gulias Davis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+3 -3
View File
@@ -1,8 +1,8 @@
EmailValidator [![Build Status](https://travis-ci.org/egulias/EmailValidator.png?branch=master)](https://travis-ci.org/egulias/EmailValidator)
EmailValidator [![Build Status](https://travis-ci.org/egulias/EmailValidator.png?branch=master)](https://travis-ci.org/egulias/EmailValidator) [![Coverage Status](https://coveralls.io/repos/egulias/EmailValidator/badge.png?branch=master)](https://coveralls.io/r/egulias/EmailValidator?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/b18d473e-bd5a-4d88-a7b2-7aeaee0ebd7b/small.png)](https://insight.sensiolabs.com/projects/b18d473e-bd5a-4d88-a7b2-7aeaee0ebd7b)[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/egulias/EmailValidator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/egulias/EmailValidator/?branch=master)
=============================
##Installation##
Install via composer. Add to your current compooser.json ```require``` key: ```"egulias/email-validator":"1.0.*" ```
Install via composer. Add to your current compooser.json ```require``` key: ```"egulias/email-validator":"1.0.x-dev" ```
##Usage##
@@ -11,7 +11,7 @@ Simple example:
```php
<?php
use egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\EmailValidator;
$validator = new EmailValidator;
if ($validator->isValid($email)) {
+3
View File
@@ -17,6 +17,9 @@
"php": ">= 5.3.3",
"doctrine/lexer": "~1.0"
},
"require-dev" : {
"satooshi/php-coveralls": "dev-master"
},
"autoload": {
"psr-0": {
"Egulias\\": "src/"
Generated
+450 -54
View File
@@ -3,7 +3,7 @@
"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"
],
"hash": "ec97acf9cb298074065a27ad27d42b64",
"hash": "cca347a6b95164b1121e034b95f824ec",
"packages": [
{
"name": "doctrine/lexer",
@@ -56,42 +56,71 @@
"parser"
],
"time": "2013-01-12 18:59:04"
},
}
],
"packages-dev": [
{
"name": "symfony/translation",
"version": "v2.3.4",
"target-dir": "Symfony/Component/Translation",
"name": "guzzle/guzzle",
"version": "v3.9.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/Translation.git",
"reference": "65f888291f0896ad492f9abc6dc05c998373aded"
"url": "https://github.com/guzzle/guzzle3.git",
"reference": "92d9934f2fca1da15178c91239576ae26e505e60"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Translation/zipball/65f888291f0896ad492f9abc6dc05c998373aded",
"reference": "65f888291f0896ad492f9abc6dc05c998373aded",
"url": "https://api.github.com/repos/guzzle/guzzle3/zipball/92d9934f2fca1da15178c91239576ae26e505e60",
"reference": "92d9934f2fca1da15178c91239576ae26e505e60",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"ext-curl": "*",
"php": ">=5.3.3",
"symfony/event-dispatcher": "~2.1"
},
"replace": {
"guzzle/batch": "self.version",
"guzzle/cache": "self.version",
"guzzle/common": "self.version",
"guzzle/http": "self.version",
"guzzle/inflection": "self.version",
"guzzle/iterator": "self.version",
"guzzle/log": "self.version",
"guzzle/parser": "self.version",
"guzzle/plugin": "self.version",
"guzzle/plugin-async": "self.version",
"guzzle/plugin-backoff": "self.version",
"guzzle/plugin-cache": "self.version",
"guzzle/plugin-cookie": "self.version",
"guzzle/plugin-curlauth": "self.version",
"guzzle/plugin-error-response": "self.version",
"guzzle/plugin-history": "self.version",
"guzzle/plugin-log": "self.version",
"guzzle/plugin-md5": "self.version",
"guzzle/plugin-mock": "self.version",
"guzzle/plugin-oauth": "self.version",
"guzzle/service": "self.version",
"guzzle/stream": "self.version"
},
"require-dev": {
"symfony/config": "~2.0",
"symfony/yaml": "~2.2"
},
"suggest": {
"symfony/config": "",
"symfony/yaml": ""
"doctrine/cache": "~1.3",
"monolog/monolog": "~1.0",
"phpunit/phpunit": "3.7.*",
"psr/log": "~1.0",
"symfony/class-loader": "~2.1",
"zendframework/zend-cache": "2.*,<2.3",
"zendframework/zend-log": "2.*,<2.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
"dev-master": "3.8-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Translation\\": ""
"Guzzle": "src/",
"Guzzle\\Tests": "tests/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -100,49 +129,159 @@
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
"name": "Guzzle Community",
"homepage": "https://github.com/guzzle/guzzle/contributors"
}
],
"description": "Symfony Translation Component",
"homepage": "http://symfony.com",
"time": "2013-08-26 05:49:51"
"description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
],
"time": "2014-05-07 17:04:22"
},
{
"name": "symfony/validator",
"version": "dev-master",
"target-dir": "Symfony/Component/Validator",
"name": "psr/log",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/Validator.git",
"reference": "d98ac2aac2cccb7032b08cd79eeb579ed35c9a66"
"url": "https://github.com/php-fig/log.git",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Validator/zipball/d98ac2aac2cccb7032b08cd79eeb579ed35c9a66",
"reference": "d98ac2aac2cccb7032b08cd79eeb579ed35c9a66",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-0": {
"Psr\\Log\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2012-12-21 11:40:51"
},
{
"name": "satooshi/php-coveralls",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/satooshi/php-coveralls.git",
"reference": "b7271847c84d160f5b0aae83e45c225e8ffc96f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/b7271847c84d160f5b0aae83e45c225e8ffc96f4",
"reference": "b7271847c84d160f5b0aae83e45c225e8ffc96f4",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-simplexml": "*",
"guzzle/guzzle": ">=3.0",
"php": ">=5.3",
"psr/log": "1.0.0",
"symfony/config": ">=2.0",
"symfony/console": ">=2.0",
"symfony/stopwatch": ">=2.2",
"symfony/yaml": ">=2.0"
},
"require-dev": {
"apigen/apigen": "2.8.*@stable",
"pdepend/pdepend": "dev-master",
"phpmd/phpmd": "dev-master",
"phpunit/php-invoker": ">=1.1.0,<1.2.0",
"phpunit/phpunit": "3.7.*@stable",
"sebastian/finder-facade": "dev-master",
"sebastian/phpcpd": "1.4.*@stable",
"squizlabs/php_codesniffer": "1.4.*@stable",
"theseer/fdomdocument": "dev-master"
},
"suggest": {
"symfony/http-kernel": "Allows Symfony integration"
},
"bin": [
"composer/bin/coveralls"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.7-dev"
}
},
"autoload": {
"psr-0": {
"Satooshi\\Component": "src/",
"Satooshi\\Bundle": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kitamura Satoshi",
"email": "with.no.parachute@gmail.com",
"homepage": "https://www.facebook.com/satooshi.jp"
}
],
"description": "PHP client library for Coveralls API",
"homepage": "https://github.com/satooshi/php-coveralls",
"keywords": [
"ci",
"coverage",
"github",
"test"
],
"time": "2014-05-14 13:09:37"
},
{
"name": "symfony/config",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Config",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
"reference": "2effc67af6f21a0d267210b72d0b0b691d113528"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/2effc67af6f21a0d267210b72d0b0b691d113528",
"reference": "2effc67af6f21a0d267210b72d0b0b691d113528",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/translation": "~2.0"
},
"require-dev": {
"symfony/config": "~2.2",
"symfony/http-foundation": "~2.1",
"symfony/intl": "~2.3",
"symfony/yaml": "~2.0"
},
"suggest": {
"doctrine/common": "",
"symfony/config": "",
"symfony/http-foundation": "",
"symfony/intl": "",
"symfony/yaml": ""
"symfony/filesystem": "~2.3"
},
"type": "library",
"extra": {
@@ -152,7 +291,7 @@
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Validator\\": ""
"Symfony\\Component\\Config\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -162,27 +301,284 @@
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Validator Component",
"description": "Symfony Config Component",
"homepage": "http://symfony.com",
"time": "2013-08-19 20:44:22"
"time": "2014-04-22 08:11:06"
},
{
"name": "symfony/console",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "24f723436e62598c9dddee2a8573d6992504dc5d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/24f723436e62598c9dddee2a8573d6992504dc5d",
"reference": "24f723436e62598c9dddee2a8573d6992504dc5d",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/event-dispatcher": "~2.1"
},
"suggest": {
"symfony/event-dispatcher": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Console\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com",
"time": "2014-05-14 21:48:29"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.4.5",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "e539602e5455aa086c0e81e604745af7789e4d8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e539602e5455aa086c0e81e604745af7789e4d8a",
"reference": "e539602e5455aa086c0e81e604745af7789e4d8a",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/dependency-injection": "~2.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2014-04-16 10:34:31"
},
{
"name": "symfony/filesystem",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/a3af8294bcce4a7c1b2892363b0c9d8109affad4",
"reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Filesystem\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com",
"time": "2014-04-16 10:34:31"
},
{
"name": "symfony/stopwatch",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Stopwatch",
"source": {
"type": "git",
"url": "https://github.com/symfony/Stopwatch.git",
"reference": "343bcc0360f2c22f371884b8f6a9fee8d1aa431a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/343bcc0360f2c22f371884b8f6a9fee8d1aa431a",
"reference": "343bcc0360f2c22f371884b8f6a9fee8d1aa431a",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Stopwatch\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Stopwatch Component",
"homepage": "http://symfony.com",
"time": "2014-04-18 20:37:09"
},
{
"name": "symfony/yaml",
"version": "v2.4.5",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "fd22bb88c3a6f73c898b39bec185a9e211b06265"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/fd22bb88c3a6f73c898b39bec185a9e211b06265",
"reference": "fd22bb88c3a6f73c898b39bec185a9e211b06265",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Yaml\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2014-05-12 09:27:48"
}
],
"packages-dev": [
],
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": {
"symfony/validator": 20
"satooshi/php-coveralls": 20
},
"platform": {
"php": ">= 5.3.3"
+69
View File
@@ -0,0 +1,69 @@
Email length
------------
http://tools.ietf.org/html/rfc5321#section-4.1.2
Forward-path = Path
Path = "<" [ A-d-l ":" ] Mailbox ">"
http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
http://tools.ietf.org/html/rfc1035#section-2.3.4
DNS
---
http://tools.ietf.org/html/rfc5321#section-2.3.5
Names that can
be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
in Section 5) are permitted, as are CNAME RRs whose targets can be
resolved, in turn, to MX or address RRs.
http://tools.ietf.org/html/rfc5321#section-5.1
The lookup first attempts to locate an MX record associated with the
name. If a CNAME record is found, the resulting name is processed as
if it were the initial name. ... If an empty list of MXs is returned,
the address is treated as if it was associated with an implicit MX
RR, with a preference of 0, pointing to that host.
is_email() author's note: We will regard the existence of a CNAME to be
sufficient evidence of the domain's existence. For performance reasons
we will not repeat the DNS lookup for the CNAME's target, but we will
raise a warning because we didn't immediately find an MX record.
Check for TLD addresses
-----------------------
TLD addresses are specifically allowed in RFC 5321 but they are
unusual to say the least. We will allocate a separate
status to these addresses on the basis that they are more likely
to be typos than genuine addresses (unless we've already
established that the domain does have an MX record)
http://tools.ietf.org/html/rfc5321#section-2.3.5
In the case
of a top-level domain used by itself in an email address, a single
string is used without any dots. This makes the requirement,
described in more detail below, that only fully-qualified domain
names appear in SMTP transactions on the public Internet,
particularly important where top-level domains are involved.
TLD format
----------
The format of TLDs has changed a number of times. The standards
used by IANA have been largely ignored by ICANN, leading to
confusion over the standards being followed. These are not defined
anywhere, except as a general component of a DNS host name (a label).
However, this could potentially lead to 123.123.123.123 being a
valid DNS name (rather than an IP address) and thereby creating
an ambiguity. The most authoritative statement on TLD formats that
the author can find is in a (rejected!) erratum to RFC 1123
submitted by John Klensin, the author of RFC 5321:
http://www.rfc-editor.org/errata_search.php?rfc=1123&eid=1353
However, a valid host name can never have the dotted-decimal
form #.#.#.#, since this change does not permit the highest-level
component label to start with a digit even if it is not all-numeric.
Comments
--------
Comments at the start of the domain are deprecated in the text
Comments at the start of a subdomain are obs-domain
(http://tools.ietf.org/html/rfc5322#section-3.4.1)
+30 -33
View File
@@ -9,29 +9,29 @@ class EmailLexer extends AbstractLexer
//ASCII values
const C_DEL = 127;
const C_NUL = 0;
const S_AT = 64;//'@';
const S_BACKSLASH = 92;//'\\';
const S_DOT = 46;//'.';
const S_DQUOTE = 34;//'"';
const S_OPENPARENTHESIS = 49;//'(';
const S_CLOSEPARENTHESIS = 261;//')';
const S_OPENBRACKET = 262;//'[';
const S_CLOSEBRACKET = 263;//']';
const S_HYPHEN = 264;//'-';
const S_COLON = 265;//':';
const S_DOUBLECOLON = 266;//'::';
const S_SP = 267;//' ';
const S_HTAB = 268;//"\t";
const S_CR = 269;//"\r";
const S_LF = 270;//"\n";
const S_IPV6TAG = 271;//'IPv6:';
const S_LOWERTHAN = 272;//'<'
const S_GREATERTHAN = 273;//'>'
const S_COMMA = 274;//','
const S_SEMICOLON = 275;//';'
const S_OPENQBRACKET = 276;//'[';
const S_CLOSEQBRACKET = 277;//']';
const S_EMPTY = null;//'';
const S_AT = 64;
const S_BACKSLASH = 92;
const S_DOT = 46;
const S_DQUOTE = 34;
const S_OPENPARENTHESIS = 49;
const S_CLOSEPARENTHESIS = 261;
const S_OPENBRACKET = 262;
const S_CLOSEBRACKET = 263;
const S_HYPHEN = 264;
const S_COLON = 265;
const S_DOUBLECOLON = 266;
const S_SP = 267;
const S_HTAB = 268;
const S_CR = 269;
const S_LF = 270;
const S_IPV6TAG = 271;
const S_LOWERTHAN = 272;
const S_GREATERTHAN = 273;
const S_COMMA = 274;
const S_SEMICOLON = 275;
const S_OPENQBRACKET = 276;
const S_CLOSEQBRACKET = 277;
const S_EMPTY = null;
const GENERIC = 300;
const CRLF = 301;
const INVALID = 302;
@@ -73,11 +73,8 @@ class EmailLexer extends AbstractLexer
protected $previous;
/**
* find
*
* @param mixed $type
*
* @throws UnexpectedValueException
* @param $type
* @throws \UnexpectedValueException
*/
public function find($type)
{
@@ -87,7 +84,7 @@ class EmailLexer extends AbstractLexer
if (!$search->lookahead) {
throw new \UnexpectedValueException($type . ' not found');
}
return true;
}
/**
@@ -120,13 +117,13 @@ class EmailLexer extends AbstractLexer
protected function getCatchablePatterns()
{
return array(
'[a-zA-Z_]+[4,6]?',
'[a-zA-Z_]+[46]?',
'[0-9]+',
'\r\n',
'::',
'\s+',
'[\x1-\x1F]+',
'.'
'[\x10-\x1F]+',
'.',
);
}
@@ -153,7 +150,7 @@ class EmailLexer extends AbstractLexer
return $this->charValue[$value];
}
if (preg_match('/[\x1-\x1F]+/', $value)) {
if (preg_match('/[\x10-\x1F]+/', $value)) {
return self::INVALID;
}
+37 -414
View File
@@ -2,6 +2,9 @@
namespace Egulias\EmailValidator;
use Egulias\EmailValidator\Parser\DomainPart;
use Egulias\EmailValidator\Parser\LocalPart;
/**
* EmailParser
*
@@ -9,55 +12,46 @@ namespace Egulias\EmailValidator;
*/
class EmailParser
{
const EMAIL_MAX_LENGTH = 254;
protected $warnings = array();
protected $domainPart = '';
protected $localPart = '';
protected $lexer;
protected $localPartParser;
protected $domainPartParser;
public function __construct(EmailLexer $lexer)
{
$this->lexer = $lexer;
$this->localPartParser = new LocalPart($this->lexer);
$this->domainPartParser = new DomainPart($this->lexer);
}
/**
* parse
*
* @param string $str
*
* @return array
*/
public function parse($str)
{
$this->lexer->setInput($str);
$this->lexer->moveNext();
$this->lexer->moveNext();
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
if (!$this->hasAtToken()) {
throw new \InvalidArgumentException('ERR_NOLOCALPART');
}
$this->parseLocalPart();
$this->parseDomainPart();
$parts = explode('@', $str);
if (strlen($parts[0] . '@' . $this->domainPart) > 254) {
// http://tools.ietf.org/html/rfc5321#section-4.1.2
// Forward-path = Path
//
// Path = "<" [ A-d-l ":" ] Mailbox ">"
//
// http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
// http://tools.ietf.org/html/rfc1035#section-2.3.4
$this->warnings[] = EmailValidator::RFC5322_TOOLONG;
}
$this->localPartParser->parse($str);
$this->domainPartParser->parse($str);
return array('local' => $parts[0], 'domain' => $this->domainPart);
$this->setParts($str);
return array('local' => $this->localPart, 'domain' => $this->domainPart);
}
/**
* getWarnings
*
* @return array
*/
public function getWarnings()
{
$localPartWarnings = $this->localPartParser->getWarnings();
$domainPartWarnings = $this->domainPartParser->getWarnings();
$this->warnings = array_merge($localPartWarnings, $domainPartWarnings);
$this->addLongEmailWarning($this->localPart, $this->domainPart);
return $this->warnings;
}
@@ -66,399 +60,28 @@ class EmailParser
return $this->domainPart;
}
/**
* parseDomainPart
*
*/
private function parseDomainPart()
protected function setParts($email)
{
$domain = '';
$parts = explode('@', $email);
$this->domainPart = $this->domainPartParser->getDomainPart();
$this->localPart = $parts[0];
}
protected function hasAtToken()
{
$this->lexer->moveNext();
$this->lexer->moveNext();
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
throw new \InvalidArgumentException('ERR_CONSECUTIVEATS');
return false;
}
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');
}
// Comments at the start of the domain are deprecated in the text
// Comments at the start of a subdomain are obs-domain
// (http://tools.ietf.org/html/rfc5322#section-3.4.1)
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->warnings[] = EmailValidator::DEPREC_COMMENT;
$this->parseComments();
}
do {
$prev = $this->lexer->getPrevious();
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_OPENPARENTHESIS) {
$this->parseComments();
$this->lexer->moveNext();
}
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new \InvalidArgumentException('ERR_CONSECUTIVEDOTS');
}
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_OPENQBRACKET) {
try {
$this->lexer->find(EmailLexer::S_CLOSEQBRACKET);
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_EXPECTING_DOMLIT_CLOSE');
}
$this->parseDomainLiteral();
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET) {
try {
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_EXPECTING_DOMLIT_CLOSE');
}
$this->parseDomainLiteral();
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
&& $this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
$prev['type'] === EmailLexer::GENERIC &&
strlen($prev['value']) > 63
) {
$this->warnings[] = EmailValidator::RFC5322_LABEL_TOOLONG;
}
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
) {
$this->parseFWS();
}
$domain .= $this->lexer->token['value'];
$this->lexer->moveNext();
} while ($this->lexer->token);
$prev = $this->lexer->getPrevious();
$length = strlen($prev['value']);
if ($prev['type'] === EmailLexer::S_DOT) {
throw new \InvalidArgumentException('ERR_DOT_END');
}
if ($prev['type'] === EmailLexer::S_HYPHEN) {
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
}
if ($length > 254) {
$this->warnings[] = EmailValidator::RFC5322_DOMAIN_TOOLONG;
}
if ($prev['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException('ERR_FWS_CRLF_END');
}
$this->domainPart = $domain;
return true;
}
private function parseDomainLiteral()
protected function addLongEmailWarning($localPart, $parsedDomainPart)
{
$IPv6TAG = false;
$addressLiteral = '';
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
// Address starts with a single colon
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {
$this->warnings[] = EmailValidator::RFC5322_TOOLONG;
}
if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
try {
$lexer = clone $this->lexer;
$lexer->moveNext();
if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
} catch (\Exception $e) {
}
}
do {
if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
} elseif ($this->lexer->token['type'] === EmailLexer::INVALID ||
$this->lexer->token['type'] === EmailLexer::C_DEL ||
$this->lexer->token['type'] === EmailLexer::S_LF
) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
}
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) {
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_HTAB, EmailLexer::S_SP))) {
$this->warnings[] = EmailValidator::CFWS_FWS;
$this->parseFWS();
}
if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
throw new \InvalidArgumentException("ERR_CR_NO_LF");
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
$addressLiteral .= $this->lexer->token['value'];
$this->lexer->moveNext();
$this->validateQuotedPair();
}
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) {
break;
}
if ($this->lexer->token['type'] === EmailLexer::S_SP ||
$this->lexer->token['type'] === EmailLexer::S_HTAB ||
$this->lexer->token['type'] === EmailLexer::CRLF
) {
$this->parseFWS();
}
if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
$IPv6TAG = true;
}
$addressLiteral .= $this->lexer->token['value'];
} while ($this->lexer->moveNext());
// Revision 2.7: Daniel Marschall's new IPv6 testing strategy
$prev = $this->lexer->getPrevious();
if ($prev['type'] === EmailLexer::S_COLON) {
// Address ends with a single colon
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONEND;
}
$addressLiteral = str_replace('[', '', $addressLiteral);
$maxGroups = 8;
$matchesIP = array();
// Extract IPv4 part from the end of the address-literal (if there is one)
if (preg_match(
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
$addressLiteral,
$matchesIP
) > 0
) {
$index = strrpos($addressLiteral, $matchesIP[0]);
if ($index === 0) {
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
return;
}
// Convert IPv4 part to IPv6 format for further testing
$addressLiteral = substr($addressLiteral, 0, $index) . '0:0';
}
if (!$IPv6TAG) {
$this->warnings[] = EmailValidator::RFC5322_DOMAINLITERAL;
return;
}
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
$IPv6 = substr($addressLiteral, 5);
//Daniel Marschall's new IPv6 testing strategy
$matchesIP = explode(':', $IPv6);
$groupCount = count($matchesIP);
$colons = strpos($IPv6, '::');
if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
// Check for unmatched characters
$this->warnings[] = EmailValidator::RFC5322_IPV6_BADCHAR;
}
if ($colons === false) {
// We need exactly the right number of groups
if ($groupCount !== $maxGroups) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_GRPCOUNT;
}
} else {
if ($colons !== strrpos($IPv6, '::')) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_2X2XCOLON;
} else {
if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
// RFC 4291 allows :: at the start or end of an address
//with 7 other groups in addition
++$maxGroups;
}
if ($groupCount > $maxGroups) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_MAXGRPS;
} elseif ($groupCount === $maxGroups) {
// Eliding a single "::"
$this->warnings[] = EmailValidator::RFC5321_IPV6DEPRECATED;
}
}
}
return $addressLiteral;
}
/**
* validateQuotedPair
*
* @TODO This needs to be reviewed
*/
private function validateQuotedPair()
{
if (!($this->lexer->token['type'] === EmailLexer::INVALID
|| $this->lexer->token['type'] === EmailLexer::C_DEL)) {
throw new \InvalidArgumentException('ERR_EXPECTING_QPAIR');
}
// SP & HTAB are allowed
$this->warnings[] = EmailValidator::DEPREC_QP;
}
private function parseLocalPart()
{
$closingQuote = false;
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
$previous = $this->lexer->getPrevious();
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
throw new \InvalidArgumentException('ERR_DOT_START');
}
if ($this->lexer->token['type'] === EmailLexer::S_DQUOTE) {
//@TODO this can be improbed
if (!$closingQuote) {
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);
$closingQuote = true;
} catch (\Exception $e) {
throw new \InvalidArgumentException('ERR_UNCLOSEDQUOTEDSTR');
}
}
}
//Comments
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments();
}
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
$this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new \InvalidArgumentException('ERR_CONSECUTIVEDOTS');
}
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_AT)) {
throw new \InvalidArgumentException('ERR_DOT_END');
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
//if ($this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
// $this->warnings[] = EmailValidator::DEPREC_QP;
//}
if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
}
if ($this->lexer->isNextTokenAny(
array(
EmailLexer::INVALID, EmailLexer::S_LOWERTHAN, EmailLexer::S_GREATERTHAN
)
)
) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
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
) {
$this->parseFWS();
}
$this->lexer->moveNext();
}
$prev = $this->lexer->getPrevious();
if (strlen($prev['value']) > EmailValidator::RFC5322_LOCAL_TOOLONG) {
$this->warnings[] = EmailValidator::RFC5322_LOCAL_TOOLONG;
}
}
/**
* parseComments
*
* @return string the the comment
*/
private function parseComments()
{
$this->warnings[] = EmailValidator::CFWS_COMMENT;
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
try {
$this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_UNCLOSEDCOMMENT');
}
//scaping in a comment
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
$this->warnings[] = EmailValidator::DEPREC_QP;
}
}
$this->lexer->moveNext();
}
$this->lexer->moveNext();
if ($this->lexer->isNextToken(EmailLexer::GENERIC) || EmailLexer::S_EMPTY === $this->lexer->peek()) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
}
}
/**
* parseFWS
*
* @throw InvalidArgumentException
*/
private function parseFWS()
{
$previous = $this->lexer->getPrevious();
if ($this->lexer->token['type'] === EmailLexer::CRLF && $this->lexer->isNextToken(EmailLexer::CRLF)) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_X2");
}
if ($this->lexer->token['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException("ERR_CR_NO_LF");
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB)) &&
$this->lexer->token['type'] === EmailLexer::CRLF ) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_END");
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $this->lexer->token['type'] !== EmailLexer::S_SP) {
throw new \InvalidArgumentException("ERR_ATEXT_AFTER_CFWS");
}
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new \InvalidArgumentException('ERR_EXPECTING_CTEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
} else {
$this->warnings[] = EmailValidator::CFWS_FWS;
}
}
}
+16 -81
View File
@@ -2,6 +2,8 @@
namespace Egulias\EmailValidator;
use Egulias\EmailValidator\Parser\LocalPart;
/**
* EmailValidator
*
@@ -9,7 +11,6 @@ namespace Egulias\EmailValidator;
*/
class EmailValidator
{
// Address is invalid for any purpose
const ERR_CONSECUTIVEATS = 128;
const ERR_EXPECTING_DTEXT = 129;
const ERR_NOLOCALPART = 130;
@@ -34,7 +35,6 @@ class EmailValidator
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;
@@ -69,7 +69,6 @@ class EmailValidator
protected $warnings = array();
protected $error;
protected $threshold = 255;
protected $emailParts = array();
public function __construct()
{
@@ -80,7 +79,6 @@ class EmailValidator
{
try {
$this->parser->parse((string)$email);
$this->emailParts = explode('@', $email);
$this->warnings = $this->parser->getWarnings();
} catch (\Exception $e) {
$rClass = new \ReflectionClass($this);
@@ -88,9 +86,11 @@ class EmailValidator
return false;
}
$dns = true;
if ($checkDNS) {
$dns = $this->checkDNS();
}
if ($this->hasWarnings() && ((int) max($this->warnings) > $this->threshold)) {
$this->error = self::ERR_DEPREC_REACHED;
@@ -101,8 +101,6 @@ class EmailValidator
}
/**
* hasWarnings
*
* @return boolean
*/
public function hasWarnings()
@@ -111,8 +109,6 @@ class EmailValidator
}
/**
* getWarnings
*
* @return array
*/
public function getWarnings()
@@ -121,8 +117,6 @@ class EmailValidator
}
/**
* getError
*
* @return string
*/
public function getError()
@@ -131,8 +125,6 @@ class EmailValidator
}
/**
* setThreshols
*
* @param int $threshold
*
* @return EmailValidator
@@ -145,8 +137,6 @@ class EmailValidator
}
/**
* getThreshold
*
* @return int
*/
public function getThreshold()
@@ -164,80 +154,25 @@ class EmailValidator
return $checked;
}
// http://tools.ietf.org/html/rfc5321#section-2.3.5
// Names that can
// be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
// in Section 5) are permitted, as are CNAME RRs whose targets can be
// resolved, in turn, to MX or address RRs.
//
// http://tools.ietf.org/html/rfc5321#section-5.1
// The lookup first attempts to locate an MX record associated with the
// name. If a CNAME record is found, the resulting name is processed as
// if it were the initial name. ... If an empty list of MXs is returned,
// the address is treated as if it was associated with an implicit MX
// RR, with a preference of 0, pointing to that host.
//
// is_email() author's note: We will regard the existence of a CNAME to be
// sufficient evidence of the domain's existence. For performance reasons
// we will not repeat the DNS lookup for the CNAME's target, but we will
// raise a warning because we didn't immediately find an MX record.
$result = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX');
$checked = true;
if (!$result) {
// Domain can't be found in DNS
$this->warnings[] = self::DNSWARN_NO_RECORD;
$checked = false;
}
// Check for TLD addresses
// -----------------------
// TLD addresses are specifically allowed in RFC 5321 but they are
// unusual to say the least. We will allocate a separate
// status to these addresses on the basis that they are more likely
// to be typos than genuine addresses (unless we've already
// established that the domain does have an MX record)
//
// http://tools.ietf.org/html/rfc5321#section-2.3.5
// In the case
// of a top-level domain used by itself in an email address, a single
// string is used without any dots. This makes the requirement,
// described in more detail below, that only fully-qualified domain
// names appear in SMTP transactions on the public Internet,
// particularly important where top-level domains are involved.
//
// TLD format
// ----------
// The format of TLDs has changed a number of times. The standards
// used by IANA have been largely ignored by ICANN, leading to
// confusion over the standards being followed. These are not defined
// anywhere, except as a general component of a DNS host name (a label).
// However, this could potentially lead to 123.123.123.123 being a
// valid DNS name (rather than an IP address) and thereby creating
// an ambiguity. The most authoritative statement on TLD formats that
// the author can find is in a (rejected!) erratum to RFC 1123
// submitted by John Klensin, the author of RFC 5321:
//
// http://www.rfc-editor.org/errata_search.php?rfc=1123&eid=1353
// However, a valid host name can never have the dotted-decimal
// form #.#.#.#, since this change does not permit the highest-level
// component label to start with a digit even if it is not all-numeric.
if (!$checked && (!in_array(self::DNSWARN_NO_RECORD, $this->warnings) &&
!in_array(self::DNSWARN_NO_MX_RECORD, $this->warnings))
) {
if (in_array($this->warnings, RFC5322_DOMAINLITERAL)) {
$this->warnings[] = self::RFC5321_TLD;
}
//@TODO review how to test TLD
//if (isset($this->atomList[self::COMPONENT_DOMAIN][$this->elementCount][0]) &&
// is_numeric($this->atomList[self::COMPONENT_DOMAIN][$this->elementCount][0])
//) {
// $this->warnings[] = self::RFC5321_TLDNUMERIC;
//}
$this->addTLDWarnings();
}
return $checked;
}
protected function addTLDWarnings()
{
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)
) {
$this->warnings[] = self::RFC5321_TLD;
}
}
}
@@ -0,0 +1,299 @@
<?php
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)
{
$this->lexer->moveNext();
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
throw new \InvalidArgumentException('ERR_DOT_START');
}
if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) {
throw new \InvalidArgumentException('ERR_NODOMAIN');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->warnings[] = EmailValidator::DEPREC_COMMENT;
$this->parseDomainComments();
}
$domain = $this->doParseDomainPart();
$prev = $this->lexer->getPrevious();
$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) {
$this->warnings[] = EmailValidator::RFC5322_DOMAIN_TOOLONG;
}
if ($prev['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException('ERR_FWS_CRLF_END');
}
$this->domainPart = $domain;
}
public function getDomainPart()
{
return $this->domainPart;
}
public function checkIPV6Tag($addressLiteral, $maxGroups = 8)
{
$prev = $this->lexer->getPrevious();
if ($prev['type'] === EmailLexer::S_COLON) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONEND;
}
$IPv6 = substr($addressLiteral, 5);
//Daniel Marschall's new IPv6 testing strategy
$matchesIP = explode(':', $IPv6);
$groupCount = count($matchesIP);
$colons = strpos($IPv6, '::');
if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_BADCHAR;
}
if ($colons === false) {
// We need exactly the right number of groups
if ($groupCount !== $maxGroups) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_GRPCOUNT;
}
return;
}
if ($colons !== strrpos($IPv6, '::')) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_2X2XCOLON;
return;
}
if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
// RFC 4291 allows :: at the start or end of an address
//with 7 other groups in addition
++$maxGroups;
}
if ($groupCount > $maxGroups) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_MAXGRPS;
} elseif ($groupCount === $maxGroups) {
$this->warnings[] = EmailValidator::RFC5321_IPV6DEPRECATED;
}
}
protected function doParseDomainPart()
{
$domain = '';
do {
$prev = $this->lexer->getPrevious();
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments();
$this->lexer->moveNext();
}
$this->checkConsecutiveDots();
$this->checkDomainPartExceptions($prev);
if ($this->hasBrackets()) {
$this->parseDomainLiteral();
}
$this->checkLabelLength($prev);
if ($this->isFWS()) {
$this->parseFWS();
}
$domain .= $this->lexer->token['value'];
$this->lexer->moveNext();
} while ($this->lexer->token);
return $domain;
}
protected function parseDomainLiteral()
{
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
$lexer = clone $this->lexer;
$lexer->moveNext();
if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
}
return $this->doParseDomainLiteral();
}
protected function doParseDomainLiteral()
{
$IPv6TAG = false;
$addressLiteral = '';
do {
if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->token['type'] === EmailLexer::INVALID ||
$this->lexer->token['type'] === EmailLexer::C_DEL ||
$this->lexer->token['type'] === EmailLexer::S_LF
) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
}
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) {
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->isNextTokenAny(
array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
)) {
$this->warnings[] = EmailValidator::CFWS_FWS;
$this->parseFWS();
}
if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
throw new \InvalidArgumentException("ERR_CR_NO_LF");
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
$addressLiteral .= $this->lexer->token['value'];
$this->lexer->moveNext();
$this->validateQuotedPair();
}
if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
$IPv6TAG = true;
}
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) {
break;
}
$addressLiteral .= $this->lexer->token['value'];
} while ($this->lexer->moveNext());
$addressLiteral = str_replace('[', '', $addressLiteral);
$addressLiteral = $this->checkIPV4Tag($addressLiteral);
if (false === $addressLiteral) {
return $addressLiteral;
}
if (!$IPv6TAG) {
$this->warnings[] = EmailValidator::RFC5322_DOMAINLITERAL;
return $addressLiteral;
}
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
$this->checkIPV6Tag($addressLiteral);
return $addressLiteral;
}
protected function checkIPV4Tag($addressLiteral)
{
$matchesIP = array();
// Extract IPv4 part from the end of the address-literal (if there is one)
if (preg_match(
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
$addressLiteral,
$matchesIP
) > 0
) {
$index = strrpos($addressLiteral, $matchesIP[0]);
if ($index === 0) {
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
return false;
}
// Convert IPv4 part to IPv6 format for further testing
$addressLiteral = substr($addressLiteral, 0, $index) . '0:0';
}
return $addressLiteral;
}
protected function checkDomainPartExceptions($prev)
{
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()
{
if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) {
return false;
}
try {
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_EXPECTING_DOMLIT_CLOSE');
}
return true;
}
protected function checkLabelLength($prev)
{
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
$prev['type'] === EmailLexer::GENERIC &&
strlen($prev['value']) > 63
) {
$this->warnings[] = EmailValidator::RFC5322_LABEL_TOOLONG;
}
}
protected function parseDomainComments()
{
$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,94 @@
<?php
namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\EmailValidator;
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()) {
throw new \InvalidArgumentException('ERR_DOT_START');
}
$closingQuote = $this->checkDQUOTE($closingQuote);
if ($closingQuote && $parseDQuote) {
$this->parseDoubleQuote();
$parseDQuote = false;
}
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();
}
$prev = $this->lexer->getPrevious();
if (strlen($prev['value']) > EmailValidator::RFC5322_LOCAL_TOOLONG) {
$this->warnings[] = EmailValidator::RFC5322_LOCAL_TOOLONG;
}
}
protected function parseDoubleQuote()
{
$special = array (
EmailLexer::S_CR => true,
EmailLexer::S_HTAB => true,
EmailLexer::S_LF => true
);
$setSpecialsWarning = true;
$this->lexer->moveNext();
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) {
if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
$this->warnings[] = EmailValidator::CFWS_FWS;
$setSpecialsWarning = false;
}
$this->lexer->moveNext();
}
}
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');
}
}
}
@@ -0,0 +1,190 @@
<?php
namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\EmailValidator;
abstract class Parser
{
protected $warnings = array();
protected $lexer;
public function __construct(EmailLexer $lexer)
{
$this->lexer = $lexer;
}
public function getWarnings()
{
return $this->warnings;
}
abstract function parse($str);
/**
* validateQuotedPair
*/
protected function validateQuotedPair()
{
if (!($this->lexer->token['type'] === EmailLexer::INVALID
|| $this->lexer->token['type'] === EmailLexer::C_DEL)) {
throw new \InvalidArgumentException('ERR_EXPECTING_QPAIR');
}
$this->warnings[] = EmailValidator::DEPREC_QP;
}
/**
* @return string the the comment
* @throws \InvalidArgumentException
*/
protected function parseComments()
{
$this->isUnclosedComment();
$this->warnings[] = EmailValidator::CFWS_COMMENT;
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
$this->warnEscaping();
$this->lexer->moveNext();
}
$this->lexer->moveNext();
if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
}
}
protected function isUnclosedComment()
{
try {
$this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
return true;
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException('ERR_UNCLOSEDCOMMENT');
}
}
protected function parseFWS()
{
$previous = $this->lexer->getPrevious();
$this->checkCRLFInFWS();
if ($this->lexer->token['type'] === EmailLexer::S_CR) {
throw new \InvalidArgumentException("ERR_CR_NO_LF");
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
throw new \InvalidArgumentException("ERR_ATEXT_AFTER_CFWS");
}
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new \InvalidArgumentException('ERR_EXPECTING_CTEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
} else {
$this->warnings[] = EmailValidator::CFWS_FWS;
}
}
protected function checkConsecutiveDots()
{
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new \InvalidArgumentException('ERR_CONSECUTIVEDOTS');
}
}
protected function isFWS()
{
if ($this->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()
{
$previous = $this->lexer->getPrevious();
if ($previous['type'] === EmailLexer::S_BACKSLASH
&&
($this->lexer->token['type'] === EmailLexer::S_SP ||
$this->lexer->token['type'] === EmailLexer::S_HTAB)
) {
return true;
}
return false;
}
protected function warnEscaping()
{
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
return false;
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
return false;
}
$this->warnings[] = EmailValidator::DEPREC_QP;
return true;
}
protected function checkDQUOTE($hasClosingQuote)
{
if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) {
return $hasClosingQuote;
}
if ($hasClosingQuote) {
return $hasClosingQuote;
}
$previous = $this->lexer->getPrevious();
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
$this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING;
try {
$this->lexer->find(EmailLexer::S_DQUOTE);
$hasClosingQuote = true;
} catch (\Exception $e) {
throw new \InvalidArgumentException('ERR_UNCLOSEDQUOTEDSTR');
}
return $hasClosingQuote;
}
protected function checkCRLFInFWS()
{
if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
return;
}
if ($this->lexer->isNextToken(EmailLexer::CRLF)) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_X2");
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
throw new \InvalidArgumentException("ERR_FWS_CRLF_END");
}
}
}
@@ -36,6 +36,14 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
$this->assertEquals(EmailLexer::S_HTAB, $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(
@@ -1,15 +0,0 @@
<?php
namespace Egulias\Tests\EmailValidator;
use Egulias\EmailValidator\EmailParser;
class EmailParserTests extends \PHPUnit_Framework_TestCase
{
public function testParserExtendsLib()
{
$mock = $this->getMock('egulias\EmailValidator\EmailLexer');
$parser = new EmailParser($mock);
$this->markTestIncomplete();
}
}
@@ -34,17 +34,22 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
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'),
);
}
/**
* @dataProvider getInvalidEmails
*/
public function testAInvalidEmails($email)
public function testInvalidEmails($email)
{
$this->assertFalse($this->validator->isValid($email));
}
@@ -60,6 +65,13 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
array('ex\ample@localhost'),
array('example@local\host'),
array('example@localhost.'),
array('user name@example.com'),
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'),
);
}
@@ -99,14 +111,13 @@ 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));
@@ -133,6 +144,13 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
),
"\"\t\"@example.co.uk"
),
array(
array(
EmailValidator::RFC5321_QUOTEDSTRING,
EmailValidator::CFWS_FWS,
),
"\"\r\"@example.co.uk"
),
array(
array(
EmailValidator::RFC5321_ADDRESSLITERAL,
@@ -212,8 +230,8 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
),
array(
array(
EmailValidator::RFC5322_IPV6_COLONEND,
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::RFC5322_IPV6_COLONEND,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:]'