mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-08-31 12:40:28 +00:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| bb530a1930 | |||
| e0691bc67d | |||
| 07a21c63d5 | |||
| 46383ef57a | |||
| 2312070f3a | |||
| f7402dd8dc | |||
| 679128b980 | |||
| 6306334f79 | |||
| 8381eb3399 | |||
| 045ebd2f8e | |||
| 86a022cd66 | |||
| 1866e51905 | |||
| a80300a3ba | |||
| 978b27b10a | |||
| f679da922a | |||
| 4b62335424 | |||
| 46050f9bc8 | |||
| c3f2d33935 | |||
| 73b80f1383 | |||
| 77ea3babe3 | |||
| 3d12808268 | |||
| c4354acc0e | |||
| 0964bfd101 | |||
| 8207889eaf | |||
| 0d75ea740d | |||
| 7bc2ec3ce0 | |||
| f01e700ffe | |||
| d3bf0dbe4b | |||
| 35038cd507 |
@@ -0,0 +1,3 @@
|
||||
service_name: travis-ci
|
||||
src_dir: .
|
||||
coverage_clover: build/logs/clover*.xml
|
||||
+14
-3
@@ -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,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
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
EmailValidator [](https://travis-ci.org/egulias/EmailValidator) [](https://insight.sensiolabs.com/projects/b18d473e-bd5a-4d88-a7b2-7aeaee0ebd7b)
|
||||
#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
-1
@@ -10,13 +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"
|
||||
},
|
||||
"require-dev" : {
|
||||
"satooshi/php-coveralls": "dev-master"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Egulias\\": "src/"
|
||||
|
||||
Generated
+461
-68
@@ -1,28 +1,34 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "ec97acf9cb298074065a27ad27d42b64",
|
||||
"hash": "9e9dff0cc08c7292600453e681201e13",
|
||||
"packages": [
|
||||
{
|
||||
"name": "doctrine/lexer",
|
||||
"version": "v1.0",
|
||||
"version": "v1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/lexer.git",
|
||||
"reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb"
|
||||
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/2f708a85bb3aab5d99dab8be435abd73e0b18acb",
|
||||
"reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb",
|
||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c",
|
||||
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Doctrine\\Common\\Lexer\\": "lib/"
|
||||
@@ -33,20 +39,17 @@
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com",
|
||||
"homepage": "http://www.instaclick.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Johannes Schmitt",
|
||||
"email": "schmittjoh@gmail.com",
|
||||
"homepage": "http://jmsyst.com",
|
||||
"role": "Developer of wrapped JMSSerializerBundle"
|
||||
"email": "schmittjoh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
|
||||
@@ -55,43 +58,300 @@
|
||||
"lexer",
|
||||
"parser"
|
||||
],
|
||||
"time": "2013-01-12 18:59:04"
|
||||
},
|
||||
"time": "2014-09-09 13:34:57"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v2.3.4",
|
||||
"target-dir": "Symfony/Component/Translation",
|
||||
"name": "guzzle/guzzle",
|
||||
"version": "v3.9.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Translation.git",
|
||||
"reference": "65f888291f0896ad492f9abc6dc05c998373aded"
|
||||
"url": "https://github.com/guzzle/guzzle3.git",
|
||||
"reference": "54991459675c1a2924122afbb0e5609ade581155"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Translation/zipball/65f888291f0896ad492f9abc6dc05c998373aded",
|
||||
"reference": "65f888291f0896ad492f9abc6dc05c998373aded",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle3/zipball/54991459675c1a2924122afbb0e5609ade581155",
|
||||
"reference": "54991459675c1a2924122afbb0e5609ade581155",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"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": {
|
||||
"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": "3.9-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Guzzle": "src/",
|
||||
"Guzzle\\Tests": "tests/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Guzzle Community",
|
||||
"homepage": "https://github.com/guzzle/guzzle/contributors"
|
||||
}
|
||||
],
|
||||
"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-08-11 04:32:36"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"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": "2fbf803803d179ab1082807308a67bbd5a760c70"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/2fbf803803d179ab1082807308a67bbd5a760c70",
|
||||
"reference": "2fbf803803d179ab1082807308a67bbd5a760c70",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-simplexml": "*",
|
||||
"guzzle/guzzle": ">=2.7",
|
||||
"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 as 2.0.0",
|
||||
"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-11-11 15:35:34"
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v2.6.1",
|
||||
"target-dir": "Symfony/Component/Config",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Config.git",
|
||||
"reference": "84c0c150c1520995f09ea9e47e817068b353cb0f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Config/zipball/84c0c150c1520995f09ea9e47e817068b353cb0f",
|
||||
"reference": "84c0c150c1520995f09ea9e47e817068b353cb0f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/filesystem": "~2.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\Config\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Config Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2014-12-02 20:19:20"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v2.6.1",
|
||||
"target-dir": "Symfony/Component/Console",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Console.git",
|
||||
"reference": "ef825fd9f809d275926547c9e57cbf14968793e8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Console/zipball/ef825fd9f809d275926547c9e57cbf14968793e8",
|
||||
"reference": "ef825fd9f809d275926547c9e57cbf14968793e8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/config": "~2.0",
|
||||
"symfony/yaml": "~2.2"
|
||||
"psr/log": "~1.0",
|
||||
"symfony/event-dispatcher": "~2.1",
|
||||
"symfony/process": "~2.1"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/config": "",
|
||||
"symfony/yaml": ""
|
||||
"psr/log": "For using the console logger",
|
||||
"symfony/event-dispatcher": "",
|
||||
"symfony/process": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.3-dev"
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\Translation\\": ""
|
||||
"Symfony\\Component\\Console\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -99,60 +359,57 @@
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Translation Component",
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2013-08-26 05:49:51"
|
||||
"time": "2014-12-02 20:19:20"
|
||||
},
|
||||
{
|
||||
"name": "symfony/validator",
|
||||
"version": "dev-master",
|
||||
"target-dir": "Symfony/Component/Validator",
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v2.6.1",
|
||||
"target-dir": "Symfony/Component/EventDispatcher",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Validator.git",
|
||||
"reference": "d98ac2aac2cccb7032b08cd79eeb579ed35c9a66"
|
||||
"url": "https://github.com/symfony/EventDispatcher.git",
|
||||
"reference": "720fe9bca893df7ad1b4546649473b5afddf0216"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Validator/zipball/d98ac2aac2cccb7032b08cd79eeb579ed35c9a66",
|
||||
"reference": "d98ac2aac2cccb7032b08cd79eeb579ed35c9a66",
|
||||
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/720fe9bca893df7ad1b4546649473b5afddf0216",
|
||||
"reference": "720fe9bca893df7ad1b4546649473b5afddf0216",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/translation": "~2.0"
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/config": "~2.2",
|
||||
"symfony/http-foundation": "~2.1",
|
||||
"symfony/intl": "~2.3",
|
||||
"symfony/yaml": "~2.0"
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "~2.0",
|
||||
"symfony/dependency-injection": "~2.6",
|
||||
"symfony/expression-language": "~2.6",
|
||||
"symfony/stopwatch": "~2.2"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/common": "",
|
||||
"symfony/config": "",
|
||||
"symfony/http-foundation": "",
|
||||
"symfony/intl": "",
|
||||
"symfony/yaml": ""
|
||||
"symfony/dependency-injection": "",
|
||||
"symfony/http-kernel": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\Validator\\": ""
|
||||
"Symfony\\Component\\EventDispatcher\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -160,34 +417,170 @@
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
}
|
||||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2014-12-02 20:19:20"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v2.6.1",
|
||||
"target-dir": "Symfony/Component/Filesystem",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Filesystem.git",
|
||||
"reference": "ff6efc95256cb33031933729e68b01d720b5436b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/ff6efc95256cb33031933729e68b01d720b5436b",
|
||||
"reference": "ff6efc95256cb33031933729e68b01d720b5436b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\Filesystem\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Validator Component",
|
||||
"description": "Symfony Filesystem Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2013-08-19 20:44:22"
|
||||
"time": "2014-12-02 20:19:20"
|
||||
},
|
||||
{
|
||||
"name": "symfony/stopwatch",
|
||||
"version": "v2.6.1",
|
||||
"target-dir": "Symfony/Component/Stopwatch",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Stopwatch.git",
|
||||
"reference": "261abd360cfb6ac65ea93ffd82073e2011d034fc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/261abd360cfb6ac65ea93ffd82073e2011d034fc",
|
||||
"reference": "261abd360cfb6ac65ea93ffd82073e2011d034fc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\Stopwatch\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Stopwatch Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2014-12-02 20:19:20"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v2.6.1",
|
||||
"target-dir": "Symfony/Component/Yaml",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Yaml.git",
|
||||
"reference": "3346fc090a3eb6b53d408db2903b241af51dcb20"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Yaml/zipball/3346fc090a3eb6b53d408db2903b241af51dcb20",
|
||||
"reference": "3346fc090a3eb6b53d408db2903b241af51dcb20",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\Yaml\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2014-12-02 20:19:20"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
||||
],
|
||||
"aliases": [
|
||||
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"symfony/validator": 20
|
||||
"satooshi/php-coveralls": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">= 5.3.3"
|
||||
},
|
||||
"platform-dev": [
|
||||
|
||||
]
|
||||
"platform-dev": []
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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,17 +71,31 @@ class EmailLexer extends AbstractLexer
|
||||
'>' => self::S_GREATERTHAN,
|
||||
'{' => self::S_OPENQBRACKET,
|
||||
'}' => self::S_CLOSEQBRACKET,
|
||||
'' => self::S_EMPTY
|
||||
'' => self::S_EMPTY,
|
||||
'\0' => self::C_NUL,
|
||||
);
|
||||
|
||||
protected $invalidASCII = array(226 => 1,);
|
||||
|
||||
protected $hasInvalidTokens = false;
|
||||
|
||||
protected $previous;
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->hasInvalidTokens = false;
|
||||
parent::reset();
|
||||
}
|
||||
|
||||
public function hasInvalidTokens()
|
||||
{
|
||||
return $this->hasInvalidTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* find
|
||||
*
|
||||
* @param mixed $type
|
||||
*
|
||||
* @throws UnexpectedValueException
|
||||
* @param $type
|
||||
* @throws \UnexpectedValueException
|
||||
* @return boolean
|
||||
*/
|
||||
public function find($type)
|
||||
{
|
||||
@@ -87,7 +105,7 @@ class EmailLexer extends AbstractLexer
|
||||
if (!$search->lookahead) {
|
||||
throw new \UnexpectedValueException($type . ' not found');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +121,7 @@ class EmailLexer extends AbstractLexer
|
||||
/**
|
||||
* moveNext
|
||||
*
|
||||
* @return mixed
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveNext()
|
||||
{
|
||||
@@ -115,29 +133,28 @@ 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]?',
|
||||
'[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]+');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,14 +166,77 @@ 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;
|
||||
}
|
||||
|
||||
if ($this->isASCIIInvalid($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 isASCIIInvalid($value)
|
||||
{
|
||||
if (isset($this->invalidASCII[ord($value)])) {
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Parser\DomainPart;
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
|
||||
/**
|
||||
* EmailParser
|
||||
*
|
||||
@@ -9,55 +12,55 @@ 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
|
||||
*
|
||||
* @param $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);
|
||||
|
||||
$this->setParts($str);
|
||||
|
||||
if ($this->lexer->hasInvalidTokens()) {
|
||||
throw new \InvalidArgumentException('ERR_INVALID_ATEXT');
|
||||
}
|
||||
|
||||
return array('local' => $parts[0], 'domain' => $this->domainPart);
|
||||
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,396 +69,32 @@ 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;
|
||||
}
|
||||
|
||||
private function parseDomainLiteral()
|
||||
{
|
||||
$IPv6TAG = false;
|
||||
$addressLiteral = '';
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
|
||||
// Address starts with a single colon
|
||||
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
|
||||
}
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* validateQuotedPair
|
||||
* @param string $localPart
|
||||
* @param string $parsedDomainPart
|
||||
*/
|
||||
private function validateQuotedPair()
|
||||
protected function addLongEmailWarning($localPart, $parsedDomainPart)
|
||||
{
|
||||
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) {
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
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) && $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;
|
||||
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {
|
||||
$this->warnings[] = EmailValidator::RFC5322_TOOLONG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,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 +33,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 +67,6 @@ class EmailValidator
|
||||
protected $warnings = array();
|
||||
protected $error;
|
||||
protected $threshold = 255;
|
||||
protected $emailParts = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -80,7 +77,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,21 +84,21 @@ 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;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($strict) ? (!$this->hasWarnings() && $dns) : true;
|
||||
return !$strict || (!$this->hasWarnings() && $dns);
|
||||
}
|
||||
|
||||
/**
|
||||
* hasWarnings
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasWarnings()
|
||||
@@ -111,8 +107,6 @@ class EmailValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* getWarnings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getWarnings()
|
||||
@@ -121,8 +115,6 @@ class EmailValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* getError
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getError()
|
||||
@@ -131,8 +123,6 @@ class EmailValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* setThreshols
|
||||
*
|
||||
* @param int $threshold
|
||||
*
|
||||
* @return EmailValidator
|
||||
@@ -145,8 +135,6 @@ class EmailValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* getThreshold
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getThreshold()
|
||||
@@ -156,87 +144,26 @@ 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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
$checked = true;
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
//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,312 @@
|
||||
<?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_HYPHEN) {
|
||||
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
|
||||
}
|
||||
|
||||
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 {
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_SEMICOLON) {
|
||||
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
|
||||
}
|
||||
|
||||
$prev = $this->lexer->getPrevious();
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_SLASH) {
|
||||
throw new \InvalidArgumentException('ERR_DOMAIN_CHAR_NOT_ALLOWED');
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->parseComments();
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
$this->checkConsecutiveDots();
|
||||
$this->checkDomainPartExceptions($prev);
|
||||
|
||||
if ($this->hasBrackets()) {
|
||||
$this->parseDomainLiteral();
|
||||
}
|
||||
|
||||
$this->checkLabelLength($prev);
|
||||
|
||||
if ($this->isFWS()) {
|
||||
$this->parseFWS();
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $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,121 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
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()) {
|
||||
throw new \InvalidArgumentException('ERR_DOT_START');
|
||||
}
|
||||
|
||||
$closingQuote = $this->checkDQUOTE($closingQuote);
|
||||
if ($closingQuote && $parseDQuote) {
|
||||
$parseDQuote = $this->parseDoubleQuote();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->parseComments();
|
||||
}
|
||||
|
||||
$this->checkConsecutiveDots();
|
||||
|
||||
if (
|
||||
$this->lexer->token['type'] === EmailLexer::S_DOT &&
|
||||
$this->lexer->isNextToken(EmailLexer::S_AT)
|
||||
) {
|
||||
throw new \InvalidArgumentException('ERR_DOT_END');
|
||||
}
|
||||
|
||||
$this->warnEscaping();
|
||||
$this->isInvalidToken($this->lexer->token, $closingQuote);
|
||||
|
||||
if ($this->isFWS()) {
|
||||
$this->parseFWS();
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
$prev = $this->lexer->getPrevious();
|
||||
if (strlen($prev['value']) > EmailValidator::RFC5322_LOCAL_TOOLONG) {
|
||||
$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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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::GENERIC
|
||||
) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,23 @@ 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 testLexerHasInvalidTokens()
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
$lexer->setInput(chr(226));
|
||||
$lexer->moveNext();
|
||||
$lexer->moveNext();
|
||||
$this->assertTrue($lexer->hasInvalidTokens());
|
||||
}
|
||||
|
||||
public function getTokens()
|
||||
{
|
||||
return array(
|
||||
@@ -53,6 +137,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 +151,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::INVALID),
|
||||
array(chr(0), EmailLexer::C_NUL)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,14 @@ 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(
|
||||
@@ -34,18 +42,26 @@ 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'),
|
||||
array('"\a"@iana.org'),
|
||||
array('"test\ test"@iana.org'),
|
||||
array('""@iana.org'),
|
||||
array('"\""@iana.org'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getInvalidEmails
|
||||
*/
|
||||
public function testAInvalidEmails($email)
|
||||
public function testInvalidEmails($email)
|
||||
{
|
||||
$this->assertFalse($this->validator->isValid($email));
|
||||
}
|
||||
@@ -53,9 +69,12 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
public function getInvalidEmails()
|
||||
{
|
||||
return array(
|
||||
|
||||
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'),
|
||||
@@ -63,6 +82,33 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
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'),
|
||||
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'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,30 +148,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'
|
||||
),
|
||||
@@ -133,9 +207,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,
|
||||
@@ -215,8 +298,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:]'
|
||||
@@ -224,12 +307,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'
|
||||
),
|
||||
@@ -260,6 +345,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