mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-09-01 21:19:25 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fa792ad18 | |||
| 68e418ec08 | |||
| ef615f1d74 | |||
| 563d0cdde5 | |||
| c7ab280976 | |||
| f46887bc48 | |||
| 840d5603eb | |||
| feba9587ec | |||
| 5568c52464 | |||
| cfa3d44471 | |||
| 2f38a470f9 | |||
| ade6887fd9 | |||
| 5065fafc8c | |||
| e834eea530 | |||
| c4b8d12921 | |||
| 3d09cd0b58 | |||
| 3e3b1e562b | |||
| ed32faa118 | |||
| 834593d590 | |||
| a6255605af | |||
| 950d0663dc | |||
| 92dd169c32 | |||
| a6c8d7101b | |||
| 128cc721d7 | |||
| c26463ff92 | |||
| a09af2de87 | |||
| c802f106b3 | |||
| 709f21f927 | |||
| e68c26e065 | |||
| 8036f2bd05 | |||
| 0043eab0c5 | |||
| 0578b32b30 | |||
| 54859fabea | |||
| 135444003f | |||
| 9dde0df1e3 | |||
| 8790f59415 | |||
| 114b663f20 | |||
| a0e0fadfce | |||
| 74c0f07434 | |||
| ad1a0e9773 | |||
| aeb6851388 | |||
| a23fcc1315 | |||
| 1bec00a100 | |||
| 3abad65421 | |||
| 3fdfa261ad | |||
| 14ca3785c1 | |||
| bc31baa11e | |||
| c0c888ae10 | |||
| 181a2fc726 | |||
| a1e85fa9fb | |||
| 7ff8caaae5 | |||
| 5d56347ddf | |||
| 80ad87acdd | |||
| 30562b69fc | |||
| cbb8b9e152 | |||
| cf670406e5 | |||
| 4d446bf971 | |||
| 4f0f9023f0 | |||
| 9160e26b13 | |||
| eb036b7d1c | |||
| 19811e0082 | |||
| b366d54b0a | |||
| 2bc46e23e5 | |||
| 7701238433 | |||
| afde3d3874 | |||
| e9c46eed92 |
@@ -0,0 +1,7 @@
|
||||
/documentation export-ignore
|
||||
/tests export-ignore
|
||||
/.* export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/psalm.xml export-ignore
|
||||
/psalm.baseline.xml export-ignore
|
||||
/README.md export-ignore
|
||||
@@ -1,2 +1,4 @@
|
||||
.idea
|
||||
composer.lock
|
||||
report/
|
||||
vendor/
|
||||
|
||||
+33
-22
@@ -1,34 +1,45 @@
|
||||
sudo: false
|
||||
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- hhvm
|
||||
|
||||
env:
|
||||
global:
|
||||
- deps=no
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- php: 5.5
|
||||
- php: 5.5.9
|
||||
dist: trusty
|
||||
env: deps=low
|
||||
- php: 5.5
|
||||
dist: trusty
|
||||
- php: 5.6
|
||||
env: deps=high
|
||||
env:
|
||||
dist: xenial
|
||||
- php: 7.0
|
||||
dist: xenial
|
||||
- php: 7.1
|
||||
env:
|
||||
- psalm=yes
|
||||
dist: bionic
|
||||
- php: 7.2
|
||||
env:
|
||||
- psalm=yes
|
||||
dist: bionic
|
||||
- php: 7.3
|
||||
dist: bionic
|
||||
env:
|
||||
- psalm=yes
|
||||
- php: 7.4
|
||||
env:
|
||||
- psalm=yes
|
||||
dist: bionic
|
||||
|
||||
install:
|
||||
- if [ "$deps" = "no" ]; then composer install; fi
|
||||
- if [ "$deps" = "low" ]; then composer update --prefer-lowest; fi
|
||||
- if [ "$deps" = "high" ]; then composer update; fi
|
||||
- if [ "$deps" = "low" ]; then composer update --prefer-lowest; else composer install; fi
|
||||
- if [ "$psalm" = "yes" ]; then composer require --dev vimeo/psalm:3.18.2; fi
|
||||
|
||||
before_script:
|
||||
- mkdir -p build/logs
|
||||
|
||||
script:
|
||||
- mkdir -p build/logs
|
||||
- phpunit --coverage-clover build/logs/clover.xml
|
||||
- vendor/bin/phpunit --coverage-clover build/logs/clover.xml
|
||||
- if [ "$psalm" = "yes" ]; then vendor/bin/psalm; fi
|
||||
|
||||
after_script:
|
||||
- php vendor/bin/coveralls
|
||||
|
||||
- php vendor/bin/coveralls
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
||||
|
||||
class DNSCheckValidation implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* @var EmailParser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $warnings = [];
|
||||
|
||||
/**
|
||||
* @var InvalidEmail
|
||||
*/
|
||||
private $error;
|
||||
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
{
|
||||
// use the input to check DNS if we cannot extract something similar to a domain
|
||||
$host = $email;
|
||||
// Arguable pattern to extract the domain. Not aiming to validate the domain nor the email
|
||||
$pattern = "/^[a-z'0-9]+([._-][a-z'0-9]+)*@([a-z0-9]+([._-][a-z0-9]+)+)+$/";
|
||||
if (preg_match($pattern, $email, $result)) {
|
||||
$host = $this->extractHost($result);
|
||||
}
|
||||
|
||||
return $this->checkDNS($host);
|
||||
}
|
||||
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getWarnings()
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
private function extractHost(array $result)
|
||||
{
|
||||
foreach ($result as $match) {
|
||||
$onlyDomainPattern = "/^([a-z0-9]+([._-][a-z0-9]+))+$/";
|
||||
if (preg_match($onlyDomainPattern, $match, $domainResult)) {
|
||||
return $domainResult[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkDNS($host)
|
||||
{
|
||||
$Aresult = true;
|
||||
$MXresult = checkdnsrr($host, 'MX');
|
||||
|
||||
if (!$MXresult) {
|
||||
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
|
||||
$Aresult = checkdnsrr($host, 'A');
|
||||
if (!$Aresult) {
|
||||
$this->error = new NoDNSRecord();
|
||||
}
|
||||
}
|
||||
return $MXresult || $Aresult;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
|
||||
interface EmailValidation
|
||||
{
|
||||
public function isValid($email, EmailLexer $emailLexer);
|
||||
|
||||
/**
|
||||
* @return InvalidEmail
|
||||
*/
|
||||
public function getError();
|
||||
|
||||
/**
|
||||
* @return array of Warning
|
||||
*/
|
||||
public function getWarnings();
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Validation\Exception\EmptyValidationList;
|
||||
|
||||
class MultipleValidationWithAnd implements EmailValidation
|
||||
{
|
||||
private $validations = [];
|
||||
private $warnings = [];
|
||||
private $error;
|
||||
|
||||
public function __construct(array $validations)
|
||||
{
|
||||
if (count($validations) == 0) {
|
||||
throw new EmptyValidationList();
|
||||
}
|
||||
|
||||
$this->validations = $validations;
|
||||
}
|
||||
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
{
|
||||
$result = true;
|
||||
$errors = [];
|
||||
foreach ($this->validations as $validation) {
|
||||
$emailLexer->reset();
|
||||
$result = $result && $validation->isValid($email, $emailLexer);
|
||||
$this->warnings = array_merge($this->warnings, $validation->getWarnings());
|
||||
$errors[] = $validation->getError();
|
||||
}
|
||||
$this->error = new MultipleErrors($errors);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getWarnings()
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
|
||||
class NoRFCWarningsValidation extends RFCValidation
|
||||
{
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
{
|
||||
return parent::isValid($email, $emailLexer) && empty($this->getWarnings());
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,31 @@
|
||||
#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
|
||||
# EmailValidator
|
||||
|
||||

|
||||
##Installation##
|
||||
[](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)
|
||||
|
||||
## Supported RFCs ##
|
||||
|
||||
This library aims to support:
|
||||
|
||||
RFC 5321, 5322, 6530, 6531, 6532.
|
||||
|
||||
## Requirements ##
|
||||
|
||||
* [Composer](https://getcomposer.org) is required for installation
|
||||
* [Spoofchecking](/src/Validation/SpoofCheckValidation.php) and [DNSCheckValidation](/src/Validation/DNSCheckValidation.php) validation requires that your PHP system has the [PHP Internationalization Libraries](https://php.net/manual/en/book.intl.php) (also known as PHP Intl)
|
||||
|
||||
## Installation ##
|
||||
|
||||
Run the command below to install via Composer
|
||||
|
||||
```shell
|
||||
composer require egulias/email-validator "~2.0"
|
||||
composer require egulias/email-validator
|
||||
```
|
||||
|
||||
##Getting Started##
|
||||
## Getting Started ##
|
||||
|
||||
`EmailValidator`requires you to decide which (or combination of them) validation/s strategy/ies you'd like to follow for each [validation](#available-validations).
|
||||
|
||||
A basic example with the RFC validation
|
||||
@@ -20,22 +33,23 @@ A basic example with the RFC validation
|
||||
<?php
|
||||
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||
|
||||
$validator = new EmailValidator();
|
||||
$validator->isValid("example@example.com", new RFCValidation()) //true
|
||||
$validator->isValid("example@example.com", new RFCValidation()); //true
|
||||
```
|
||||
|
||||
|
||||
###Available validations###
|
||||
### Available validations ###
|
||||
|
||||
1. [RFCValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/RFCValidation.php)
|
||||
2. [NoWarningsRFCValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/NoRFCWarningsValidation.php)
|
||||
3. [DNSCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/DNSCheckValidation.php)
|
||||
4. [SpoofCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/SpoofCheckValidation.php)
|
||||
5. [MultipleValidationsWithAnd](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/MultipleValidationWithAnd.php)
|
||||
1. [RFCValidation](/src/Validation/RFCValidation.php)
|
||||
2. [NoRFCWarningsValidation](/src/Validation/NoRFCWarningsValidation.php)
|
||||
3. [DNSCheckValidation](/src/Validation/DNSCheckValidation.php)
|
||||
4. [SpoofCheckValidation](/src/Validation/SpoofCheckValidation.php)
|
||||
5. [MultipleValidationWithAnd](/src/Validation/MultipleValidationWithAnd.php)
|
||||
6. [Your own validation](#how-to-extend)
|
||||
|
||||
`MultipleValidationsWithAnd`
|
||||
`MultipleValidationWithAnd`
|
||||
|
||||
It is a validation that operates over other validations performing a logical and (&&) over the result of each validation.
|
||||
|
||||
@@ -43,29 +57,33 @@ It is a validation that operates over other validations performing a logical and
|
||||
<?php
|
||||
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Validation\DNSCheckValidation;
|
||||
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
|
||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||
|
||||
$validator = new EmailValidator();
|
||||
$multipleValidations = new MultipleValidationsWithAnd([
|
||||
$multipleValidations = new MultipleValidationWithAnd([
|
||||
new RFCValidation(),
|
||||
new DNSCheckValidation()
|
||||
])
|
||||
$validator->isValid("example@example.com", $multipleValidations) //true
|
||||
]);
|
||||
$validator->isValid("example@example.com", $multipleValidations); //true
|
||||
```
|
||||
|
||||
###How to extend###
|
||||
### How to extend ###
|
||||
|
||||
It's easy! You just need to extend [EmailValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/EmailValidation.php) and you can use your own validation.
|
||||
It's easy! You just need to implement [EmailValidation](/src/Validation/EmailValidation.php) and you can use your own validation.
|
||||
|
||||
|
||||
##Other Contributors##
|
||||
## Other Contributors ##
|
||||
|
||||
(You can find current contributors [here](https://github.com/egulias/EmailValidator/graphs/contributors))
|
||||
|
||||
As this is a port from another library and work, here are other people related to the previous one:
|
||||
|
||||
* Ricard Clau [@ricardclau](http://github.com/ricardclau): Performance against PHP built-in filter_var
|
||||
* 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
|
||||
* Ricard Clau [@ricardclau](https://github.com/ricardclau): Performance against PHP built-in filter_var
|
||||
* Josepf Bielawski [@stloyd](https://github.com/stloyd): For its first re-work of Dominic's lib
|
||||
* Dominic Sayers [@dominicsayers](https://github.com/dominicsayers): The original isemail function
|
||||
|
||||
## License ##
|
||||
|
||||
##License##
|
||||
Released under the MIT License attached with this code.
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
||||
use Egulias\EmailValidator\Validation\DNSCheckValidation;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
|
||||
class DNSCheckValidationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testValidDNS()
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$this->assertTrue($validation->isValid("example@example.com", new EmailLexer()));
|
||||
}
|
||||
|
||||
public function testInvalidDNS()
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$this->assertFalse($validation->isValid("example@invalid.example.com", new EmailLexer()));
|
||||
}
|
||||
|
||||
public function testDNSWarnings()
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$expectedWarnings = [NoDNSMXRecord::CODE => new NoDNSMXRecord()];
|
||||
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
||||
$this->assertEquals($expectedWarnings, $validation->getWarnings());
|
||||
}
|
||||
|
||||
public function testNoDNSError()
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$expectedError = new NoDNSRecord();
|
||||
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
||||
$this->assertEquals($expectedError, $validation->getError());
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\Exception\CommaInDomain;
|
||||
use Egulias\EmailValidator\Exception\NoDomainPart;
|
||||
use Egulias\EmailValidator\Validation\MultipleErrors;
|
||||
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
|
||||
use Egulias\EmailValidator\Warning\AddressLiteral;
|
||||
use Egulias\EmailValidator\Warning\DomainLiteral;
|
||||
|
||||
class MultipleValidationWitAndTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testUsesAndLogicalOperation()
|
||||
{
|
||||
$lexer = $this->getMock("Egulias\\EmailValidator\\EmailLexer");
|
||||
$validationTrue = $this->getMock("Egulias\\EmailValidator\\Validation\\EmailValidation");
|
||||
$validationTrue->expects($this->any())->method("isValid")->willReturn(true);
|
||||
$validationTrue->expects($this->any())->method("getWarnings")->willReturn([]);
|
||||
$validationFalse = $this->getMock("Egulias\\EmailValidator\\Validation\\EmailValidation");
|
||||
$validationFalse->expects($this->any())->method("isValid")->willReturn(false);
|
||||
$validationFalse->expects($this->any())->method("getWarnings")->willReturn([]);
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validationTrue, $validationFalse]);
|
||||
$this->assertFalse($multipleValidation->isValid("exmpale@example.com", $lexer));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Egulias\EmailValidator\Validation\Exception\EmptyValidationList
|
||||
*/
|
||||
public function testEmptyListIsNotAllowed()
|
||||
{
|
||||
new MultipleValidationWithAnd([]);
|
||||
}
|
||||
|
||||
public function testAccumulatesWarnings()
|
||||
{
|
||||
$warnings1 = [
|
||||
AddressLiteral::CODE => new AddressLiteral()
|
||||
];
|
||||
$warnings2 = [
|
||||
DomainLiteral::CODE => new DomainLiteral()
|
||||
];
|
||||
$expectedResult = array_merge($warnings1, $warnings2);
|
||||
|
||||
$lexer = $this->getMock("Egulias\\EmailValidator\\EmailLexer");
|
||||
$validation1 = $this->getMock("Egulias\\EmailValidator\\Validation\\EmailValidation");
|
||||
$validation1->expects($this->any())->method("isValid")->willReturn(true);
|
||||
$validation1->expects($this->once())->method("getWarnings")->willReturn($warnings1);
|
||||
|
||||
$validation2 = $this->getMock("Egulias\\EmailValidator\\Validation\\EmailValidation");
|
||||
$validation2->expects($this->any())->method("isValid")->willReturn(false);
|
||||
$validation2->expects($this->once())->method("getWarnings")->willReturn($warnings2);
|
||||
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validation1, $validation2]);
|
||||
$multipleValidation->isValid("example@example.com", $lexer);
|
||||
$this->assertEquals($expectedResult, $multipleValidation->getWarnings());
|
||||
}
|
||||
|
||||
public function testGathersAllTheErrors()
|
||||
{
|
||||
$error1 = new CommaInDomain();
|
||||
$error2 = new NoDomainPart();
|
||||
|
||||
$expectedResult = new MultipleErrors([$error1, $error2]);
|
||||
|
||||
$lexer = $this->getMock("Egulias\\EmailValidator\\EmailLexer");
|
||||
|
||||
$validation1 = $this->getMock("Egulias\\EmailValidator\\Validation\\EmailValidation");
|
||||
$validation1->expects($this->any())->method("isValid")->willReturn(true);
|
||||
$validation1->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
$validation1->expects($this->once())->method("getError")->willReturn($error1);
|
||||
|
||||
$validation2 = $this->getMock("Egulias\\EmailValidator\\Validation\\EmailValidation");
|
||||
$validation2->expects($this->any())->method("isValid")->willReturn(false);
|
||||
$validation2->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
$validation2->expects($this->once())->method("getError")->willReturn($error2);
|
||||
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validation1, $validation2]);
|
||||
$multipleValidation->isValid("example@example.com", $lexer);
|
||||
$this->assertEquals($expectedResult, $multipleValidation->getError());
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
|
||||
|
||||
class NoWarningsRFCValidationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testEmailWithWarningsIsInvalid()
|
||||
{
|
||||
$validation = new NoRFCWarningsValidation();
|
||||
|
||||
$this->assertFalse($validation->isValid('examp"l"e@example.com', new EmailLexer()));
|
||||
}
|
||||
|
||||
public function testEmailWithoutWarningsIsValid()
|
||||
{
|
||||
$validation = new NoRFCWarningsValidation();
|
||||
|
||||
$this->assertTrue($validation->isValid('example@example.com', new EmailLexer()));
|
||||
}
|
||||
}
|
||||
+18
-16
@@ -2,7 +2,6 @@
|
||||
"name": "egulias/email-validator",
|
||||
"description": "A library for validating emails against several RFCs",
|
||||
"homepage": "https://github.com/egulias/EmailValidator",
|
||||
"type": "Library",
|
||||
"keywords": ["email", "validation", "validator", "emailvalidation", "emailvalidator"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
@@ -10,27 +9,30 @@
|
||||
],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
"dev-master": "2.1.x-dev"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/dominicsayers/isemail"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">= 5.5",
|
||||
"doctrine/lexer": "^1.0.1"
|
||||
"require": {
|
||||
"php": ">=5.5",
|
||||
"doctrine/lexer": "^1.0.1",
|
||||
"symfony/polyfill-intl-idn": "^1.10"
|
||||
},
|
||||
"require-dev" : {
|
||||
"satooshi/php-coveralls": "dev-master",
|
||||
"phpunit/phpunit": "^4.8.0",
|
||||
"dominicsayers/isemail": "dev-master"
|
||||
"require-dev": {
|
||||
"dominicsayers/isemail": "^3.0.7",
|
||||
"phpunit/phpunit": "^4.8.36|^7.5.15",
|
||||
"satooshi/php-coveralls": "^1.0.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Egulias\\EmailValidator\\": "EmailValidator"
|
||||
"Egulias\\EmailValidator\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Egulias\\EmailValidator\\Tests\\": "tests"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
-1661
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,23 @@
|
||||
Email length
|
||||
------------
|
||||
http://tools.ietf.org/html/rfc5321#section-4.1.2
|
||||
https://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
|
||||
https://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
|
||||
https://tools.ietf.org/html/rfc1035#section-2.3.4
|
||||
|
||||
DNS
|
||||
---
|
||||
|
||||
http://tools.ietf.org/html/rfc5321#section-2.3.5
|
||||
https://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
|
||||
https://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,
|
||||
@@ -37,7 +37,7 @@ 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
|
||||
https://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,
|
||||
@@ -57,7 +57,7 @@ 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
|
||||
https://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.
|
||||
@@ -66,4 +66,4 @@ 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)
|
||||
(https://tools.ietf.org/html/rfc5322#section-3.4.1)
|
||||
|
||||
+4
-5
@@ -8,19 +8,18 @@
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="EmailValidator Test Suite">
|
||||
<directory>./Tests/EmailValidator</directory>
|
||||
<directory>./tests/EmailValidator</directory>
|
||||
<exclude>./vendor/</exclude>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<blacklist>
|
||||
<directory>./vendor</directory>
|
||||
</blacklist>
|
||||
<whitelist>
|
||||
<directory>./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="3.8.3@389af1bfc739bfdff3f9e3dc7bd6499aee51a831">
|
||||
<file src="src/EmailLexer.php">
|
||||
<DocblockTypeContradiction occurrences="1">
|
||||
<code>self::$nullToken</code>
|
||||
</DocblockTypeContradiction>
|
||||
</file>
|
||||
<file src="src/Parser/Parser.php">
|
||||
<MissingReturnType occurrences="1">
|
||||
<code>parse</code>
|
||||
</MissingReturnType>
|
||||
</file>
|
||||
<file src="src/Validation/SpoofCheckValidation.php">
|
||||
<UndefinedClass occurrences="2">
|
||||
<code>Spoofchecker</code>
|
||||
<code>Spoofchecker</code>
|
||||
</UndefinedClass>
|
||||
</file>
|
||||
</files>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
requireVoidReturnType="false"
|
||||
totallyTyped="false"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config ./vendor/vimeo/psalm/config.xsd"
|
||||
errorBaseline="./psalm.baseline.xml"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
|
||||
<issueHandlers>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
@@ -13,6 +13,7 @@ class EmailLexer extends AbstractLexer
|
||||
const S_BACKSLASH = 92;
|
||||
const S_DOT = 46;
|
||||
const S_DQUOTE = 34;
|
||||
const S_SQUOTE = 39;
|
||||
const S_OPENPARENTHESIS = 49;
|
||||
const S_CLOSEPARENTHESIS = 261;
|
||||
const S_OPENBRACKET = 262;
|
||||
@@ -58,6 +59,7 @@ class EmailLexer extends AbstractLexer
|
||||
'/' => self::S_SLASH,
|
||||
',' => self::S_COMMA,
|
||||
'.' => self::S_DOT,
|
||||
"'" => self::S_SQUOTE,
|
||||
'"' => self::S_DQUOTE,
|
||||
'-' => self::S_HYPHEN,
|
||||
'::' => self::S_DOUBLECOLON,
|
||||
@@ -67,33 +69,79 @@ class EmailLexer extends AbstractLexer
|
||||
"\n" => self::S_LF,
|
||||
"\r\n" => self::CRLF,
|
||||
'IPv6' => self::S_IPV6TAG,
|
||||
'<' => self::S_LOWERTHAN,
|
||||
'>' => self::S_GREATERTHAN,
|
||||
'{' => self::S_OPENQBRACKET,
|
||||
'}' => self::S_CLOSEQBRACKET,
|
||||
'' => self::S_EMPTY,
|
||||
'\0' => self::C_NUL,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasInvalidTokens = false;
|
||||
|
||||
protected $previous;
|
||||
/**
|
||||
* @var array
|
||||
*
|
||||
* @psalm-var array{value:string, type:null|int, position:int}|array<empty, empty>
|
||||
*/
|
||||
protected $previous = [];
|
||||
|
||||
/**
|
||||
* The last matched/seen token.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @psalm-var array{value:string, type:null|int, position:int}
|
||||
*/
|
||||
public $token;
|
||||
|
||||
/**
|
||||
* The next token in the input.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
public $lookahead;
|
||||
|
||||
/**
|
||||
* @psalm-var array{value:'', type:null, position:0}
|
||||
*/
|
||||
private static $nullToken = [
|
||||
'value' => '',
|
||||
'type' => null,
|
||||
'position' => 0,
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->previous = $this->token = self::$nullToken;
|
||||
$this->lookahead = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->hasInvalidTokens = false;
|
||||
parent::reset();
|
||||
$this->previous = $this->token = self::$nullToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasInvalidTokens()
|
||||
{
|
||||
return $this->hasInvalidTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @param int $type
|
||||
* @throws \UnexpectedValueException
|
||||
* @return boolean
|
||||
*
|
||||
* @psalm-suppress InvalidScalarArgument
|
||||
*/
|
||||
public function find($type)
|
||||
{
|
||||
@@ -109,7 +157,7 @@ class EmailLexer extends AbstractLexer
|
||||
/**
|
||||
* getPrevious
|
||||
*
|
||||
* @return array token
|
||||
* @return array
|
||||
*/
|
||||
public function getPrevious()
|
||||
{
|
||||
@@ -124,8 +172,10 @@ class EmailLexer extends AbstractLexer
|
||||
public function moveNext()
|
||||
{
|
||||
$this->previous = $this->token;
|
||||
$hasNext = parent::moveNext();
|
||||
$this->token = $this->token ?: self::$nullToken;
|
||||
|
||||
return parent::moveNext();
|
||||
return $hasNext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,6 +231,11 @@ class EmailLexer extends AbstractLexer
|
||||
return self::GENERIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValid($value)
|
||||
{
|
||||
if (isset($this->charValue[$value])) {
|
||||
@@ -191,7 +246,7 @@ class EmailLexer extends AbstractLexer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNullType($value)
|
||||
@@ -204,7 +259,7 @@ class EmailLexer extends AbstractLexer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isUTF8Invalid($value)
|
||||
@@ -216,6 +271,9 @@ class EmailLexer extends AbstractLexer
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getModifiers()
|
||||
{
|
||||
return 'iu';
|
||||
@@ -17,11 +17,33 @@ class EmailParser
|
||||
{
|
||||
const EMAIL_MAX_LENGTH = 254;
|
||||
|
||||
protected $warnings;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $warnings = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $domainPart = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $localPart = '';
|
||||
/**
|
||||
* @var EmailLexer
|
||||
*/
|
||||
protected $lexer;
|
||||
|
||||
/**
|
||||
* @var LocalPart
|
||||
*/
|
||||
protected $localPartParser;
|
||||
|
||||
/**
|
||||
* @var DomainPart
|
||||
*/
|
||||
protected $domainPartParser;
|
||||
|
||||
public function __construct(EmailLexer $lexer)
|
||||
@@ -29,11 +51,10 @@ class EmailParser
|
||||
$this->lexer = $lexer;
|
||||
$this->localPartParser = new LocalPart($this->lexer);
|
||||
$this->domainPartParser = new DomainPart($this->lexer);
|
||||
$this->warnings = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $str
|
||||
* @param string $str
|
||||
* @return array
|
||||
*/
|
||||
public function parse($str)
|
||||
@@ -57,6 +78,9 @@ class EmailParser
|
||||
return array('local' => $this->localPart, 'domain' => $this->domainPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Warning\Warning[]
|
||||
*/
|
||||
public function getWarnings()
|
||||
{
|
||||
$localPartWarnings = $this->localPartParser->getWarnings();
|
||||
@@ -68,11 +92,17 @@ class EmailParser
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getParsedDomainPart()
|
||||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*/
|
||||
protected function setParts($email)
|
||||
{
|
||||
$parts = explode('@', $email);
|
||||
@@ -80,6 +110,9 @@ class EmailParser
|
||||
$this->localPart = $parts[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasAtToken()
|
||||
{
|
||||
$this->lexer->moveNext();
|
||||
@@ -11,24 +11,24 @@ class EmailValidator
|
||||
* @var EmailLexer
|
||||
*/
|
||||
private $lexer;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $warnings;
|
||||
|
||||
/**
|
||||
* @var InvalidEmail
|
||||
* @var Warning\Warning[]
|
||||
*/
|
||||
protected $warnings = [];
|
||||
|
||||
/**
|
||||
* @var InvalidEmail|null
|
||||
*/
|
||||
protected $error;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->lexer = new EmailLexer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $email
|
||||
* @param string $email
|
||||
* @param EmailValidation $emailValidation
|
||||
* @return bool
|
||||
*/
|
||||
@@ -37,7 +37,7 @@ class EmailValidator
|
||||
$isValid = $emailValidation->isValid($email, $this->lexer);
|
||||
$this->warnings = $emailValidation->getWarnings();
|
||||
$this->error = $emailValidation->getError();
|
||||
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class EmailValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return InvalidEmail|null
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Exception;
|
||||
|
||||
class DomainAcceptsNoMail extends InvalidEmail
|
||||
{
|
||||
const CODE = 154;
|
||||
const REASON = 'Domain accepts no mail (Null MX, RFC7505)';
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Egulias\EmailValidator\Exception;
|
||||
|
||||
class ExpectedQPair extends InvalidEmail
|
||||
class ExpectingQPair extends InvalidEmail
|
||||
{
|
||||
const CODE = 136;
|
||||
const REASON = "Expecting QPAIR";
|
||||
@@ -6,7 +6,7 @@ abstract class InvalidEmail extends \InvalidArgumentException
|
||||
{
|
||||
const REASON = "Invalid email";
|
||||
const CODE = 0;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(static::REASON, static::CODE);
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Exception;
|
||||
|
||||
class LocalOrReservedDomain extends InvalidEmail
|
||||
{
|
||||
const CODE = 153;
|
||||
const REASON = 'Local, mDNS or reserved domain (RFC2606, RFC6762)';
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace Egulias\EmailValidator\Exception;
|
||||
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
|
||||
class NoDNSRecord extends InvalidEmail
|
||||
{
|
||||
const CODE = 5;
|
||||
@@ -5,5 +5,5 @@ namespace Egulias\EmailValidator\Exception;
|
||||
class UnclosedComment extends InvalidEmail
|
||||
{
|
||||
const CODE = 146;
|
||||
const REASON = "No colosing comment token found";
|
||||
const REASON = "No closing comment token found";
|
||||
}
|
||||
@@ -35,27 +35,17 @@ use Egulias\EmailValidator\Warning\TLD;
|
||||
class DomainPart extends Parser
|
||||
{
|
||||
const DOMAIN_MAX_LENGTH = 254;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $domainPart = '';
|
||||
|
||||
public function parse($domainPart)
|
||||
{
|
||||
$this->lexer->moveNext();
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
|
||||
throw new DotAtStart();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) {
|
||||
throw new NoDomainPart();
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
|
||||
throw new DomainHyphened();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->warnings[DeprecatedComment::CODE] = new DeprecatedComment();
|
||||
$this->parseDomainComments();
|
||||
}
|
||||
$this->performDomainStartChecks();
|
||||
|
||||
$domain = $this->doParseDomainPart();
|
||||
|
||||
@@ -77,11 +67,50 @@ class DomainPart extends Parser
|
||||
$this->domainPart = $domain;
|
||||
}
|
||||
|
||||
private function performDomainStartChecks()
|
||||
{
|
||||
$this->checkInvalidTokensAfterAT();
|
||||
$this->checkEmptyDomain();
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->warnings[DeprecatedComment::CODE] = new DeprecatedComment();
|
||||
$this->parseDomainComments();
|
||||
}
|
||||
}
|
||||
|
||||
private function checkEmptyDomain()
|
||||
{
|
||||
$thereIsNoDomain = $this->lexer->token['type'] === EmailLexer::S_EMPTY ||
|
||||
($this->lexer->token['type'] === EmailLexer::S_SP &&
|
||||
!$this->lexer->isNextToken(EmailLexer::GENERIC));
|
||||
|
||||
if ($thereIsNoDomain) {
|
||||
throw new NoDomainPart();
|
||||
}
|
||||
}
|
||||
|
||||
private function checkInvalidTokensAfterAT()
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
|
||||
throw new DotAtStart();
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
|
||||
throw new DomainHyphened();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDomainPart()
|
||||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $addressLiteral
|
||||
* @param int $maxGroups
|
||||
*/
|
||||
public function checkIPV6Tag($addressLiteral, $maxGroups = 8)
|
||||
{
|
||||
$prev = $this->lexer->getPrevious();
|
||||
@@ -125,6 +154,9 @@ class DomainPart extends Parser
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function doParseDomainPart()
|
||||
{
|
||||
$domain = '';
|
||||
@@ -166,12 +198,15 @@ class DomainPart extends Parser
|
||||
|
||||
$domain .= $this->lexer->token['value'];
|
||||
$this->lexer->moveNext();
|
||||
} while ($this->lexer->token);
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_SP) {
|
||||
throw new CharNotAllowed();
|
||||
}
|
||||
} while (null !== $this->lexer->token['type']);
|
||||
|
||||
return $domain;
|
||||
}
|
||||
|
||||
private function checkNotAllowedChars($token)
|
||||
|
||||
private function checkNotAllowedChars(array $token)
|
||||
{
|
||||
$notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true];
|
||||
if (isset($notAllowed[$token['type']])) {
|
||||
@@ -179,6 +214,9 @@ class DomainPart extends Parser
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
protected function parseDomainLiteral()
|
||||
{
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
|
||||
@@ -195,6 +233,9 @@ class DomainPart extends Parser
|
||||
return $this->doParseDomainLiteral();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
protected function doParseDomainLiteral()
|
||||
{
|
||||
$IPv6TAG = false;
|
||||
@@ -262,6 +303,11 @@ class DomainPart extends Parser
|
||||
return $addressLiteral;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $addressLiteral
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
protected function checkIPV4Tag($addressLiteral)
|
||||
{
|
||||
$matchesIP = array();
|
||||
@@ -279,16 +325,17 @@ class DomainPart extends Parser
|
||||
return false;
|
||||
}
|
||||
// Convert IPv4 part to IPv6 format for further testing
|
||||
$addressLiteral = substr($addressLiteral, 0, $index) . '0:0';
|
||||
$addressLiteral = substr($addressLiteral, 0, (int) $index) . '0:0';
|
||||
}
|
||||
|
||||
return $addressLiteral;
|
||||
}
|
||||
|
||||
protected function checkDomainPartExceptions($prev)
|
||||
protected function checkDomainPartExceptions(array $prev)
|
||||
{
|
||||
$invalidDomainTokens = array(
|
||||
EmailLexer::S_DQUOTE => true,
|
||||
EmailLexer::S_SQUOTE => true,
|
||||
EmailLexer::S_SEMICOLON => true,
|
||||
EmailLexer::S_GREATERTHAN => true,
|
||||
EmailLexer::S_LOWERTHAN => true,
|
||||
@@ -320,6 +367,9 @@ class DomainPart extends Parser
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasBrackets()
|
||||
{
|
||||
if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) {
|
||||
@@ -335,7 +385,7 @@ class DomainPart extends Parser
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkLabelLength($prev)
|
||||
protected function checkLabelLength(array $prev)
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
|
||||
$prev['type'] === EmailLexer::GENERIC &&
|
||||
@@ -5,7 +5,6 @@ namespace Egulias\EmailValidator\Parser;
|
||||
use Egulias\EmailValidator\Exception\DotAtEnd;
|
||||
use Egulias\EmailValidator\Exception\DotAtStart;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Exception\ExpectingAT;
|
||||
use Egulias\EmailValidator\Exception\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Exception\UnclosedQuotedString;
|
||||
@@ -20,9 +19,10 @@ class LocalPart extends Parser
|
||||
$parseDQuote = true;
|
||||
$closingQuote = false;
|
||||
$openedParenthesis = 0;
|
||||
$totalLength = 0;
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) {
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type']) {
|
||||
throw new DotAtStart();
|
||||
}
|
||||
|
||||
@@ -35,12 +35,13 @@ class LocalPart extends Parser
|
||||
$this->parseComments();
|
||||
$openedParenthesis += $this->getOpenedParenthesis();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
||||
if ($openedParenthesis === 0) {
|
||||
throw new UnopenedComment();
|
||||
} else {
|
||||
$openedParenthesis--;
|
||||
}
|
||||
|
||||
$openedParenthesis--;
|
||||
}
|
||||
|
||||
$this->checkConsecutiveDots();
|
||||
@@ -58,15 +59,18 @@ class LocalPart extends Parser
|
||||
$this->parseFWS();
|
||||
}
|
||||
|
||||
$totalLength += strlen($this->lexer->token['value']);
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
$prev = $this->lexer->getPrevious();
|
||||
if (strlen($prev['value']) > LocalTooLong::LOCAL_PART_LENGTH) {
|
||||
if ($totalLength > LocalTooLong::LOCAL_PART_LENGTH) {
|
||||
$this->warnings[LocalTooLong::CODE] = new LocalTooLong();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function parseDoubleQuote()
|
||||
{
|
||||
$parseAgain = true;
|
||||
@@ -86,7 +90,7 @@ class LocalPart extends Parser
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) {
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && null !== $this->lexer->token['type']) {
|
||||
$parseAgain = false;
|
||||
if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
|
||||
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
|
||||
@@ -118,7 +122,10 @@ class LocalPart extends Parser
|
||||
return $parseAgain;
|
||||
}
|
||||
|
||||
protected function isInvalidToken($token, $closingQuote)
|
||||
/**
|
||||
* @param bool $closingQuote
|
||||
*/
|
||||
protected function isInvalidToken(array $token, $closingQuote)
|
||||
{
|
||||
$forbidden = array(
|
||||
EmailLexer::S_COMMA,
|
||||
@@ -8,7 +8,7 @@ use Egulias\EmailValidator\Exception\ConsecutiveDot;
|
||||
use Egulias\EmailValidator\Exception\CRLFAtTheEnd;
|
||||
use Egulias\EmailValidator\Exception\CRLFX2;
|
||||
use Egulias\EmailValidator\Exception\CRNoLF;
|
||||
use Egulias\EmailValidator\Exception\ExpectedQPair;
|
||||
use Egulias\EmailValidator\Exception\ExpectingQPair;
|
||||
use Egulias\EmailValidator\Exception\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Exception\ExpectingCTEXT;
|
||||
use Egulias\EmailValidator\Exception\UnclosedComment;
|
||||
@@ -21,8 +21,19 @@ use Egulias\EmailValidator\Warning\QuotedString;
|
||||
|
||||
abstract class Parser
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $warnings = [];
|
||||
|
||||
/**
|
||||
* @var EmailLexer
|
||||
*/
|
||||
protected $lexer;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $openedParenthesis = 0;
|
||||
|
||||
public function __construct(EmailLexer $lexer)
|
||||
@@ -30,11 +41,17 @@ abstract class Parser
|
||||
$this->lexer = $lexer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Egulias\EmailValidator\Warning\Warning[]
|
||||
*/
|
||||
public function getWarnings()
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
*/
|
||||
abstract public function parse($str);
|
||||
|
||||
/** @return int */
|
||||
@@ -50,7 +67,7 @@ abstract class Parser
|
||||
{
|
||||
if (!($this->lexer->token['type'] === EmailLexer::INVALID
|
||||
|| $this->lexer->token['type'] === EmailLexer::C_DEL)) {
|
||||
throw new ExpectedQPair();
|
||||
throw new ExpectingQPair();
|
||||
}
|
||||
|
||||
$this->warnings[QuotedPart::CODE] =
|
||||
@@ -80,6 +97,9 @@ abstract class Parser
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function isUnclosedComment()
|
||||
{
|
||||
try {
|
||||
@@ -122,6 +142,9 @@ abstract class Parser
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function isFWS()
|
||||
{
|
||||
if ($this->escaped()) {
|
||||
@@ -140,11 +163,14 @@ abstract class Parser
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function escaped()
|
||||
{
|
||||
$previous = $this->lexer->getPrevious();
|
||||
|
||||
if ($previous['type'] === EmailLexer::S_BACKSLASH
|
||||
if ($previous && $previous['type'] === EmailLexer::S_BACKSLASH
|
||||
&&
|
||||
$this->lexer->token['type'] !== EmailLexer::GENERIC
|
||||
) {
|
||||
@@ -154,6 +180,9 @@ abstract class Parser
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function warnEscaping()
|
||||
{
|
||||
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
|
||||
@@ -174,6 +203,11 @@ abstract class Parser
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $hasClosingQuote
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkDQUOTE($hasClosingQuote)
|
||||
{
|
||||
if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) {
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
use Egulias\EmailValidator\Exception\LocalOrReservedDomain;
|
||||
use Egulias\EmailValidator\Exception\DomainAcceptsNoMail;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
||||
|
||||
class DNSCheckValidation implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $warnings = [];
|
||||
|
||||
/**
|
||||
* @var InvalidEmail|null
|
||||
*/
|
||||
private $error;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $mxRecords = [];
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (!function_exists('idn_to_ascii')) {
|
||||
throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
|
||||
}
|
||||
}
|
||||
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
{
|
||||
// use the input to check DNS if we cannot extract something similar to a domain
|
||||
$host = $email;
|
||||
|
||||
// Arguable pattern to extract the domain. Not aiming to validate the domain nor the email
|
||||
if (false !== $lastAtPos = strrpos($email, '@')) {
|
||||
$host = substr($email, $lastAtPos + 1);
|
||||
}
|
||||
|
||||
// Get the domain parts
|
||||
$hostParts = explode('.', $host);
|
||||
|
||||
// Reserved Top Level DNS Names (https://tools.ietf.org/html/rfc2606#section-2),
|
||||
// mDNS and private DNS Namespaces (https://tools.ietf.org/html/rfc6762#appendix-G)
|
||||
$reservedTopLevelDnsNames = [
|
||||
// Reserved Top Level DNS Names
|
||||
'test',
|
||||
'example',
|
||||
'invalid',
|
||||
'localhost',
|
||||
|
||||
// mDNS
|
||||
'local',
|
||||
|
||||
// Private DNS Namespaces
|
||||
'intranet',
|
||||
'internal',
|
||||
'private',
|
||||
'corp',
|
||||
'home',
|
||||
'lan',
|
||||
];
|
||||
|
||||
$isLocalDomain = count($hostParts) <= 1;
|
||||
$isReservedTopLevel = in_array($hostParts[(count($hostParts) - 1)], $reservedTopLevelDnsNames, true);
|
||||
|
||||
// Exclude reserved top level DNS names
|
||||
if ($isLocalDomain || $isReservedTopLevel) {
|
||||
$this->error = new LocalOrReservedDomain();
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->checkDns($host);
|
||||
}
|
||||
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getWarnings()
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkDns($host)
|
||||
{
|
||||
$variant = INTL_IDNA_VARIANT_UTS46;
|
||||
|
||||
$host = rtrim(idn_to_ascii($host, IDNA_DEFAULT, $variant), '.') . '.';
|
||||
|
||||
return $this->validateDnsRecords($host);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate the DNS records for given host.
|
||||
*
|
||||
* @param string $host A set of DNS records in the format returned by dns_get_record.
|
||||
*
|
||||
* @return bool True on success.
|
||||
*/
|
||||
private function validateDnsRecords($host)
|
||||
{
|
||||
// Get all MX, A and AAAA DNS records for host
|
||||
// Using @ as workaround to fix https://bugs.php.net/bug.php?id=73149
|
||||
$dnsRecords = @dns_get_record($host, DNS_MX + DNS_A + DNS_AAAA);
|
||||
|
||||
|
||||
// No MX, A or AAAA DNS records
|
||||
if (empty($dnsRecords)) {
|
||||
$this->error = new NoDNSRecord();
|
||||
return false;
|
||||
}
|
||||
|
||||
// For each DNS record
|
||||
foreach ($dnsRecords as $dnsRecord) {
|
||||
if (!$this->validateMXRecord($dnsRecord)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// No MX records (fallback to A or AAAA records)
|
||||
if (empty($this->mxRecords)) {
|
||||
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an MX record
|
||||
*
|
||||
* @param array $dnsRecord Given DNS record.
|
||||
*
|
||||
* @return bool True if valid.
|
||||
*/
|
||||
private function validateMxRecord($dnsRecord)
|
||||
{
|
||||
if ($dnsRecord['type'] !== 'MX') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// "Null MX" record indicates the domain accepts no mail (https://tools.ietf.org/html/rfc7505)
|
||||
if (empty($dnsRecord['target']) || $dnsRecord['target'] === '.') {
|
||||
$this->error = new DomainAcceptsNoMail();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->mxRecords[] = $dnsRecord;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\Warning;
|
||||
|
||||
interface EmailValidation
|
||||
{
|
||||
/**
|
||||
* Returns true if the given email is valid.
|
||||
*
|
||||
* @param string $email The email you want to validate.
|
||||
* @param EmailLexer $emailLexer The email lexer.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($email, EmailLexer $emailLexer);
|
||||
|
||||
/**
|
||||
* Returns the validation error.
|
||||
*
|
||||
* @return InvalidEmail|null
|
||||
*/
|
||||
public function getError();
|
||||
|
||||
/**
|
||||
* Returns the validation warnings.
|
||||
*
|
||||
* @return Warning[]
|
||||
*/
|
||||
public function getWarnings();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation\Error;
|
||||
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
|
||||
class RFCWarnings extends InvalidEmail
|
||||
{
|
||||
const CODE = 997;
|
||||
const REASON = 'Warnings were found.';
|
||||
}
|
||||
+3
@@ -6,6 +6,9 @@ use Exception;
|
||||
|
||||
class EmptyValidationList extends \InvalidArgumentException
|
||||
{
|
||||
/**
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct($code = 0, Exception $previous = null)
|
||||
{
|
||||
parent::__construct("Empty validation list is not allowed", $code, $previous);
|
||||
@@ -9,16 +9,22 @@ class MultipleErrors extends InvalidEmail
|
||||
const CODE = 999;
|
||||
const REASON = "Accumulated errors for multiple validations";
|
||||
/**
|
||||
* @var array
|
||||
* @var InvalidEmail[]
|
||||
*/
|
||||
private $errors = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param InvalidEmail[] $errors
|
||||
*/
|
||||
public function __construct(array $errors)
|
||||
{
|
||||
$this->errors = $errors;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return InvalidEmail[]
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Validation\Exception\EmptyValidationList;
|
||||
|
||||
class MultipleValidationWithAnd implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* If one of validations gets failure skips all succeeding validation.
|
||||
* This means MultipleErrors will only contain a single error which first found.
|
||||
*/
|
||||
const STOP_ON_ERROR = 0;
|
||||
|
||||
/**
|
||||
* All of validations will be invoked even if one of them got failure.
|
||||
* So MultipleErrors will contain all causes.
|
||||
*/
|
||||
const ALLOW_ALL_ERRORS = 1;
|
||||
|
||||
/**
|
||||
* @var EmailValidation[]
|
||||
*/
|
||||
private $validations = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $warnings = [];
|
||||
|
||||
/**
|
||||
* @var MultipleErrors|null
|
||||
*/
|
||||
private $error;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $mode;
|
||||
|
||||
/**
|
||||
* @param EmailValidation[] $validations The validations.
|
||||
* @param int $mode The validation mode (one of the constants).
|
||||
*/
|
||||
public function __construct(array $validations, $mode = self::ALLOW_ALL_ERRORS)
|
||||
{
|
||||
if (count($validations) == 0) {
|
||||
throw new EmptyValidationList();
|
||||
}
|
||||
|
||||
$this->validations = $validations;
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
{
|
||||
$result = true;
|
||||
$errors = [];
|
||||
foreach ($this->validations as $validation) {
|
||||
$emailLexer->reset();
|
||||
$validationResult = $validation->isValid($email, $emailLexer);
|
||||
$result = $result && $validationResult;
|
||||
$this->warnings = array_merge($this->warnings, $validation->getWarnings());
|
||||
$errors = $this->addNewError($validation->getError(), $errors);
|
||||
|
||||
if ($this->shouldStop($result)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$this->error = new MultipleErrors($errors);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Egulias\EmailValidator\Exception\InvalidEmail|null $possibleError
|
||||
* @param \Egulias\EmailValidator\Exception\InvalidEmail[] $errors
|
||||
*
|
||||
* @return \Egulias\EmailValidator\Exception\InvalidEmail[]
|
||||
*/
|
||||
private function addNewError($possibleError, array $errors)
|
||||
{
|
||||
if (null !== $possibleError) {
|
||||
$errors[] = $possibleError;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $result
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function shouldStop($result)
|
||||
{
|
||||
return !$result && $this->mode === self::STOP_ON_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the validation errors.
|
||||
*
|
||||
* @return MultipleErrors|null
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWarnings()
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
use Egulias\EmailValidator\Validation\Error\RFCWarnings;
|
||||
|
||||
class NoRFCWarningsValidation extends RFCValidation
|
||||
{
|
||||
/**
|
||||
* @var InvalidEmail|null
|
||||
*/
|
||||
private $error;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
{
|
||||
if (!parent::isValid($email, $emailLexer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($this->getWarnings())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->error = new RFCWarnings();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error ?: parent::getError();
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
class RFCValidation implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* @var EmailParser
|
||||
* @var EmailParser|null
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
@@ -19,10 +19,10 @@ class RFCValidation implements EmailValidation
|
||||
private $warnings = [];
|
||||
|
||||
/**
|
||||
* @var InvalidEmail
|
||||
* @var InvalidEmail|null
|
||||
*/
|
||||
private $error;
|
||||
|
||||
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
{
|
||||
$this->parser = new EmailParser($emailLexer);
|
||||
@@ -32,7 +32,7 @@ class RFCValidation implements EmailValidation
|
||||
$this->error = $invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$this->warnings = $this->parser->getWarnings();
|
||||
return true;
|
||||
}
|
||||
+17
-3
@@ -3,28 +3,42 @@
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
use Egulias\EmailValidator\Validation\Error\SpoofEmail;
|
||||
use \Spoofchecker;
|
||||
|
||||
class SpoofCheckValidation implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* @var InvalidEmail
|
||||
* @var InvalidEmail|null
|
||||
*/
|
||||
private $error;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('intl')) {
|
||||
throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress InvalidArgument
|
||||
*/
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
{
|
||||
$checker = new Spoofchecker();
|
||||
$checker->setChecks(Spoofchecker::SINGLE_SCRIPT);
|
||||
|
||||
|
||||
if ($checker->isSuspicious($email)) {
|
||||
$this->error = new SpoofEmail();
|
||||
}
|
||||
|
||||
|
||||
return $this->error === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InvalidEmail|null
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
@@ -6,6 +6,10 @@ class QuotedPart extends Warning
|
||||
{
|
||||
const CODE = 36;
|
||||
|
||||
/**
|
||||
* @param scalar $prevToken
|
||||
* @param scalar $postToken
|
||||
*/
|
||||
public function __construct($prevToken, $postToken)
|
||||
{
|
||||
$this->message = "Deprecated Quoted String found between $prevToken and $postToken";
|
||||
@@ -6,6 +6,10 @@ class QuotedString extends Warning
|
||||
{
|
||||
const CODE = 11;
|
||||
|
||||
/**
|
||||
* @param scalar $prevToken
|
||||
* @param scalar $postToken
|
||||
*/
|
||||
public function __construct($prevToken, $postToken)
|
||||
{
|
||||
$this->message = "Quoted String found between $prevToken and $postToken";
|
||||
@@ -5,24 +5,41 @@ namespace Egulias\EmailValidator\Warning;
|
||||
abstract class Warning
|
||||
{
|
||||
const CODE = 0;
|
||||
protected $message;
|
||||
protected $rfcNumber;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $rfcNumber = 0;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function code()
|
||||
{
|
||||
return self::CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function RFCNumber()
|
||||
{
|
||||
return $this->rfcNumber;
|
||||
}
|
||||
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->message() . " rfc: " . $this->rfcNumber . "interal code: " . static::CODE;
|
||||
@@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\Tests\EmailValidator;
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class EmailLexerTests extends \PHPUnit_Framework_TestCase
|
||||
class EmailLexerTests extends TestCase
|
||||
{
|
||||
|
||||
public function testLexerExtendsLib()
|
||||
@@ -137,6 +138,7 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
|
||||
array(":", EmailLexer::S_COLON),
|
||||
array(".", EmailLexer::S_DOT),
|
||||
array("\"", EmailLexer::S_DQUOTE),
|
||||
array("'", EmailLexer::S_SQUOTE),
|
||||
array("-", EmailLexer::S_HYPHEN),
|
||||
array("\\", EmailLexer::S_BACKSLASH),
|
||||
array("/", EmailLexer::S_SLASH),
|
||||
+7
-5
@@ -1,27 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\Tests\EmailValidator;
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Validation\EmailValidation;
|
||||
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class EmailValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
class EmailValidatorTest extends TestCase
|
||||
{
|
||||
public function testValidationIsUsed()
|
||||
{
|
||||
$validator = new EmailValidator();
|
||||
$validation = $this->getMock("Egulias\\EmailValidator\\Validation\\EmailValidation");
|
||||
$validation = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation->expects($this->once())->method("isValid")->willReturn(true);
|
||||
$validation->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
$validation->expects($this->once())->method("getError")->willReturn(null);
|
||||
|
||||
$this->assertTrue($validator->isValid("example@example.com", $validation));
|
||||
}
|
||||
|
||||
|
||||
public function testMultipleValidation()
|
||||
{
|
||||
$validator = new EmailValidator();
|
||||
$validation = $this->getMock("Egulias\\EmailValidator\\Validation\\EmailValidation");
|
||||
$validation = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation->expects($this->once())->method("isValid")->willReturn(true);
|
||||
$validation->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
$validation->expects($this->once())->method("getError")->willReturn(null);
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
||||
use Egulias\EmailValidator\Exception\LocalOrReservedDomain;
|
||||
use Egulias\EmailValidator\Exception\DomainAcceptsNoMail;
|
||||
use Egulias\EmailValidator\Validation\DNSCheckValidation;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DNSCheckValidationTest extends TestCase
|
||||
{
|
||||
public function validEmailsProvider()
|
||||
{
|
||||
return [
|
||||
// dot-atom
|
||||
['Abc@ietf.org'],
|
||||
['ABC@ietf.org'],
|
||||
['Abc.123@ietf.org'],
|
||||
['user+mailbox/department=shipping@ietf.org'],
|
||||
['!#$%&\'*+-/=?^_`.{|}~@ietf.org'],
|
||||
|
||||
// quoted string
|
||||
['"Abc@def"@ietf.org'],
|
||||
['"Fred\ Bloggs"@ietf.org'],
|
||||
['"Joe.\\Blow"@ietf.org'],
|
||||
|
||||
// unicode
|
||||
['ñandu.cl'],
|
||||
];
|
||||
}
|
||||
|
||||
public function localOrReservedEmailsProvider()
|
||||
{
|
||||
return [
|
||||
// Reserved Top Level DNS Names
|
||||
['test'],
|
||||
['example'],
|
||||
['invalid'],
|
||||
['localhost'],
|
||||
|
||||
// mDNS
|
||||
['local'],
|
||||
|
||||
// Private DNS Namespaces
|
||||
['intranet'],
|
||||
['internal'],
|
||||
['private'],
|
||||
['corp'],
|
||||
['home'],
|
||||
['lan'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider validEmailsProvider
|
||||
*/
|
||||
public function testValidDNS($validEmail)
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$this->assertTrue($validation->isValid($validEmail, new EmailLexer()));
|
||||
}
|
||||
|
||||
public function testInvalidDNS()
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$this->assertFalse($validation->isValid("example@invalid.example.com", new EmailLexer()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider localOrReservedEmailsProvider
|
||||
*/
|
||||
public function testLocalOrReservedDomainError($localOrReservedEmails)
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$expectedError = new LocalOrReservedDomain();
|
||||
$validation->isValid($localOrReservedEmails, new EmailLexer());
|
||||
$this->assertEquals($expectedError, $validation->getError());
|
||||
}
|
||||
|
||||
public function testDomainAcceptsNoMailError()
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$expectedError = new DomainAcceptsNoMail();
|
||||
$isValidResult = $validation->isValid("nullmx@example.com", new EmailLexer());
|
||||
$this->assertEquals($expectedError, $validation->getError());
|
||||
$this->assertFalse($isValidResult);
|
||||
}
|
||||
|
||||
/*
|
||||
public function testDNSWarnings()
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$expectedWarnings = [NoDNSMXRecord::CODE => new NoDNSMXRecord()];
|
||||
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
||||
$this->assertEquals($expectedWarnings, $validation->getWarnings());
|
||||
}
|
||||
*/
|
||||
|
||||
public function testNoDNSError()
|
||||
{
|
||||
$validation = new DNSCheckValidation();
|
||||
$expectedError = new NoDNSRecord();
|
||||
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
||||
$this->assertEquals($expectedError, $validation->getError());
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\Tests\EmailValidator\Validation;
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Validation\DNSCheckValidation;
|
||||
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
|
||||
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
|
||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class IsEmailFunctionTests extends \PHPUnit_Framework_TestCase
|
||||
class IsEmailFunctionTests extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider isEmailTestSuite
|
||||
@@ -27,9 +27,9 @@ class IsEmailFunctionTests extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function isEmailTestSuite()
|
||||
{
|
||||
$testSuite = dirname(__FILE__) . '/../../resources/is_email_tests.xml';
|
||||
$document = new \DOMDocument();
|
||||
$document->load($testSuite);
|
||||
$testSuite = __DIR__ . '/../../resources/is_email_tests.xml';
|
||||
$document = new \DOMDocument();
|
||||
$document->load($testSuite);
|
||||
$elements = $document->getElementsByTagName('test');
|
||||
$tests = [];
|
||||
|
||||
@@ -40,5 +40,4 @@ class IsEmailFunctionTests extends \PHPUnit_Framework_TestCase
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\CommaInDomain;
|
||||
use Egulias\EmailValidator\Exception\NoDomainPart;
|
||||
use Egulias\EmailValidator\Validation\EmailValidation;
|
||||
use Egulias\EmailValidator\Validation\MultipleErrors;
|
||||
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
|
||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||
use Egulias\EmailValidator\Warning\AddressLiteral;
|
||||
use Egulias\EmailValidator\Warning\DomainLiteral;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MultipleValidationWithAndTest extends TestCase
|
||||
{
|
||||
public function testUsesAndLogicalOperation()
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
$validationTrue = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validationTrue->expects($this->any())->method("isValid")->willReturn(true);
|
||||
$validationTrue->expects($this->any())->method("getWarnings")->willReturn([]);
|
||||
$validationFalse = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validationFalse->expects($this->any())->method("isValid")->willReturn(false);
|
||||
$validationFalse->expects($this->any())->method("getWarnings")->willReturn([]);
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validationTrue, $validationFalse]);
|
||||
$this->assertFalse($multipleValidation->isValid("exmpale@example.com", $lexer));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Egulias\EmailValidator\Validation\Exception\EmptyValidationList
|
||||
*/
|
||||
public function testEmptyListIsNotAllowed()
|
||||
{
|
||||
new MultipleValidationWithAnd([]);
|
||||
}
|
||||
|
||||
public function testValidationIsValid()
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
|
||||
$validation = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation->expects($this->any())->method("isValid")->willReturn(true);
|
||||
$validation->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validation]);
|
||||
$this->assertTrue($multipleValidation->isValid("example@example.com", $lexer));
|
||||
$this->assertNull($multipleValidation->getError());
|
||||
}
|
||||
|
||||
public function testAccumulatesWarnings()
|
||||
{
|
||||
$warnings1 = [
|
||||
AddressLiteral::CODE => new AddressLiteral()
|
||||
];
|
||||
$warnings2 = [
|
||||
DomainLiteral::CODE => new DomainLiteral()
|
||||
];
|
||||
$expectedResult = array_merge($warnings1, $warnings2);
|
||||
|
||||
$lexer = new EmailLexer();
|
||||
$validation1 = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation1->expects($this->any())->method("isValid")->willReturn(true);
|
||||
$validation1->expects($this->once())->method("getWarnings")->willReturn($warnings1);
|
||||
|
||||
$validation2 = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
|
||||
$validation2->expects($this->any())->method("isValid")->willReturn(false);
|
||||
$validation2->expects($this->once())->method("getWarnings")->willReturn($warnings2);
|
||||
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validation1, $validation2]);
|
||||
$multipleValidation->isValid("example@example.com", $lexer);
|
||||
$this->assertEquals($expectedResult, $multipleValidation->getWarnings());
|
||||
}
|
||||
|
||||
public function testGathersAllTheErrors()
|
||||
{
|
||||
$error1 = new CommaInDomain();
|
||||
$error2 = new NoDomainPart();
|
||||
|
||||
$expectedResult = new MultipleErrors([$error1, $error2]);
|
||||
|
||||
$lexer = new EmailLexer();
|
||||
|
||||
$validation1 = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation1->expects($this->once())->method("isValid")->willReturn(false);
|
||||
$validation1->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
$validation1->expects($this->once())->method("getError")->willReturn($error1);
|
||||
|
||||
$validation2 = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation2->expects($this->once())->method("isValid")->willReturn(false);
|
||||
$validation2->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
$validation2->expects($this->once())->method("getError")->willReturn($error2);
|
||||
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validation1, $validation2]);
|
||||
$multipleValidation->isValid("example@example.com", $lexer);
|
||||
$this->assertEquals($expectedResult, $multipleValidation->getError());
|
||||
}
|
||||
|
||||
public function testStopsAfterFirstError()
|
||||
{
|
||||
$error1 = new CommaInDomain();
|
||||
$error2 = new NoDomainPart();
|
||||
|
||||
$expectedResult = new MultipleErrors([$error1]);
|
||||
|
||||
$lexer = new EmailLexer();
|
||||
|
||||
$validation1 = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation1->expects($this->any())->method("isValid")->willReturn(false);
|
||||
$validation1->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
$validation1->expects($this->once())->method("getError")->willReturn($error1);
|
||||
|
||||
$validation2 = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation2->expects($this->any())->method("isValid")->willReturn(false);
|
||||
$validation2->expects($this->never())->method("getWarnings")->willReturn([]);
|
||||
$validation2->expects($this->never())->method("getError")->willReturn($error2);
|
||||
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validation1, $validation2], MultipleValidationWithAnd::STOP_ON_ERROR);
|
||||
$multipleValidation->isValid("example@example.com", $lexer);
|
||||
$this->assertEquals($expectedResult, $multipleValidation->getError());
|
||||
}
|
||||
|
||||
public function testBreakOutOfLoopWhenError()
|
||||
{
|
||||
$error = new CommaInDomain();
|
||||
|
||||
$expectedResult = new MultipleErrors([$error]);
|
||||
|
||||
$lexer = new EmailLexer();
|
||||
|
||||
$validation1 = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation1->expects($this->any())->method("isValid")->willReturn(false);
|
||||
$validation1->expects($this->once())->method("getWarnings")->willReturn([]);
|
||||
$validation1->expects($this->once())->method("getError")->willReturn($error);
|
||||
|
||||
$validation2 = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validation2->expects($this->never())->method("isValid");
|
||||
$validation2->expects($this->never())->method("getWarnings");
|
||||
$validation2->expects($this->never())->method("getError");
|
||||
|
||||
$multipleValidation = new MultipleValidationWithAnd([$validation1, $validation2], MultipleValidationWithAnd::STOP_ON_ERROR);
|
||||
$multipleValidation->isValid("example@example.com", $lexer);
|
||||
$this->assertEquals($expectedResult, $multipleValidation->getError());
|
||||
}
|
||||
|
||||
public function testBreakoutOnInvalidEmail()
|
||||
{
|
||||
$lexer = new EmailLexer();
|
||||
|
||||
$validationNotCalled = $this->getMockBuilder(EmailValidation::class)->getMock();
|
||||
$validationNotCalled->expects($this->never())->method("isValid");
|
||||
$validationNotCalled->expects($this->never())->method("getWarnings");
|
||||
$validationNotCalled->expects($this->never())->method("getError");
|
||||
$multipleValidation = new MultipleValidationWithAnd([new RFCValidation(), $validationNotCalled], MultipleValidationWithAnd::STOP_ON_ERROR);
|
||||
$this->assertFalse($multipleValidation->isValid("invalid-email", $lexer));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\NoDomainPart;
|
||||
use Egulias\EmailValidator\Validation\Error\RFCWarnings;
|
||||
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class NoRFCWarningsValidationTest extends TestCase
|
||||
{
|
||||
public function testInvalidEmailIsInvalid()
|
||||
{
|
||||
$validation = new NoRFCWarningsValidation();
|
||||
|
||||
$this->assertFalse($validation->isValid('non-email-string', new EmailLexer()));
|
||||
$this->assertInstanceOf(NoDomainPart::class, $validation->getError());
|
||||
}
|
||||
|
||||
public function testEmailWithWarningsIsInvalid()
|
||||
{
|
||||
$validation = new NoRFCWarningsValidation();
|
||||
|
||||
$this->assertFalse($validation->isValid(str_repeat('x', 254).'@example.com', new EmailLexer())); // too long email
|
||||
$this->assertInstanceOf(RFCWarnings::class, $validation->getError());
|
||||
}
|
||||
|
||||
public function testEmailWithoutWarningsIsValid()
|
||||
{
|
||||
$validation = new NoRFCWarningsValidation();
|
||||
|
||||
$this->assertTrue($validation->isValid('example@example.com', new EmailLexer()));
|
||||
$this->assertNull($validation->getError());
|
||||
}
|
||||
}
|
||||
+18
-9
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\Tests\EmailValidator\Validation;
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||
@@ -35,8 +35,9 @@ use Egulias\EmailValidator\Warning\LabelTooLong;
|
||||
use Egulias\EmailValidator\Warning\LocalTooLong;
|
||||
use Egulias\EmailValidator\Warning\ObsoleteDTEXT;
|
||||
use Egulias\EmailValidator\Warning\QuotedString;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class RFCValidationTest extends \PHPUnit_Framework_TestCase
|
||||
class RFCValidationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var RFCValidation
|
||||
@@ -47,7 +48,7 @@ class RFCValidationTest extends \PHPUnit_Framework_TestCase
|
||||
* @var EmailLexer
|
||||
*/
|
||||
protected $lexer;
|
||||
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->validator = new RFCValidation();
|
||||
@@ -103,10 +104,10 @@ class RFCValidationTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testInvalidUTF8Email()
|
||||
{
|
||||
$email = "\x80\x81\x82@\x83\x84\x85.\x86\x87\x88";
|
||||
$email = "\x80\x81\x82@\x83\x84\x85.\x86\x87\x88";
|
||||
$this->assertFalse($this->validator->isValid($email, $this->lexer));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider getInvalidEmails
|
||||
*/
|
||||
@@ -165,6 +166,9 @@ class RFCValidationTest extends \PHPUnit_Framework_TestCase
|
||||
['test@email>'],
|
||||
['test@email<'],
|
||||
['test@email{'],
|
||||
['test@ '],
|
||||
['test@example.com []'],
|
||||
["test@example.com'"],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -209,6 +213,7 @@ class RFCValidationTest extends \PHPUnit_Framework_TestCase
|
||||
[new CRNoLF(), "example@exa\rmple.co.uk"],
|
||||
[new CRNoLF(), "example@[\r]"],
|
||||
[new CRNoLF(), "exam\rple@example.co.uk"],
|
||||
[new ExpectingATEXT(), "test@example.com'"],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -219,16 +224,16 @@ class RFCValidationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$this->assertTrue($this->validator->isValid($email, $this->lexer));
|
||||
$warnings = $this->validator->getWarnings();
|
||||
$this->assertTrue(
|
||||
count($warnings) === count($expectedWarnings),
|
||||
$this->assertCount(
|
||||
count($warnings), $expectedWarnings,
|
||||
"Expected: " . implode(",", $expectedWarnings) . " and got " . implode(",", $warnings)
|
||||
);
|
||||
|
||||
foreach ($warnings as $warning) {
|
||||
$this->assertTrue(isset($expectedWarnings[$warning->code()]));
|
||||
$this->assertArrayHasKey($warning->code(), $expectedWarnings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getInvalidEmailsWithWarnings()
|
||||
{
|
||||
return [
|
||||
@@ -263,6 +268,10 @@ class RFCValidationTest extends \PHPUnit_Framework_TestCase
|
||||
[LocalTooLong::CODE,],
|
||||
'too_long_localpart_too_long_localpart_too_long_localpart_too_long_localpart@invalid.example.com'
|
||||
],
|
||||
[
|
||||
[LocalTooLong::CODE],
|
||||
'too_long_localpart_too_long_localpart_123_too_long_localpart_too_long_localpart@example.com'
|
||||
],
|
||||
[
|
||||
[LabelTooLong::CODE,],
|
||||
'example@toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart.co.uk'
|
||||
+6
-5
@@ -1,11 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\Tests\EmailValidator\Validation;
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Validation\SpoofCheckValidation;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SpoofCheckValidationTest extends \PHPUnit_Framework_TestCase
|
||||
class SpoofCheckValidationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider validUTF8EmailsProvider
|
||||
@@ -14,17 +15,17 @@ class SpoofCheckValidationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$this->markTestSkipped("Skipped for Travis CI since it is failing on this test for unknown reasons.");
|
||||
$validation = new SpoofCheckValidation();
|
||||
|
||||
|
||||
$this->assertTrue($validation->isValid($email, new EmailLexer()));
|
||||
}
|
||||
|
||||
|
||||
public function testEmailWithSpoofsIsInvalid()
|
||||
{
|
||||
$validation = new SpoofCheckValidation();
|
||||
|
||||
$this->assertFalse($validation->isValid("Кириллица"."latin漢字"."ひらがな"."カタカナ", new EmailLexer()));
|
||||
}
|
||||
|
||||
|
||||
public function validUTF8EmailsProvider()
|
||||
{
|
||||
return [
|
||||
Reference in New Issue
Block a user