mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-09-03 05:57:57 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 840d5603eb | |||
| feba9587ec | |||
| 5568c52464 |
@@ -1,7 +1,12 @@
|
|||||||
# EmailValidator
|
# 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)
|
|
||||||
=============================
|
[](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)
|
||||||
|
|
||||||
## Suported RFCs ##
|
## Suported RFCs ##
|
||||||
|
|
||||||
This library aims to support:
|
This library aims to support:
|
||||||
|
|
||||||
RFC 5321, 5322, 6530, 6531, 6532.
|
RFC 5321, 5322, 6530, 6531, 6532.
|
||||||
@@ -9,7 +14,7 @@ RFC 5321, 5322, 6530, 6531, 6532.
|
|||||||
## Requirements ##
|
## Requirements ##
|
||||||
|
|
||||||
* [Composer](https://getcomposer.org) is required for installation
|
* [Composer](https://getcomposer.org) is required for installation
|
||||||
* [Spoofchecking](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/SpoofCheckValidation.php) and [DNSCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/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)
|
* [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 ##
|
## Installation ##
|
||||||
|
|
||||||
@@ -20,6 +25,7 @@ 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).
|
`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
|
A basic example with the RFC validation
|
||||||
@@ -36,11 +42,11 @@ $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)
|
1. [RFCValidation](/src/Validation/RFCValidation.php)
|
||||||
2. [NoRFCWarningsValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/NoRFCWarningsValidation.php)
|
2. [NoRFCWarningsValidation](/src/Validation/NoRFCWarningsValidation.php)
|
||||||
3. [DNSCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/DNSCheckValidation.php)
|
3. [DNSCheckValidation](/src/Validation/DNSCheckValidation.php)
|
||||||
4. [SpoofCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/SpoofCheckValidation.php)
|
4. [SpoofCheckValidation](/src/Validation/SpoofCheckValidation.php)
|
||||||
5. [MultipleValidationWithAnd](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/MultipleValidationWithAnd.php)
|
5. [MultipleValidationWithAnd](/src/Validation/MultipleValidationWithAnd.php)
|
||||||
6. [Your own validation](#how-to-extend)
|
6. [Your own validation](#how-to-extend)
|
||||||
|
|
||||||
`MultipleValidationWithAnd`
|
`MultipleValidationWithAnd`
|
||||||
@@ -65,10 +71,11 @@ $validator->isValid("example@example.com", $multipleValidations); //true
|
|||||||
|
|
||||||
### How to extend ###
|
### How to extend ###
|
||||||
|
|
||||||
It's easy! You just need to implement [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))
|
(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:
|
As this is a port from another library and work, here are other people related to the previous one:
|
||||||
@@ -78,5 +85,5 @@ As this is a port from another library and work, here are other people related t
|
|||||||
* Dominic Sayers [@dominicsayers](https://github.com/dominicsayers): The original isemail function
|
* Dominic Sayers [@dominicsayers](https://github.com/dominicsayers): The original isemail function
|
||||||
|
|
||||||
## License ##
|
## License ##
|
||||||
Released under the MIT License attached with this code.
|
|
||||||
|
|
||||||
|
Released under the MIT License attached with this code.
|
||||||
|
|||||||
+2
-2
@@ -12,14 +12,14 @@
|
|||||||
>
|
>
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="EmailValidator Test Suite">
|
<testsuite name="EmailValidator Test Suite">
|
||||||
<directory>./Tests/EmailValidator</directory>
|
<directory>./tests/EmailValidator</directory>
|
||||||
<exclude>./vendor/</exclude>
|
<exclude>./vendor/</exclude>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
|
||||||
<filter>
|
<filter>
|
||||||
<whitelist>
|
<whitelist>
|
||||||
<directory>./EmailValidator/</directory>
|
<directory>./src/</directory>
|
||||||
</whitelist>
|
</whitelist>
|
||||||
</filter>
|
</filter>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|||||||
@@ -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)';
|
||||||
|
}
|
||||||
@@ -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)';
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ namespace Egulias\EmailValidator\Validation;
|
|||||||
|
|
||||||
use Egulias\EmailValidator\EmailLexer;
|
use Egulias\EmailValidator\EmailLexer;
|
||||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||||
|
use Egulias\EmailValidator\Exception\LocalOrReservedDomain;
|
||||||
|
use Egulias\EmailValidator\Exception\DomainAcceptsNoMail;
|
||||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||||
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
||||||
|
|
||||||
@@ -19,6 +21,12 @@ class DNSCheckValidation implements EmailValidation
|
|||||||
*/
|
*/
|
||||||
private $error;
|
private $error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $mxRecords = [];
|
||||||
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
if (!function_exists('idn_to_ascii')) {
|
if (!function_exists('idn_to_ascii')) {
|
||||||
@@ -36,7 +44,40 @@ class DNSCheckValidation implements EmailValidation
|
|||||||
$host = substr($email, $lastAtPos + 1);
|
$host = substr($email, $lastAtPos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->checkDNS($host);
|
// 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()
|
public function getError()
|
||||||
@@ -54,24 +95,74 @@ class DNSCheckValidation implements EmailValidation
|
|||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function checkDNS($host)
|
protected function checkDns($host)
|
||||||
{
|
{
|
||||||
$variant = INTL_IDNA_VARIANT_2003;
|
$variant = INTL_IDNA_VARIANT_2003;
|
||||||
if ( defined('INTL_IDNA_VARIANT_UTS46') ) {
|
if (defined('INTL_IDNA_VARIANT_UTS46')) {
|
||||||
$variant = INTL_IDNA_VARIANT_UTS46;
|
$variant = INTL_IDNA_VARIANT_UTS46;
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = rtrim(idn_to_ascii($host, IDNA_DEFAULT, $variant), '.') . '.';
|
$host = rtrim(idn_to_ascii($host, IDNA_DEFAULT, $variant), '.') . '.';
|
||||||
|
|
||||||
$Aresult = true;
|
return $this->validateDnsRecords($host);
|
||||||
$MXresult = checkdnsrr($host, 'MX');
|
}
|
||||||
|
|
||||||
if (!$MXresult) {
|
|
||||||
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
|
/**
|
||||||
$Aresult = checkdnsrr($host, 'A') || checkdnsrr($host, 'AAAA');
|
* Validate the DNS records for given host.
|
||||||
if (!$Aresult) {
|
*
|
||||||
$this->error = new NoDNSRecord();
|
* @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
|
||||||
|
$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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $MXresult || $Aresult;
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ namespace Egulias\EmailValidator\Tests\EmailValidator\Validation;
|
|||||||
|
|
||||||
use Egulias\EmailValidator\EmailLexer;
|
use Egulias\EmailValidator\EmailLexer;
|
||||||
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
||||||
|
use Egulias\EmailValidator\Exception\LocalOrReservedDomain;
|
||||||
|
use Egulias\EmailValidator\Exception\DomainAcceptsNoMail;
|
||||||
use Egulias\EmailValidator\Validation\DNSCheckValidation;
|
use Egulias\EmailValidator\Validation\DNSCheckValidation;
|
||||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
@@ -14,22 +16,44 @@ class DNSCheckValidationTest extends TestCase
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
// dot-atom
|
// dot-atom
|
||||||
['Abc@example.com'],
|
['Abc@ietf.org'],
|
||||||
['ABC@EXAMPLE.COM'],
|
['ABC@ietf.org'],
|
||||||
['Abc.123@example.com'],
|
['Abc.123@ietf.org'],
|
||||||
['user+mailbox/department=shipping@example.com'],
|
['user+mailbox/department=shipping@ietf.org'],
|
||||||
['!#$%&\'*+-/=?^_`.{|}~@example.com'],
|
['!#$%&\'*+-/=?^_`.{|}~@ietf.org'],
|
||||||
|
|
||||||
// quoted string
|
// quoted string
|
||||||
['"Abc@def"@example.com'],
|
['"Abc@def"@ietf.org'],
|
||||||
['"Fred\ Bloggs"@example.com'],
|
['"Fred\ Bloggs"@ietf.org'],
|
||||||
['"Joe.\\Blow"@example.com'],
|
['"Joe.\\Blow"@ietf.org'],
|
||||||
|
|
||||||
// unicide
|
// unicide
|
||||||
['ñandu.cl'],
|
['ñ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
|
* @dataProvider validEmailsProvider
|
||||||
*/
|
*/
|
||||||
@@ -45,6 +69,27 @@ class DNSCheckValidationTest extends TestCase
|
|||||||
$this->assertFalse($validation->isValid("example@invalid.example.com", new EmailLexer()));
|
$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()
|
public function testDNSWarnings()
|
||||||
{
|
{
|
||||||
$validation = new DNSCheckValidation();
|
$validation = new DNSCheckValidation();
|
||||||
@@ -52,6 +97,7 @@ class DNSCheckValidationTest extends TestCase
|
|||||||
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
||||||
$this->assertEquals($expectedWarnings, $validation->getWarnings());
|
$this->assertEquals($expectedWarnings, $validation->getWarnings());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
public function testNoDNSError()
|
public function testNoDNSError()
|
||||||
{
|
{
|
||||||
@@ -60,4 +106,4 @@ class DNSCheckValidationTest extends TestCase
|
|||||||
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
||||||
$this->assertEquals($expectedError, $validation->getError());
|
$this->assertEquals($expectedError, $validation->getError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user