Compare commits

..

15 Commits

Author SHA1 Message Date
Chris McCafferty b8bb147f46 Added EmailValidatorInterface (#120) 2016-07-03 23:52:18 +02:00
Eduardo Gulias Davis cbfe3ee6f7 Added test proof (#110) 2016-05-15 12:06:34 +02:00
Christian Flothmann 4b59310c1a fix version constraints (#109) 2016-05-15 11:16:58 +02:00
Eduardo Gulias Davis f463fbe542 Changed invalid dns domains and backslash in domain (#107)
Fixes #91 and #74 for v1.2.x
2016-05-15 01:10:54 +02:00
Eduardo Gulias Davis afc6adc0b9 Merge pull request #89 from natechicago/minor-edits
Adding documentation and consolidating some code for readability
2016-03-22 09:34:08 +01:00
natechicago 58e44fb064 Adding documentation, and making a few minor code changes for readability. 2016-03-15 18:57:49 -05:00
Eduardo Gulias Davis 04c6cdf987 Merge pull request #84 from egulias/82-brackets
#82 orphan brackets
2015-11-11 04:28:32 +01:00
Eduardo Gulias Davis cc9defe645 fixes #82 2015-11-11 04:24:15 +01:00
Eduardo Gulias Davis 50aaa0b18f Control tests for .com.xx domains 2015-11-11 03:59:45 +01:00
Andrei Sozonov de448a30fa opened parenthesis converted to class field 2015-10-12 00:34:23 +02:00
Andrei Sozonov a7b385301d #80 validate number of closing parenthesis 2015-10-12 00:34:23 +02:00
Eduardo Gulias Davis f6447f8b99 Merge pull request #77 from alexpott/patch-1
Remove duplicated tokens
2015-07-16 08:29:32 +02:00
alexpott d4001715b2 Update EmailLexer.php
Removing duplicate array keys
2015-07-15 15:39:42 +01:00
Eduardo Gulias Davis 4b7fcf6796 Merge pull request #75 from xabbuh/patch-1
update installation instructions
2015-06-29 23:49:12 +02:00
Christian Flothmann 2a1145ffbf update installation instructions
Given that Composer will install the latest version of a package
matching the stability restrictions that apply to project, users can
get an undesired version of package when they don't specify a version
constraint.
2015-06-26 19:35:00 +02:00
87 changed files with 1896 additions and 3188 deletions
-4
View File
@@ -1,4 +0,0 @@
/Tests export-ignore
/documentation export-ignore
/.* export-ignore
/phpunit.xml.dist
-1
View File
@@ -1,3 +1,2 @@
report/
vendor/
.idea
+9 -10
View File
@@ -3,25 +3,26 @@ sudo: false
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3
- hhvm
env:
global:
- deps=no
- deps=high
matrix:
fast_finish: true
include:
- php: 5.5
- php: 5.3
env: deps=low
- php: 5.6
env: deps=high
- php: 5.4
env: deps=no
- php: 5.5
env: deps=no
install:
- if [ "$deps" = "no" ]; then composer install; fi
@@ -30,7 +31,5 @@ install:
script:
- mkdir -p build/logs
- vendor/bin/phpunit --coverage-clover build/logs/clover.xml
- phpunit --coverage-clover build/logs/clover.xml
after_script:
- php vendor/bin/coveralls
-67
View File
@@ -1,67 +0,0 @@
<?php
namespace Egulias\EmailValidator;
use Egulias\EmailValidator\Exception\InvalidEmail;
use Egulias\EmailValidator\Validation\EmailValidation;
class EmailValidator
{
/**
* @var EmailLexer
*/
private $lexer;
/**
* @var array
*/
protected $warnings;
/**
* @var InvalidEmail
*/
protected $error;
public function __construct()
{
$this->lexer = new EmailLexer();
}
/**
* @param $email
* @param EmailValidation $emailValidation
* @return bool
*/
public function isValid($email, EmailValidation $emailValidation)
{
$isValid = $emailValidation->isValid($email, $this->lexer);
$this->warnings = $emailValidation->getWarnings();
$this->error = $emailValidation->getError();
return $isValid;
}
/**
* @return boolean
*/
public function hasWarnings()
{
return !empty($this->warnings);
}
/**
* @return array
*/
public function getWarnings()
{
return $this->warnings;
}
/**
* @return InvalidEmail
*/
public function getError()
{
return $this->error;
}
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class AtextAfterCFWS extends InvalidEmail
{
const CODE = 133;
const REASON = "ATEXT found after CFWS";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class CRLFAtTheEnd extends InvalidEmail
{
const CODE = 149;
const REASON = "CRLF at the end";
}
-9
View File
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class CRLFX2 extends InvalidEmail
{
const CODE = 148;
const REASON = "Folding whitespace CR LF found twice";
}
-9
View File
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class CRNoLF extends InvalidEmail
{
const CODE = 150;
const REASON = "Missing LF after CR";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class CharNotAllowed extends InvalidEmail
{
const CODE = 201;
const REASON = "Non allowed character in domain";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class CommaInDomain extends InvalidEmail
{
const CODE = 200;
const REASON = "Comma ',' is not allowed in domain part";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class ConsecutiveAt extends InvalidEmail
{
const CODE = 128;
const REASON = "Consecutive AT";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class ConsecutiveDot extends InvalidEmail
{
const CODE = 132;
const REASON = "Consecutive DOT";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class DomainHyphened extends InvalidEmail
{
const CODE = 144;
const REASON = "Hyphen found in domain";
}
-9
View File
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class DotAtEnd extends InvalidEmail
{
const CODE = 142;
const REASON = "Dot at the end";
}
-9
View File
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class DotAtStart extends InvalidEmail
{
const CODE = 141;
const REASON = "Found DOT at start";
}
-9
View File
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class ExpectingAT extends InvalidEmail
{
const CODE = 202;
const REASON = "Expecting AT '@' ";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class ExpectingATEXT extends InvalidEmail
{
const CODE = 137;
const REASON = "Expecting ATEXT";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class ExpectingCTEXT extends InvalidEmail
{
const CODE = 139;
const REASON = "Expecting CTEXT";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class ExpectingDTEXT extends InvalidEmail
{
const CODE = 129;
const REASON = "Expected DTEXT";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class ExpectingDomainLiteralClose extends InvalidEmail
{
const CODE = 137;
const REASON = "Closing bracket ']' for domain literal not found";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class ExpectedQPair extends InvalidEmail
{
const CODE = 136;
const REASON = "Expecting QPAIR";
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
abstract class InvalidEmail extends \InvalidArgumentException
{
const REASON = "Invalid email";
const CODE = 0;
public function __construct()
{
parent::__construct(static::REASON, static::CODE);
}
}
-11
View File
@@ -1,11 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
use Egulias\EmailValidator\Exception\InvalidEmail;
class NoDNSRecord extends InvalidEmail
{
const CODE = 5;
const REASON = 'No MX or A DSN record was found for this email';
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class NoDomainPart extends InvalidEmail
{
const CODE = 131;
const REASON = "No Domain part";
}
-9
View File
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class NoLocalPart extends InvalidEmail
{
const CODE = 130;
const REASON = "No local part";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class UnclosedComment extends InvalidEmail
{
const CODE = 146;
const REASON = "No colosing comment token found";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class UnclosedQuotedString extends InvalidEmail
{
const CODE = 145;
const REASON = "Unclosed quoted string";
}
@@ -1,9 +0,0 @@
<?php
namespace Egulias\EmailValidator\Exception;
class UnopenedComment extends InvalidEmail
{
const CODE = 152;
const REASON = "No opening comment token found";
}
@@ -1,72 +0,0 @@
<?php
namespace Egulias\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Exception\InvalidEmail;
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
use Egulias\EmailValidator\Exception\NoDNSRecord;
class DNSCheckValidation implements EmailValidation
{
/**
* @var array
*/
private $warnings = [];
/**
* @var InvalidEmail
*/
private $error;
public function __construct()
{
if (!extension_loaded('intl')) {
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);
}
return $this->checkDNS($host);
}
public function getError()
{
return $this->error;
}
public function getWarnings()
{
return $this->warnings;
}
protected function checkDNS($host)
{
$variant = INTL_IDNA_VARIANT_2003;
if ( defined('INTL_IDNA_VARIANT_UTS46') ) {
$variant = INTL_IDNA_VARIANT_UTS46;
}
$host = rtrim(idn_to_ascii($host, IDNA_DEFAULT, $variant), '.') . '.';
$Aresult = true;
$MXresult = checkdnsrr($host, 'MX');
if (!$MXresult) {
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
$Aresult = checkdnsrr($host, 'A') || checkdnsrr($host, 'AAAA');
if (!$Aresult) {
$this->error = new NoDNSRecord();
}
}
return $MXresult || $Aresult;
}
}
@@ -1,34 +0,0 @@
<?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();
}
@@ -1,11 +0,0 @@
<?php
namespace Egulias\EmailValidator\Validation\Error;
use Egulias\EmailValidator\Exception\InvalidEmail;
class RFCWarnings extends InvalidEmail
{
const CODE = 997;
const REASON = 'Warnings were found.';
}
@@ -1,11 +0,0 @@
<?php
namespace Egulias\EmailValidator\Validation\Error;
use Egulias\EmailValidator\Exception\InvalidEmail;
class SpoofEmail extends InvalidEmail
{
const CODE = 998;
const REASON = "The email contains mixed UTF8 chars that makes it suspicious";
}
@@ -1,13 +0,0 @@
<?php
namespace Egulias\EmailValidator\Validation\Exception;
use Exception;
class EmptyValidationList extends \InvalidArgumentException
{
public function __construct($code = 0, Exception $previous = null)
{
parent::__construct("Empty validation list is not allowed", $code, $previous);
}
}
@@ -1,26 +0,0 @@
<?php
namespace Egulias\EmailValidator\Validation;
use Egulias\EmailValidator\Exception\InvalidEmail;
class MultipleErrors extends InvalidEmail
{
const CODE = 999;
const REASON = "Accumulated errors for multiple validations";
/**
* @var array
*/
private $errors = [];
public function __construct(array $errors)
{
$this->errors = $errors;
parent::__construct();
}
public function getErrors()
{
return $this->errors;
}
}
@@ -1,111 +0,0 @@
<?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
*/
private $error;
/**
* @var bool
*/
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;
}
private function addNewError($possibleError, array $errors)
{
if (null !== $possibleError) {
$errors[] = $possibleError;
}
return $errors;
}
private function shouldStop($result)
{
return !$result && $this->mode === self::STOP_ON_ERROR;
}
/**
* {@inheritdoc}
*/
public function getError()
{
return $this->error;
}
/**
* {@inheritdoc}
*/
public function getWarnings()
{
return $this->warnings;
}
}
@@ -1,41 +0,0 @@
<?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
*/
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();
}
}
@@ -1,49 +0,0 @@
<?php
namespace Egulias\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\EmailParser;
use Egulias\EmailValidator\Exception\InvalidEmail;
class RFCValidation implements EmailValidation
{
/**
* @var EmailParser
*/
private $parser;
/**
* @var array
*/
private $warnings = [];
/**
* @var InvalidEmail
*/
private $error;
public function isValid($email, EmailLexer $emailLexer)
{
$this->parser = new EmailParser($emailLexer);
try {
$this->parser->parse((string)$email);
} catch (InvalidEmail $invalid) {
$this->error = $invalid;
return false;
}
$this->warnings = $this->parser->getWarnings();
return true;
}
public function getError()
{
return $this->error;
}
public function getWarnings()
{
return $this->warnings;
}
}
@@ -1,45 +0,0 @@
<?php
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
*/
private $error;
public function __construct()
{
if (!extension_loaded('intl')) {
throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
}
}
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;
}
public function getError()
{
return $this->error;
}
public function getWarnings()
{
return [];
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class AddressLiteral extends Warning
{
const CODE = 12;
public function __construct()
{
$this->message = 'Address literal in domain part';
$this->rfcNumber = 5321;
}
}
-13
View File
@@ -1,13 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class CFWSNearAt extends Warning
{
const CODE = 49;
public function __construct()
{
$this->message = "Deprecated folding white space near @";
}
}
-13
View File
@@ -1,13 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class CFWSWithFWS extends Warning
{
const CODE = 18;
public function __construct()
{
$this->message = 'Folding whites space followed by folding white space';
}
}
-13
View File
@@ -1,13 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class Comment extends Warning
{
const CODE = 17;
public function __construct()
{
$this->message = "Comments found in this email";
}
}
@@ -1,13 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class DeprecatedComment extends Warning
{
const CODE = 37;
public function __construct()
{
$this->message = 'Deprecated comments';
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class DomainLiteral extends Warning
{
const CODE = 70;
public function __construct()
{
$this->message = 'Domain Literal';
$this->rfcNumber = 5322;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class DomainTooLong extends Warning
{
const CODE = 255;
public function __construct()
{
$this->message = 'Domain is too long, exceeds 255 chars';
$this->rfcNumber = 5322;
}
}
-15
View File
@@ -1,15 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
use Egulias\EmailValidator\EmailParser;
class EmailTooLong extends Warning
{
const CODE = 66;
public function __construct()
{
$this->message = 'Email is too long, exceeds ' . EmailParser::EMAIL_MAX_LENGTH;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class IPV6BadChar extends Warning
{
const CODE = 74;
public function __construct()
{
$this->message = 'Bad char in IPV6 domain literal';
$this->rfcNumber = 5322;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class IPV6ColonEnd extends Warning
{
const CODE = 77;
public function __construct()
{
$this->message = ':: found at the end of the domain literal';
$this->rfcNumber = 5322;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class IPV6ColonStart extends Warning
{
const CODE = 76;
public function __construct()
{
$this->message = ':: found at the start of the domain literal';
$this->rfcNumber = 5322;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class IPV6Deprecated extends Warning
{
const CODE = 13;
public function __construct()
{
$this->message = 'Deprecated form of IPV6';
$this->rfcNumber = 5321;
}
}
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class IPV6DoubleColon extends Warning
{
const CODE = 73;
public function __construct()
{
$this->message = 'Double colon found after IPV6 tag';
$this->rfcNumber = 5322;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class IPV6GroupCount extends Warning
{
const CODE = 72;
public function __construct()
{
$this->message = 'Group count is not IPV6 valid';
$this->rfcNumber = 5322;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class IPV6MaxGroups extends Warning
{
const CODE = 75;
public function __construct()
{
$this->message = 'Reached the maximum number of IPV6 groups allowed';
$this->rfcNumber = 5321;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class LabelTooLong extends Warning
{
const CODE = 63;
public function __construct()
{
$this->message = 'Label too long';
$this->rfcNumber = 5322;
}
}
-15
View File
@@ -1,15 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class LocalTooLong extends Warning
{
const CODE = 64;
const LOCAL_PART_LENGTH = 64;
public function __construct()
{
$this->message = 'Local part is too long, exceeds 64 chars (octets)';
$this->rfcNumber = 5322;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class NoDNSMXRecord extends Warning
{
const CODE = 6;
public function __construct()
{
$this->message = 'No MX DSN record was found for this email';
$this->rfcNumber = 5321;
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class ObsoleteDTEXT extends Warning
{
const CODE = 71;
public function __construct()
{
$this->rfcNumber = 5322;
$this->message = 'Obsolete DTEXT in domain literal';
}
}
-13
View File
@@ -1,13 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class QuotedPart extends Warning
{
const CODE = 36;
public function __construct($prevToken, $postToken)
{
$this->message = "Deprecated Quoted String found between $prevToken and $postToken";
}
}
-13
View File
@@ -1,13 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class QuotedString extends Warning
{
const CODE = 11;
public function __construct($prevToken, $postToken)
{
$this->message = "Quoted String found between $prevToken and $postToken";
}
}
-13
View File
@@ -1,13 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
class TLD extends Warning
{
const CODE = 9;
public function __construct()
{
$this->message = "RFC5321, TLD";
}
}
-30
View File
@@ -1,30 +0,0 @@
<?php
namespace Egulias\EmailValidator\Warning;
abstract class Warning
{
const CODE = 0;
protected $message;
protected $rfcNumber;
public function message()
{
return $this->message;
}
public function code()
{
return self::CODE;
}
public function RFCNumber()
{
return $this->rfcNumber;
}
public function __toString()
{
return $this->message() . " rfc: " . $this->rfcNumber . "interal code: " . static::CODE;
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2013-2016 Eduardo Gulias Davis
Copyright (c) 2013 Eduardo Gulias Davis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+33 -56
View File
@@ -1,82 +1,59 @@
# EmailValidator
#EmailValidator
[![Build Status](https://travis-ci.org/egulias/EmailValidator.png?branch=master)](https://travis-ci.org/egulias/EmailValidator) [![Coverage Status](https://coveralls.io/repos/egulias/EmailValidator/badge.png?branch=master)](https://coveralls.io/r/egulias/EmailValidator?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/egulias/EmailValidator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/egulias/EmailValidator/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6/small.png)](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6)
=============================
## Suported RFCs ##
This library aims to support:
With the help of
RFC 5321, 5322, 6530, 6531, 6532.
## Requirements ##
* [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)
## Installation ##
![Powered by PhpStorm](https://www.jetbrains.com/phpstorm/documentation/docs/logo_phpstorm.png)
##Installation##
Run the command below to install via Composer
```shell
composer require egulias/email-validator "~2.1"
composer require egulias/email-validator "~1.2"
```
## 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).
##Usage##
A basic example with the RFC validation
```php
<?php
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
$validator = new EmailValidator();
$validator->isValid("example@example.com", new RFCValidation()); //true
```
### Available validations ###
1. [RFCValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/RFCValidation.php)
2. [NoRFCWarningsValidation](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. [MultipleValidationWithAnd](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/MultipleValidationWithAnd.php)
6. [Your own validation](#how-to-extend)
`MultipleValidationWithAnd`
It is a validation that operates over other validations performing a logical and (&&) over the result of each validation.
Simple example:
```php
<?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 MultipleValidationWithAnd([
new RFCValidation(),
new DNSCheckValidation()
]);
$validator->isValid("example@example.com", $multipleValidations); //true
$validator = new EmailValidator;
if ($validator->isValid($email)) {
echo $email . ' is a valid email address';
}
```
### How to extend ###
More advanced example (returns detailed diagnostic error codes):
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.
```php
<?php
use Egulias\EmailValidator\EmailValidator;
## Other Contributors ##
(You can find current contributors [here](https://github.com/egulias/EmailValidator/graphs/contributors))
$validator = new EmailValidator;
$email = 'dominic@sayers.cc';
$result = $validator->isValid($email);
As this is a port from another library and work, here are other people related to the previous one:
if ($result) {
echo $email . ' is a valid email address';
} else if ($validator->hasWarnings()) {
echo 'Warning! ' . $email . ' has unusual/deprecated features (result code ' . var_export($validator->getWarnings(), true) . ')';
} else {
echo $email . ' is not a valid email address (result code ' . $validator->getError() . ')';
}
```
* 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
##Contributors##
As this is a port from another library and work, here are other people related to the previous:
## License ##
* 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
##License##
Released under the MIT License attached with this code.
@@ -1,33 +0,0 @@
<?php
namespace Egulias\Tests\EmailValidator;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
use PHPUnit\Framework\TestCase;
class EmailValidatorTest extends TestCase
{
public function testValidationIsUsed()
{
$validator = new EmailValidator();
$validation = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->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->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->getMock();
$validation->expects($this->once())->method("isValid")->willReturn(true);
$validation->expects($this->once())->method("getWarnings")->willReturn([]);
$validation->expects($this->once())->method("getError")->willReturn(null);
$multiple = new MultipleValidationWithAnd([$validation]);
$this->assertTrue($validator->isValid("example@example.com", $multiple));
}
}
@@ -1,63 +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;
use PHPUnit\Framework\TestCase;
class DNSCheckValidationTest extends TestCase
{
public function validEmailsProvider()
{
return [
// dot-atom
['Abc@example.com'],
['ABC@EXAMPLE.COM'],
['Abc.123@example.com'],
['user+mailbox/department=shipping@example.com'],
['!#$%&\'*+-/=?^_`.{|}~@example.com'],
// quoted string
['"Abc@def"@example.com'],
['"Fred\ Bloggs"@example.com'],
['"Joe.\\Blow"@example.com'],
// unicide
['ñandu.cl'],
];
}
/**
* @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()));
}
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,44 +0,0 @@
<?php
namespace Egulias\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 TestCase
{
/**
* @dataProvider isEmailTestSuite
*/
public function testAgainstIsEmailTestSuite($email)
{
$validator = new EmailValidator();
$validations = new MultipleValidationWithAnd([
new NoRFCWarningsValidation(),
new DNSCheckValidation()
]);
$this->assertFalse($validator->isValid($email, $validations), "Tested email " . $email);
}
public function isEmailTestSuite()
{
$testSuite = dirname(__FILE__) . '/../../resources/is_email_tests.xml';
$document = new \DOMDocument();
$document->load($testSuite);
$elements = $document->getElementsByTagName('test');
$tests = [];
foreach($elements as $testElement) {
$childNode = $testElement->childNodes;
$tests[][] = ($childNode->item(1)->getAttribute('value'));
}
return $tests;
}
}
@@ -1,159 +0,0 @@
<?php
namespace Egulias\Tests\EmailValidator\Validation;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Exception\CommaInDomain;
use Egulias\EmailValidator\Exception\NoDomainPart;
use Egulias\EmailValidator\Validation\MultipleErrors;
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
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 = $this->getMockBuilder("Egulias\\EmailValidator\\EmailLexer")->getMock();
$validationTrue = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->getMock();
$validationTrue->expects($this->any())->method("isValid")->willReturn(true);
$validationTrue->expects($this->any())->method("getWarnings")->willReturn([]);
$validationFalse = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->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 = $this->getMockBuilder("Egulias\\EmailValidator\\EmailLexer")->getMock();
$validation = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->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 = $this->getMockBuilder("Egulias\\EmailValidator\\EmailLexer")->getMock();
$validation1 = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->getMock();
$validation1->expects($this->any())->method("isValid")->willReturn(true);
$validation1->expects($this->once())->method("getWarnings")->willReturn($warnings1);
$validation2 = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->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 = $this->getMockBuilder("Egulias\\EmailValidator\\EmailLexer")->getMock();
$validation1 = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->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("Egulias\\EmailValidator\\Validation\\EmailValidation")->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 = $this->getMockBuilder("Egulias\\EmailValidator\\EmailLexer")->getMock();
$validation1 = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->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("Egulias\\EmailValidator\\Validation\\EmailValidation")->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 = $this->getMockBuilder("Egulias\\EmailValidator\\EmailLexer")->getMock();
$validation1 = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->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("Egulias\\EmailValidator\\Validation\\EmailValidation")->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 = $this->getMockBuilder("Egulias\\EmailValidator\\EmailLexer")->getMock();
$validationNotCalled = $this->getMockBuilder("Egulias\\EmailValidator\\Validation\\EmailValidation")->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));
}
}
@@ -1,36 +0,0 @@
<?php
namespace Egulias\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());
}
}
@@ -1,285 +0,0 @@
<?php
namespace Egulias\Tests\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Validation\RFCValidation;
use Egulias\EmailValidator\Exception\AtextAfterCFWS;
use Egulias\EmailValidator\Exception\ConsecutiveAt;
use Egulias\EmailValidator\Exception\ConsecutiveDot;
use Egulias\EmailValidator\Exception\CRNoLF;
use Egulias\EmailValidator\Exception\DomainHyphened;
use Egulias\EmailValidator\Exception\DotAtEnd;
use Egulias\EmailValidator\Exception\DotAtStart;
use Egulias\EmailValidator\Exception\ExpectingATEXT;
use Egulias\EmailValidator\Exception\ExpectingDTEXT;
use Egulias\EmailValidator\Exception\NoDomainPart;
use Egulias\EmailValidator\Exception\NoLocalPart;
use Egulias\EmailValidator\Exception\UnclosedComment;
use Egulias\EmailValidator\Exception\UnclosedQuotedString;
use Egulias\EmailValidator\Exception\UnopenedComment;
use Egulias\EmailValidator\Warning\AddressLiteral;
use Egulias\EmailValidator\Warning\CFWSNearAt;
use Egulias\EmailValidator\Warning\CFWSWithFWS;
use Egulias\EmailValidator\Warning\Comment;
use Egulias\EmailValidator\Warning\DomainLiteral;
use Egulias\EmailValidator\Warning\DomainTooLong;
use Egulias\EmailValidator\Warning\IPV6BadChar;
use Egulias\EmailValidator\Warning\IPV6ColonEnd;
use Egulias\EmailValidator\Warning\IPV6ColonStart;
use Egulias\EmailValidator\Warning\IPV6Deprecated;
use Egulias\EmailValidator\Warning\IPV6DoubleColon;
use Egulias\EmailValidator\Warning\IPV6GroupCount;
use Egulias\EmailValidator\Warning\IPV6MaxGroups;
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 TestCase
{
/**
* @var RFCValidation
*/
protected $validator;
/**
* @var EmailLexer
*/
protected $lexer;
protected function setUp()
{
$this->validator = new RFCValidation();
$this->lexer = new EmailLexer();
}
protected function tearDown()
{
$this->validator = null;
}
/**
* @dataProvider getValidEmails
*/
public function testValidEmails($email)
{
$this->assertTrue($this->validator->isValid($email, $this->lexer));
}
public function getValidEmails()
{
return array(
['â@iana.org'],
['fabien@symfony.com'],
['example@example.co.uk'],
['fabien_potencier@example.fr'],
['example@localhost'],
['fab\'ien@symfony.com'],
['fab\ ien@symfony.com'],
['example((example))@fakedfake.co.uk'],
['example@faked(fake).co.uk'],
['fabien+@symfony.com'],
['инфо@письмо.рф'],
['"username"@example.com'],
['"user,name"@example.com'],
['"user name"@example.com'],
['"user@name"@example.com'],
['"user\"name"@example.com'],
['"\a"@iana.org'],
['"test\ test"@iana.org'],
['""@iana.org'],
['"\""@iana.org'],
['müller@möller.de'],
['test@email*'],
['test@email!'],
['test@email&'],
['test@email^'],
['test@email%'],
['test@email$'],
["1500111@профи-инвест.рф"],
);
}
public function testInvalidUTF8Email()
{
$email = "\x80\x81\x82@\x83\x84\x85.\x86\x87\x88";
$this->assertFalse($this->validator->isValid($email, $this->lexer));
}
/**
* @dataProvider getInvalidEmails
*/
public function testInvalidEmails($email)
{
$this->assertFalse($this->validator->isValid($email, $this->lexer));
}
public function getInvalidEmails()
{
return [
['test@example.com test'],
['user name@example.com'],
['user name@example.com'],
['example.@example.co.uk'],
['example@example@example.co.uk'],
['(test_exampel@example.fr]'],
['example(example]example@example.co.uk'],
['.example@localhost'],
['ex\ample@localhost'],
['example@local\host'],
['example@localhost\\'],
['example@localhost.'],
['user name@example.com'],
['username@ example . com'],
['example@(fake].com'],
['example@(fake.com'],
['username@example,com'],
['usern,ame@example.com'],
['user[na]me@example.com'],
['"""@iana.org'],
['"\"@iana.org'],
['"test"test@iana.org'],
['"test""test"@iana.org'],
['"test"."test"@iana.org'],
['"test".test@iana.org'],
['"test"' . chr(0) . '@iana.org'],
['"test\"@iana.org'],
[chr(226) . '@iana.org'],
['test@' . chr(226) . '.org'],
['\r\ntest@iana.org'],
['\r\n test@iana.org'],
['\r\n \r\ntest@iana.org'],
['\r\n \r\ntest@iana.org'],
['\r\n \r\n test@iana.org'],
['test@iana.org \r\n'],
['test@iana.org \r\n '],
['test@iana.org \r\n \r\n'],
['test@iana.org \r\n\r\n'],
['test@iana.org \r\n\r\n '],
['test@iana/icann.org'],
['test@foo;bar.com'],
['test;123@foobar.com'],
['test@example..com'],
['email.email@email."'],
['test@email>'],
['test@email<'],
['test@email{'],
];
}
/**
* @dataProvider getInvalidEmailsWithErrors
*/
public function testInvalidEmailsWithErrorsCheck($error, $email)
{
$this->assertFalse($this->validator->isValid($email, $this->lexer));
$this->assertEquals($error, $this->validator->getError());
}
public function getInvalidEmailsWithErrors()
{
return [
[new NoLocalPart(), '@example.co.uk'],
[new NoDomainPart(), 'example@'],
[new DomainHyphened(), 'example@example-.co.uk'],
[new DomainHyphened(), 'example@example-'],
[new ConsecutiveAt(), 'example@@example.co.uk'],
[new ConsecutiveDot(), 'example..example@example.co.uk'],
[new ConsecutiveDot(), 'example@example..co.uk'],
[new ExpectingATEXT(), '<example_example>@example.fr'],
[new DotAtStart(), '.example@localhost'],
[new DotAtStart(), 'example@.localhost'],
[new DotAtEnd(), 'example@localhost.'],
[new DotAtEnd(), 'example.@example.co.uk'],
[new UnclosedComment(), '(example@localhost'],
[new UnclosedQuotedString(), '"example@localhost'],
[new ExpectingATEXT(), 'exa"mple@localhost'],
[new UnclosedComment(), '(example@localhost'],
[new UnopenedComment(), 'comment)example@localhost'],
[new UnopenedComment(), 'example(comment))@localhost'],
[new UnopenedComment(), 'example@comment)localhost'],
[new UnopenedComment(), 'example@localhost(comment))'],
[new UnopenedComment(), 'example@(comment))example.com'],
//This was the original. But atext is not allowed after \n
//array(EmailValidator::ERR_EXPECTING_ATEXT, "exampl\ne@example.co.uk"),
[new AtextAfterCFWS(), "exampl\ne@example.co.uk"],
[new ExpectingDTEXT(), "example@[[]"],
[new AtextAfterCFWS(), "exampl\te@example.co.uk"],
[new CRNoLF(), "example@exa\rmple.co.uk"],
[new CRNoLF(), "example@[\r]"],
[new CRNoLF(), "exam\rple@example.co.uk"],
];
}
/**
* @dataProvider getInvalidEmailsWithWarnings
*/
public function testInvalidEmailsWithWarningsCheck($expectedWarnings, $email)
{
$this->assertTrue($this->validator->isValid($email, $this->lexer));
$warnings = $this->validator->getWarnings();
$this->assertCount(
count($warnings), $expectedWarnings,
"Expected: " . implode(",", $expectedWarnings) . " and got " . implode(",", $warnings)
);
foreach ($warnings as $warning) {
$this->assertArrayHasKey($warning->code(), $expectedWarnings);
}
}
public function getInvalidEmailsWithWarnings()
{
return [
[[CFWSNearAt::CODE], 'example @invalid.example.com'],
[[CFWSNearAt::CODE], 'example@ invalid.example.com'],
[[Comment::CODE], 'example@invalid.example(examplecomment).com'],
[[Comment::CODE,CFWSNearAt::CODE], 'example(examplecomment)@invalid.example.com'],
[[QuotedString::CODE, CFWSWithFWS::CODE,], "\"\t\"@invalid.example.com"],
[[QuotedString::CODE, CFWSWithFWS::CODE,], "\"\r\"@invalid.example.com"],
[[AddressLiteral::CODE,], 'example@[127.0.0.1]'],
[[AddressLiteral::CODE,], 'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]'],
[[AddressLiteral::CODE, IPV6Deprecated::CODE], 'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370::]'],
[[AddressLiteral::CODE, IPV6MaxGroups::CODE,], 'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334::]'],
[[AddressLiteral::CODE, IPV6DoubleColon::CODE,], 'example@[IPv6:1::1::1]'],
[[ObsoleteDTEXT::CODE, DomainLiteral::CODE,], "example@[\n]"],
[[DomainLiteral::CODE,], 'example@[::1]'],
[[DomainLiteral::CODE,], 'example@[::123.45.67.178]'],
[
[IPV6ColonStart::CODE, AddressLiteral::CODE, IPV6GroupCount::CODE,],
'example@[IPv6::2001:0db8:85a3:0000:0000:8a2e:0370:7334]'
],
[
[AddressLiteral::CODE, IPV6BadChar::CODE,],
'example@[IPv6:z001:0db8:85a3:0000:0000:8a2e:0370:7334]'
],
[
[AddressLiteral::CODE, IPV6ColonEnd::CODE,],
'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:]'
],
[[QuotedString::CODE,], '"example"@invalid.example.com'],
[
[LocalTooLong::CODE,],
'too_long_localpart_too_long_localpart_too_long_localpart_too_long_localpart@invalid.example.com'
],
[
[LabelTooLong::CODE,],
'example@toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart.co.uk'
],
[
[DomainTooLong::CODE, LabelTooLong::CODE,],
'example2@toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocal'.
'parttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'.
'toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'
],
[
[DomainTooLong::CODE, LabelTooLong::CODE,],
'example@toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocal'.
'parttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'.
'toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpar'
],
];
}
}
@@ -1,42 +0,0 @@
<?php
namespace Egulias\Tests\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Validation\SpoofCheckValidation;
use PHPUnit\Framework\TestCase;
class SpoofCheckValidationTest extends TestCase
{
/**
* @dataProvider validUTF8EmailsProvider
*/
public function testUTF8EmailAreValid($email)
{
$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 [
// Cyrillic
['Кириллица@Кириллица'],
// Latin + Han + Hiragana + Katakana
["latin漢字"."ひらがな"."カタカナ"."@example.com"],
// Latin + Han + Hangul
["latin"."漢字"."조선말"."@example.com"],
// Latin + Han + Bopomofo
["latin"."漢字"."ㄅㄆㄇㄈ"."@example.com"]
];
}
}
File diff suppressed because it is too large Load Diff
+5 -21
View File
@@ -1,6 +1,6 @@
{
"name": "egulias/email-validator",
"description": "A library for validating emails against several RFCs",
"description": "A library for validating emails",
"homepage": "https://github.com/egulias/EmailValidator",
"type": "Library",
"keywords": ["email", "validation", "validator", "emailvalidation", "emailvalidator"],
@@ -13,32 +13,16 @@
"dev-master": "2.0.x-dev"
}
},
"repositories": [
{
"type": "git",
"url": "https://github.com/dominicsayers/isemail"
}
],
"require": {
"php": ">= 5.5",
"php": ">= 5.3.3",
"doctrine/lexer": "^1.0.1"
},
"require-dev" : {
"satooshi/php-coveralls": "^1.0.1",
"phpunit/phpunit": "^4.8.35||^5.7||^6.0",
"dominicsayers/isemail": "dev-master"
},
"suggest": {
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
"phpunit/phpunit": "^4.8.24"
},
"autoload": {
"psr-4": {
"Egulias\\EmailValidator\\": "EmailValidator"
}
},
"autoload-dev": {
"psr-4": {
"Egulias\\Tests\\": "test"
"psr-0": {
"Egulias\\": "src/"
}
}
}
Generated
+1078
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -1,23 +1,23 @@
Email length
------------
https://tools.ietf.org/html/rfc5321#section-4.1.2
http://tools.ietf.org/html/rfc5321#section-4.1.2
Forward-path = Path
Path = "<" [ A-d-l ":" ] Mailbox ">"
https://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
https://tools.ietf.org/html/rfc1035#section-2.3.4
http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
http://tools.ietf.org/html/rfc1035#section-2.3.4
DNS
---
https://tools.ietf.org/html/rfc5321#section-2.3.5
http://tools.ietf.org/html/rfc5321#section-2.3.5
Names that can
be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
in Section 5) are permitted, as are CNAME RRs whose targets can be
resolved, in turn, to MX or address RRs.
https://tools.ietf.org/html/rfc5321#section-5.1
http://tools.ietf.org/html/rfc5321#section-5.1
The lookup first attempts to locate an MX record associated with the
name. If a CNAME record is found, the resulting name is processed as
if it were the initial name. ... If an empty list of MXs is returned,
@@ -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)
https://tools.ietf.org/html/rfc5321#section-2.3.5
http://tools.ietf.org/html/rfc5321#section-2.3.5
In the case
of a top-level domain used by itself in an email address, a single
string is used without any dots. This makes the requirement,
@@ -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:
https://www.rfc-editor.org/errata_search.php?rfc=1123&eid=1353
http://www.rfc-editor.org/errata_search.php?rfc=1123&eid=1353
However, a valid host name can never have the dotted-decimal
form #.#.#.#, since this change does not permit the highest-level
component label to start with a digit even if it is not all-numeric.
@@ -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
(https://tools.ietf.org/html/rfc5322#section-3.4.1)
(http://tools.ietf.org/html/rfc5322#section-3.4.1)
+2 -2
View File
@@ -9,11 +9,11 @@
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="EmailValidator Test Suite">
<directory>./Tests/EmailValidator</directory>
<directory>./tests/egulias/Tests/</directory>
<exclude>./vendor/</exclude>
</testsuite>
</testsuites>
@@ -2,11 +2,8 @@
namespace Egulias\EmailValidator;
use Egulias\EmailValidator\Exception\ExpectingATEXT;
use Egulias\EmailValidator\Exception\NoLocalPart;
use Egulias\EmailValidator\Parser\DomainPart;
use Egulias\EmailValidator\Parser\LocalPart;
use Egulias\EmailValidator\Warning\EmailTooLong;
/**
* EmailParser
@@ -17,7 +14,7 @@ class EmailParser
{
const EMAIL_MAX_LENGTH = 254;
protected $warnings;
protected $warnings = array();
protected $domainPart = '';
protected $localPart = '';
protected $lexer;
@@ -29,7 +26,6 @@ class EmailParser
$this->lexer = $lexer;
$this->localPartParser = new LocalPart($this->lexer);
$this->domainPartParser = new DomainPart($this->lexer);
$this->warnings = new \SplObjectStorage();
}
/**
@@ -41,7 +37,7 @@ class EmailParser
$this->lexer->setInput($str);
if (!$this->hasAtToken()) {
throw new NoLocalPart();
throw new \InvalidArgumentException('ERR_NOLOCALPART');
}
@@ -51,7 +47,7 @@ class EmailParser
$this->setParts($str);
if ($this->lexer->hasInvalidTokens()) {
throw new ExpectingATEXT();
throw new \InvalidArgumentException('ERR_INVALID_ATEXT');
}
return array('local' => $this->localPart, 'domain' => $this->domainPart);
@@ -61,8 +57,8 @@ class EmailParser
{
$localPartWarnings = $this->localPartParser->getWarnings();
$domainPartWarnings = $this->domainPartParser->getWarnings();
$this->warnings = array_merge($localPartWarnings, $domainPartWarnings);
$this->warnings = array_merge($localPartWarnings, $domainPartWarnings);
$this->addLongEmailWarning($this->localPart, $this->domainPart);
return $this->warnings;
@@ -98,7 +94,7 @@ class EmailParser
protected function addLongEmailWarning($localPart, $parsedDomainPart)
{
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {
$this->warnings[EmailTooLong::CODE] = new EmailTooLong();
$this->warnings[] = EmailValidator::RFC5322_TOOLONG;
}
}
}
@@ -0,0 +1,206 @@
<?php
namespace Egulias\EmailValidator;
/**
* EmailValidator
*
* @author Eduardo Gulias Davis <me@egulias.com>
*/
class EmailValidator implements EmailValidatorInterface
{
/**
* Critical validation errors used to indicate that
* an email address is invalid:
*/
const ERR_CONSECUTIVEATS = 128;
const ERR_EXPECTING_DTEXT = 129;
const ERR_NOLOCALPART = 130;
const ERR_NODOMAIN = 131;
const ERR_CONSECUTIVEDOTS = 132;
const ERR_ATEXT_AFTER_CFWS = 133;
const ERR_EXPECTING_QPAIR = 136;
const ERR_EXPECTING_ATEXT = 137;
const ERR_EXPECTING_CTEXT = 139;
const ERR_DOT_START = 141;
const ERR_DOT_END = 142;
const ERR_DOMAINHYPHENEND = 144;
const ERR_UNCLOSEDQUOTEDSTR = 145;
const ERR_UNCLOSEDCOMMENT = 146;
const ERR_FWS_CRLF_X2 = 148;
const ERR_FWS_CRLF_END = 149;
const ERR_CR_NO_LF = 150;
const ERR_DEPREC_REACHED = 151;
const ERR_UNOPENEDCOMMENT = 152;
const ERR_ATEXT_AFTER_QS = 134; // not in use
const ERR_ATEXT_AFTER_DOMLIT = 135; // not in use
const ERR_EXPECTING_QTEXT = 138; // not in use
const ERR_BACKSLASHEND = 140; // not in use
const ERR_DOMAINHYPHENSTART = 143; // not in use
const ERR_UNCLOSEDDOMLIT = 147; // not in use
/**
* Informational validation warnings regarding unusual or
* deprecated features found in an email address:
*/
// Address is valid for SMTP (RFC-5321), but has unusual elements.
const RFC5321_TLD = 9;
const RFC5321_QUOTEDSTRING = 11;
const RFC5321_ADDRESSLITERAL = 12;
const RFC5321_IPV6DEPRECATED = 13;
const RFC5321_TLDNUMERIC = 10; // not in use
// Address is only valid according to the broad
// definition of RFC-5322. It is otherwise invalid.
const RFC5322_LOCAL_TOOLONG = 64;
const RFC5322_LABEL_TOOLONG = 63;
const RFC5322_TOOLONG = 66;
const RFC5322_DOMAIN_TOOLONG = 255;
const RFC5322_DOMAINLITERAL = 70;
const RFC5322_DOMLIT_OBSDTEXT = 71;
const RFC5322_IPV6_GRPCOUNT = 72;
const RFC5322_IPV6_2X2XCOLON = 73;
const RFC5322_IPV6_BADCHAR = 74;
const RFC5322_IPV6_MAXGRPS = 75;
const RFC5322_IPV6_COLONSTRT = 76;
const RFC5322_IPV6_COLONEND = 77;
const RFC5322_DOMAIN = 65; // not in use
// Address contains deprecated elements, but may
// still be valid in restricted contexts.
const DEPREC_QP = 36;
const DEPREC_COMMENT = 37;
const DEPREC_CFWS_NEAR_AT = 49;
const DEPREC_LOCALPART = 33; // not in use
const DEPREC_FWS = 34; // not in use
const DEPREC_QTEXT = 35; // not in use
const DEPREC_CTEXT = 38; // not in use
// Address is valid within the message,
// but cannot be used unmodified in the envelope.
const CFWS_COMMENT = 17;
const CFWS_FWS = 18;
// Hostname DNS checks were unsuccessful.
const DNSWARN_NO_MX_RECORD = 5;
const DNSWARN_NO_RECORD = 6;
/**
* @var EmailParser
*/
protected $parser;
/**
* Contains any informational warnings regarding unusual/deprecated
* features that were encountered during validation.
*
* @var array
*/
protected $warnings = array();
/**
* If a critical validation problem is encountered, this will be
* set to the value of one of this class's ERR_* constants.
*
* @var int
*/
protected $error;
/**
* @var int
*/
protected $threshold = 255;
public function __construct()
{
$this->parser = new EmailParser(new EmailLexer());
}
/**
* {@inheritdoc}
*/
public function isValid($email, $checkDNS = false, $strict = false)
{
try {
$this->parser->parse((string)$email);
$this->warnings = $this->parser->getWarnings();
} catch (\Exception $e) {
$rClass = new \ReflectionClass($this);
$this->error = $rClass->getConstant($e->getMessage());
return false;
}
$dnsProblemExists = ($checkDNS ? !$this->checkDNS() : false);
if ($this->hasWarnings() && ((int) max($this->warnings) > $this->threshold)) {
$this->error = self::ERR_DEPREC_REACHED;
return false;
}
return ($strict ? (!$this->hasWarnings() && !$dnsProblemExists) : true);
}
/**
* {@inheritdoc}
*/
public function hasWarnings()
{
return !empty($this->warnings);
}
/**
* {@inheritdoc}
*/
public function getWarnings()
{
return $this->warnings;
}
/**
* {@inheritdoc}
*/
public function getError()
{
return $this->error;
}
/**
* {@inheritdoc}
*/
public function setThreshold($threshold)
{
$this->threshold = (int) $threshold;
return $this;
}
/**
* {@inheritdoc}
*/
public function getThreshold()
{
return $this->threshold;
}
/**
* @return bool Whether or not an MX record exists for the
* email address's host name.
*/
protected function checkDNS()
{
$mxRecordExists = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX');
if (!$mxRecordExists) {
$this->warnings[] = self::DNSWARN_NO_RECORD;
$this->addTLDWarnings();
}
return $mxRecordExists;
}
protected function addTLDWarnings()
{
if (!in_array(self::DNSWARN_NO_RECORD, $this->warnings) &&
!in_array(self::DNSWARN_NO_MX_RECORD, $this->warnings) &&
in_array(self::RFC5322_DOMAINLITERAL, $this->warnings)
) {
$this->warnings[] = self::RFC5321_TLD;
}
}
}
@@ -0,0 +1,62 @@
<?php
namespace Egulias\EmailValidator;
/**
* EmailValidatorInterface
*
* @author Chris McCafferty <cilefen@gmail.com>
*/
interface EmailValidatorInterface
{
/**
* Validates an email address against the following standards:
*
* RFC-5321: Simple Mail Transfer Protocol
* RFC-5322: Internet Message Format
* RFC-6530: Overview and Framework for Internationalized Email
* RFC-6531: SMTP Extension for Internationalized Email
* RFC-6532: Internationalized Email Headers
* RFC-1123 section 2.1: Requirements for Internet Hosts -- Application and Support
* RFC-4291 section 2.2: IP Version 6 Addressing Architecture
*
* @param string $email The email address to validate.
* @param bool $checkDNS Whether or not the email address's hostname should
* be confirmed with a DNS lookup. This only comes
* into play if strict mode is also enabled.
* @param bool $strict If this is true, and any informational warnings
* were raised during validation, the email address
* will be considered invalid. Additionally, if
* $checkDNS is true and the DNS lookup failed,
* the email address will be considered invalid.
* @return bool
*/
public function isValid($email, $checkDNS = false, $strict = false);
/**
* @return bool
*/
public function hasWarnings();
/**
* @return array
*/
public function getWarnings();
/**
* @return string
*/
public function getError();
/**
* @param int $threshold The acceptable number of deprecation warnings.
*
* @return EmailValidator
*/
public function setThreshold($threshold);
/**
* @return int
*/
public function getThreshold();
}
@@ -1,36 +1,10 @@
<?php
namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Exception\CharNotAllowed;
use Egulias\EmailValidator\Exception\CommaInDomain;
use Egulias\EmailValidator\Exception\ConsecutiveAt;
use Egulias\EmailValidator\Exception\CRLFAtTheEnd;
use Egulias\EmailValidator\Exception\CRNoLF;
use Egulias\EmailValidator\Exception\DomainHyphened;
use Egulias\EmailValidator\Exception\DotAtEnd;
use Egulias\EmailValidator\Exception\DotAtStart;
use Egulias\EmailValidator\Exception\ExpectingATEXT;
use Egulias\EmailValidator\Exception\ExpectingDomainLiteralClose;
use Egulias\EmailValidator\Exception\ExpectingDTEXT;
use Egulias\EmailValidator\Exception\NoDomainPart;
use Egulias\EmailValidator\Exception\UnopenedComment;
use Egulias\EmailValidator\Warning\AddressLiteral;
use Egulias\EmailValidator\Warning\CFWSWithFWS;
use Egulias\EmailValidator\Warning\DeprecatedComment;
use Egulias\EmailValidator\Warning\DomainLiteral;
use Egulias\EmailValidator\Warning\DomainTooLong;
use Egulias\EmailValidator\Warning\IPV6BadChar;
use Egulias\EmailValidator\Warning\IPV6ColonEnd;
use Egulias\EmailValidator\Warning\IPV6ColonStart;
use Egulias\EmailValidator\Warning\IPV6Deprecated;
use Egulias\EmailValidator\Warning\IPV6DoubleColon;
use Egulias\EmailValidator\Warning\IPV6GroupCount;
use Egulias\EmailValidator\Warning\IPV6MaxGroups;
use Egulias\EmailValidator\Warning\LabelTooLong;
use Egulias\EmailValidator\Warning\ObsoleteDTEXT;
use Egulias\EmailValidator\Warning\TLD;
use Egulias\EmailValidator\EmailValidator;
class DomainPart extends Parser
{
@@ -42,18 +16,18 @@ class DomainPart extends Parser
$this->lexer->moveNext();
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
throw new DotAtStart();
throw new \InvalidArgumentException('ERR_DOT_START');
}
if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) {
throw new NoDomainPart();
throw new \InvalidArgumentException('ERR_NODOMAIN');
}
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
throw new DomainHyphened();
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->warnings[DeprecatedComment::CODE] = new DeprecatedComment();
$this->warnings[] = EmailValidator::DEPREC_COMMENT;
$this->parseDomainComments();
}
@@ -63,16 +37,16 @@ class DomainPart extends Parser
$length = strlen($domain);
if ($prev['type'] === EmailLexer::S_DOT) {
throw new DotAtEnd();
throw new \InvalidArgumentException('ERR_DOT_END');
}
if ($prev['type'] === EmailLexer::S_HYPHEN) {
throw new DomainHyphened();
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
}
if ($length > self::DOMAIN_MAX_LENGTH) {
$this->warnings[DomainTooLong::CODE] = new DomainTooLong();
$this->warnings[] = EmailValidator::RFC5322_DOMAIN_TOOLONG;
}
if ($prev['type'] === EmailLexer::S_CR) {
throw new CRLFAtTheEnd();
throw new \InvalidArgumentException('ERR_FWS_CRLF_END');
}
$this->domainPart = $domain;
}
@@ -86,7 +60,7 @@ class DomainPart extends Parser
{
$prev = $this->lexer->getPrevious();
if ($prev['type'] === EmailLexer::S_COLON) {
$this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd();
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONEND;
}
$IPv6 = substr($addressLiteral, 5);
@@ -96,19 +70,19 @@ class DomainPart extends Parser
$colons = strpos($IPv6, '::');
if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
$this->warnings[IPV6BadChar::CODE] = new IPV6BadChar();
$this->warnings[] = EmailValidator::RFC5322_IPV6_BADCHAR;
}
if ($colons === false) {
// We need exactly the right number of groups
if ($groupCount !== $maxGroups) {
$this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount();
$this->warnings[] = EmailValidator::RFC5322_IPV6_GRPCOUNT;
}
return;
}
if ($colons !== strrpos($IPv6, '::')) {
$this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon();
$this->warnings[] = EmailValidator::RFC5322_IPV6_2X2XCOLON;
return;
}
@@ -119,9 +93,9 @@ class DomainPart extends Parser
}
if ($groupCount > $maxGroups) {
$this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups();
$this->warnings[] = EmailValidator::RFC5322_IPV6_MAXGRPS;
} elseif ($groupCount === $maxGroups) {
$this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated();
$this->warnings[] = EmailValidator::RFC5321_IPV6DEPRECATED;
}
}
@@ -129,6 +103,7 @@ class DomainPart extends Parser
{
$domain = '';
$openedParenthesis = 0;
$openBrackets = false;
do {
$prev = $this->lexer->getPrevious();
@@ -145,7 +120,7 @@ class DomainPart extends Parser
}
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
if ($openedParenthesis === 0) {
throw new UnopenedComment();
throw new \InvalidArgumentException('ERR_UNOPENEDCOMMENT');
} else {
$openedParenthesis--;
}
@@ -154,7 +129,7 @@ class DomainPart extends Parser
$this->checkConsecutiveDots();
$this->checkDomainPartExceptions($prev);
if ($this->hasBrackets()) {
if ($openBrackets = $this->hasBrackets($openBrackets)) {
$this->parseDomainLiteral();
}
@@ -173,22 +148,22 @@ class DomainPart extends Parser
private function checkNotAllowedChars($token)
{
$notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true];
$notAllowed = array(EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true);
if (isset($notAllowed[$token['type']])) {
throw new CharNotAllowed();
throw new \InvalidArgumentException('ERR_DOMAIN_CHAR_NOT_ALLOWED');
}
}
protected function parseDomainLiteral()
{
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
$this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
$lexer = clone $this->lexer;
$lexer->moveNext();
if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
$this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
$this->warnings[] = EmailValidator::RFC5322_IPV6_COLONSTRT;
}
}
@@ -201,33 +176,32 @@ class DomainPart extends Parser
$addressLiteral = '';
do {
if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new ExpectingDTEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->token['type'] === EmailLexer::INVALID ||
$this->lexer->token['type'] === EmailLexer::C_DEL ||
$this->lexer->token['type'] === EmailLexer::S_LF
) {
$this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
}
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) {
throw new ExpectingDTEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_DTEXT');
}
if ($this->lexer->isNextTokenAny(
array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
)) {
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
$this->warnings[] = EmailValidator::CFWS_FWS;
$this->parseFWS();
}
if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
throw new CRNoLF();
throw new \InvalidArgumentException('ERR_CR_NO_LF');
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
$this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
$this->warnings[] = EmailValidator::RFC5322_DOMLIT_OBSDTEXT;
$addressLiteral .= $this->lexer->token['value'];
$this->lexer->moveNext();
$this->validateQuotedPair();
@@ -251,11 +225,11 @@ class DomainPart extends Parser
}
if (!$IPv6TAG) {
$this->warnings[DomainLiteral::CODE] = new DomainLiteral();
$this->warnings[] = EmailValidator::RFC5322_DOMAINLITERAL;
return $addressLiteral;
}
$this->warnings[AddressLiteral::CODE] = new AddressLiteral();
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
$this->checkIPV6Tag($addressLiteral);
@@ -268,14 +242,14 @@ class DomainPart extends Parser
// Extract IPv4 part from the end of the address-literal (if there is one)
if (preg_match(
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
$addressLiteral,
$matchesIP
) > 0
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
$addressLiteral,
$matchesIP
) > 0
) {
$index = strrpos($addressLiteral, $matchesIP[0]);
if ($index === 0) {
$this->warnings[AddressLiteral::CODE] = new AddressLiteral();
$this->warnings[] = EmailValidator::RFC5321_ADDRESSLITERAL;
return false;
}
// Convert IPv4 part to IPv6 format for further testing
@@ -295,33 +269,37 @@ class DomainPart extends Parser
);
if (isset($invalidDomainTokens[$this->lexer->token['type']])) {
throw new ExpectingATEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->token['type'] === EmailLexer::S_COMMA) {
throw new CommaInDomain();
throw new \InvalidArgumentException('ERR_COMMA_IN_DOMAIN');
}
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
throw new ConsecutiveAt();
throw new \InvalidArgumentException('ERR_CONSECUTIVEATS');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) {
throw new ExpectingATEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new DomainHyphened();
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
&& $this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new ExpectingATEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
}
protected function hasBrackets()
protected function hasBrackets($openBrackets)
{
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEBRACKET && !$openBrackets) {
throw new \InvalidArgumentException('ERR_EXPECTING_OPENBRACKET');
}
if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) {
return false;
}
@@ -329,7 +307,7 @@ class DomainPart extends Parser
try {
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
} catch (\RuntimeException $e) {
throw new ExpectingDomainLiteralClose();
throw new \InvalidArgumentException('ERR_EXPECTING_DOMLIT_CLOSE');
}
return true;
@@ -341,7 +319,7 @@ class DomainPart extends Parser
$prev['type'] === EmailLexer::GENERIC &&
strlen($prev['value']) > 63
) {
$this->warnings[LabelTooLong::CODE] = new LabelTooLong();
$this->warnings[] = EmailValidator::RFC5322_LABEL_TOOLONG;
}
}
@@ -355,14 +333,7 @@ class DomainPart extends Parser
$this->lexer->moveNext();
if ($this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new ExpectingATEXT();
}
}
protected function addTLDWarnings()
{
if ($this->warnings[DomainLiteral::CODE]) {
$this->warnings[TLD::CODE] = new TLD();
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
}
}
@@ -2,16 +2,8 @@
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;
use Egulias\EmailValidator\Exception\UnopenedComment;
use Egulias\EmailValidator\Warning\CFWSWithFWS;
use Egulias\EmailValidator\Warning\LocalTooLong;
class LocalPart extends Parser
{
@@ -23,7 +15,7 @@ class LocalPart extends Parser
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) {
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) {
throw new DotAtStart();
throw new \InvalidArgumentException('ERR_DOT_START');
}
$closingQuote = $this->checkDQUOTE($closingQuote);
@@ -37,7 +29,7 @@ class LocalPart extends Parser
}
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
if ($openedParenthesis === 0) {
throw new UnopenedComment();
throw new \InvalidArgumentException('ERR_UNOPENEDCOMMENT');
} else {
$openedParenthesis--;
}
@@ -48,7 +40,7 @@ class LocalPart extends Parser
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
$this->lexer->isNextToken(EmailLexer::S_AT)
) {
throw new DotAtEnd();
throw new \InvalidArgumentException('ERR_DOT_END');
}
$this->warnEscaping();
@@ -62,8 +54,8 @@ class LocalPart extends Parser
}
$prev = $this->lexer->getPrevious();
if (strlen($prev['value']) > LocalTooLong::LOCAL_PART_LENGTH) {
$this->warnings[LocalTooLong::CODE] = new LocalTooLong();
if (strlen($prev['value']) > EmailValidator::RFC5322_LOCAL_TOOLONG) {
$this->warnings[] = EmailValidator::RFC5322_LOCAL_TOOLONG;
}
}
@@ -89,17 +81,14 @@ class LocalPart extends Parser
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) {
$parseAgain = false;
if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
$this->warnings[] = EmailValidator::CFWS_FWS;
$setSpecialsWarning = false;
}
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) {
$this->lexer->moveNext();
}
$this->lexer->moveNext();
if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
throw new ExpectingATEXT();
throw new \InvalidArgumentException('ERR_EXPECTED_ATEXT');
}
}
@@ -107,12 +96,12 @@ class LocalPart extends Parser
if ($prev['type'] === EmailLexer::S_BACKSLASH) {
if (!$this->checkDQUOTE(false)) {
throw new UnclosedQuotedString();
throw new \InvalidArgumentException('ERR_UNCLOSED_DQUOTE');
}
}
if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
throw new ExpectingAT();
throw new \InvalidArgumentException('ERR_EXPECED_AT');
}
return $parseAgain;
@@ -132,7 +121,7 @@ class LocalPart extends Parser
);
if (in_array($token['type'], $forbidden) && !$closingQuote) {
throw new ExpectingATEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
}
}
@@ -3,25 +3,11 @@
namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Exception\AtextAfterCFWS;
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\ExpectingATEXT;
use Egulias\EmailValidator\Exception\ExpectingCTEXT;
use Egulias\EmailValidator\Exception\UnclosedComment;
use Egulias\EmailValidator\Exception\UnclosedQuotedString;
use Egulias\EmailValidator\Warning\CFWSNearAt;
use Egulias\EmailValidator\Warning\CFWSWithFWS;
use Egulias\EmailValidator\Warning\Comment;
use Egulias\EmailValidator\Warning\QuotedPart;
use Egulias\EmailValidator\Warning\QuotedString;
use Egulias\EmailValidator\EmailValidator;
abstract class Parser
{
protected $warnings = [];
protected $warnings = array();
protected $lexer;
protected $openedParenthesis = 0;
@@ -50,18 +36,17 @@ abstract class Parser
{
if (!($this->lexer->token['type'] === EmailLexer::INVALID
|| $this->lexer->token['type'] === EmailLexer::C_DEL)) {
throw new ExpectedQPair();
throw new \InvalidArgumentException('ERR_EXPECTING_QPAIR');
}
$this->warnings[QuotedPart::CODE] =
new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
$this->warnings[] = EmailValidator::DEPREC_QP;
}
protected function parseComments()
{
$this->openedParenthesis = 1;
$this->isUnclosedComment();
$this->warnings[Comment::CODE] = new Comment();
$this->warnings[] = EmailValidator::CFWS_COMMENT;
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
$this->openedParenthesis++;
@@ -72,11 +57,11 @@ abstract class Parser
$this->lexer->moveNext();
if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) {
throw new ExpectingATEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
$this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
}
}
@@ -86,7 +71,7 @@ abstract class Parser
$this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
return true;
} catch (\RuntimeException $e) {
throw new UnclosedComment();
throw new \InvalidArgumentException('ERR_UNCLOSEDCOMMENT');
}
}
@@ -97,28 +82,28 @@ abstract class Parser
$this->checkCRLFInFWS();
if ($this->lexer->token['type'] === EmailLexer::S_CR) {
throw new CRNoLF();
throw new \InvalidArgumentException('ERR_CR_NO_LF');
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
throw new AtextAfterCFWS();
throw new \InvalidArgumentException('ERR_ATEXT_AFTER_CFWS');
}
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
throw new ExpectingCTEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_CTEXT');
}
if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
$this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
$this->warnings[] = EmailValidator::DEPREC_CFWS_NEAR_AT;
} else {
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
$this->warnings[] = EmailValidator::CFWS_FWS;
}
}
protected function checkConsecutiveDots()
{
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
throw new ConsecutiveDot();
throw new \InvalidArgumentException('ERR_CONSECUTIVEDOTS');
}
}
@@ -161,15 +146,14 @@ abstract class Parser
}
if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new ExpectingATEXT();
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
return false;
}
$this->warnings[QuotedPart::CODE] =
new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
$this->warnings[] = EmailValidator::DEPREC_QP;
return true;
}
@@ -183,17 +167,17 @@ abstract class Parser
return $hasClosingQuote;
}
$previous = $this->lexer->getPrevious();
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
throw new ExpectingATEXT();
if ($previous['type'] === EmailLexer::GENERIC && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
$this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING;
try {
$this->lexer->find(EmailLexer::S_DQUOTE);
$hasClosingQuote = true;
} catch (\Exception $e) {
throw new UnclosedQuotedString();
throw new \InvalidArgumentException('ERR_UNCLOSEDQUOTEDSTR');
}
$this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']);
return $hasClosingQuote;
}
@@ -203,13 +187,11 @@ abstract class Parser
if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
return;
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
throw new CRLFX2();
if ($this->lexer->isNextToken(EmailLexer::CRLF)) {
throw new \InvalidArgumentException('ERR_FWS_CRLF_X2');
}
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
throw new CRLFAtTheEnd();
throw new \InvalidArgumentException('ERR_FWS_CRLF_END');
}
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
if ( ! is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
echo 'Could not find "vendor/autoload.php". Did you forget to run "composer install --dev"?'.PHP_EOL;
exit(1);
}
require_once $autoloadFile;
@@ -2,11 +2,11 @@
use Egulias\EmailValidator\EmailValidator;
require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../bootstrap.php';
$iterations = 10000;
$testingMail = 'example@example.com';
$testingMail = 'fabien@symfony.com';
echo 'Testing ' . $iterations . ' iterations with ' . $testingMail . PHP_EOL;
$a = microtime(true);
@@ -19,7 +19,7 @@ echo ($b - $a) . ' seconds with filter_var' . PHP_EOL;
$a = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$validator = new EmailValidator();
$isValid = $validator->isValid($testingMail, new \Egulias\EmailValidator\Validation\RFCValidation());
$isValid = $validator->isValid($testingMail);
}
$b = microtime(true);
echo ($b - $a) . ' seconds with EmailValidator + instantiation' . PHP_EOL;
@@ -27,7 +27,7 @@ echo ($b - $a) . ' seconds with EmailValidator + instantiation' . PHP_EOL;
$a = microtime(true);
$validator = new EmailValidator();
for ($i = 0; $i < $iterations; $i++) {
$isValid = $validator->isValid($testingMail, new \Egulias\EmailValidator\Validation\RFCValidation());
$isValid = $validator->isValid($testingMail);
}
$b = microtime(true);
echo ($b - $a) . ' seconds with EmailValidator once instanced' . PHP_EOL;
@@ -2,11 +2,12 @@
use Egulias\EmailValidator\EmailValidator;
require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../bootstrap.php';
require __DIR__ . '/../../../../isemail/is_email.php';
$iterations = 10000;
$testingMail = 'example@example.com';
$testingMail = 'fabien@symfony.com';
echo 'Testing ' . $iterations . ' iterations with ' . $testingMail . PHP_EOL;
$a = microtime(true);
@@ -19,7 +20,7 @@ echo ($b - $a) . ' seconds with is_email' . PHP_EOL;
$a = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$validator = new EmailValidator();
$isValid = $validator->isValid($testingMail, new \Egulias\EmailValidator\Validation\RFCValidation());
$isValid = $validator->isValid($testingMail);
}
$b = microtime(true);
echo ($b - $a) . ' seconds with EmailValidator + instantiation' . PHP_EOL;
@@ -27,7 +28,7 @@ echo ($b - $a) . ' seconds with EmailValidator + instantiation' . PHP_EOL;
$a = microtime(true);
$validator = new EmailValidator();
for ($i = 0; $i < $iterations; $i++) {
$isValid = $validator->isValid($testingMail, new \Egulias\EmailValidator\Validation\RFCValidation());
$isValid = $validator->isValid($testingMail);
}
$b = microtime(true);
echo ($b - $a) . ' seconds with EmailValidator once instanced' . PHP_EOL;
@@ -1,11 +1,10 @@
<?php
namespace Egulias\Tests\EmailValidator;
namespace Egulias\EmailValidator\Tests;
use Egulias\EmailValidator\EmailLexer;
use PHPUnit\Framework\TestCase;
class EmailLexerTests extends TestCase
class EmailLexerTests extends \PHPUnit_Framework_TestCase
{
public function testLexerExtendsLib()
@@ -0,0 +1,385 @@
<?php
namespace Egulias\Tests\EmailValidator;
use Egulias\EmailValidator\EmailValidator;
class EmailValidatorTest extends \PHPUnit_Framework_TestCase
{
protected $validator;
protected function setUp()
{
$this->validator = new EmailValidator();
}
protected function tearDown()
{
$this->validator = null;
}
/**
* @dataProvider getValidEmails
*/
public function testValidEmails($email)
{
$this->assertTrue($this->validator->isValid($email));
}
public function testInvalidUTF8Email()
{
$validator = new EmailValidator;
$email = "\x80\x81\x82@\x83\x84\x85.\x86\x87\x88";
$this->assertFalse($validator->isValid($email));
}
public function getValidEmails()
{
return array(
array('â@iana.org'),
array('fabien@symfony.com'),
array('example@example.co.uk'),
array('fabien_potencier@example.fr'),
array('example@localhost'),
array('fab\'ien@symfony.com'),
array('fab\ ien@symfony.com'),
array('example((example))@fakedfake.co.uk'),
array('example@faked(fake).co.uk'),
array('fabien+@symfony.com'),
array('инфо@письмо.рф'),
array('"username"@example.com'),
array('"user,name"@example.com'),
array('"user name"@example.com'),
array('"user@name"@example.com'),
array('"\a"@iana.org'),
array('"test\ test"@iana.org'),
array('""@iana.org'),
array('"\""@iana.org'),
array('müller@möller.de'),
array('test@email*'),
array('test@email!'),
array('test@email&'),
array('test@email^'),
array('test@email%'),
array('test@email$'),
array('test@email.com.au'),
array('1500111@профи-инвест.рф'),
);
}
/**
* @dataProvider getInvalidEmails
*/
public function testInvalidEmails($email)
{
$this->assertFalse($this->validator->isValid($email));
}
public function getInvalidEmails()
{
return array(
array('test@example.com test'),
array('user name@example.com'),
array('user name@example.com'),
array('example.@example.co.uk'),
array('example@example@example.co.uk'),
array('(test_exampel@example.fr)'),
array('example(example)example@example.co.uk'),
array('.example@localhost'),
array('ex\ample@localhost'),
array('example@local\host'),
array('example@localhost\\'),
array('example@localhost.'),
array('user name@example.com'),
array('username@ example . com'),
array('example@(fake).com'),
array('example@(fake.com'),
array('username@example,com'),
array('usern,ame@example.com'),
array('user[na]me@example.com'),
array('"""@iana.org'),
array('"\"@iana.org'),
array('"test"test@iana.org'),
array('"test""test"@iana.org'),
array('"test"."test"@iana.org'),
array('"test".test@iana.org'),
array('"test"' . chr(0) . '@iana.org'),
array('"test\"@iana.org'),
array(chr(226) . '@iana.org'),
array('test@' . chr(226) . '.org'),
array('\r\ntest@iana.org'),
array('\r\n test@iana.org'),
array('\r\n \r\ntest@iana.org'),
array('\r\n \r\ntest@iana.org'),
array('\r\n \r\n test@iana.org'),
array('test@iana.org \r\n'),
array('test@iana.org \r\n '),
array('test@iana.org \r\n \r\n'),
array('test@iana.org \r\n\r\n'),
array('test@iana.org \r\n\r\n '),
array('test@iana/icann.org'),
array('test@foo;bar.com'),
array('test;123@foobar.com'),
array('test@example..com'),
array('email.email@email."'),
array('test@email>'),
array('test@email<'),
array('test@email{'),
array('test@email.com]'),
array('test@ema[il.com'),
);
}
/**
* @dataProvider getInvalidEmailsWithErrors
*/
public function testInvalidEmailsWithErrorsCheck($errors, $email)
{
$this->assertFalse($this->validator->isValid($email));
$this->assertEquals($errors, $this->validator->getError());
}
public function getInvalidEmailsWithErrors()
{
return array(
array(EmailValidator::ERR_NOLOCALPART, '@example.co.uk'),
array(EmailValidator::ERR_NODOMAIN, 'example@'),
array(EmailValidator::ERR_DOMAINHYPHENEND, 'example@example-.co.uk'),
array(EmailValidator::ERR_DOMAINHYPHENEND, 'example@example-'),
array(EmailValidator::ERR_CONSECUTIVEATS, 'example@@example.co.uk'),
array(EmailValidator::ERR_CONSECUTIVEDOTS, 'example..example@example.co.uk'),
array(EmailValidator::ERR_CONSECUTIVEDOTS, 'example@example..co.uk'),
array(EmailValidator::ERR_EXPECTING_ATEXT, '<fabien_potencier>@example.fr'),
array(EmailValidator::ERR_DOT_START, '.example@localhost'),
array(EmailValidator::ERR_DOT_START, 'example@.localhost'),
array(EmailValidator::ERR_DOT_END, 'example@localhost.'),
array(EmailValidator::ERR_DOT_END, 'example.@example.co.uk'),
array(EmailValidator::ERR_UNCLOSEDCOMMENT, '(example@localhost'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'comment)example@localhost'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example(comment))@localhost'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@comment)localhost'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@localhost(comment))'),
array(EmailValidator::ERR_UNOPENEDCOMMENT, 'example@(comment))example.com'),
array(EmailValidator::ERR_UNCLOSEDQUOTEDSTR, '"example@localhost'),
array(EmailValidator::ERR_EXPECTING_ATEXT, 'exa"mple@localhost'),
//This was the original. But atext is not allowed after \n
//array(EmailValidator::ERR_EXPECTING_ATEXT, "exampl\ne@example.co.uk"),
array(EmailValidator::ERR_ATEXT_AFTER_CFWS, "exampl\ne@example.co.uk"),
array(EmailValidator::ERR_EXPECTING_DTEXT, "example@[[]"),
array(EmailValidator::ERR_ATEXT_AFTER_CFWS, "exampl\te@example.co.uk"),
array(EmailValidator::ERR_CR_NO_LF, "example@exa\rmple.co.uk"),
array(EmailValidator::ERR_CR_NO_LF, "example@[\r]"),
array(EmailValidator::ERR_CR_NO_LF, "exam\rple@example.co.uk"),
);
}
/**
* @dataProvider getInvalidEmailsWithWarnings
*/
public function testValidEmailsWithWarningsCheck($warnings, $email)
{
$this->assertTrue($this->validator->isValid($email, true));
$this->assertEquals($warnings, $this->validator->getWarnings());
}
/**
* @dataProvider getInvalidEmailsWithWarnings
*/
public function testInvalidEmailsWithDnsCheckAndStrictMode($warnings, $email)
{
$this->assertFalse($this->validator->isValid($email, true, true));
$this->assertEquals($warnings, $this->validator->getWarnings());
}
public function getInvalidEmailsWithWarnings()
{
return array(
array(
array(
EmailValidator::DEPREC_CFWS_NEAR_AT,
EmailValidator::DNSWARN_NO_RECORD
),
'example @invalid.example.com'
),
array(
array(
EmailValidator::DEPREC_CFWS_NEAR_AT,
EmailValidator::DNSWARN_NO_RECORD
),
'example@ invalid.example.com'
),
array(
array(
EmailValidator::CFWS_COMMENT,
EmailValidator::DNSWARN_NO_RECORD
),
'example@invalid.example(examplecomment).com'
),
array(
array(
EmailValidator::CFWS_COMMENT,
EmailValidator::DEPREC_CFWS_NEAR_AT,
EmailValidator::DNSWARN_NO_RECORD,
),
'example(examplecomment)@invalid.example.com'
),
array(
array(
EmailValidator::RFC5321_QUOTEDSTRING,
EmailValidator::CFWS_FWS,
EmailValidator::DNSWARN_NO_RECORD,
),
"\"\t\"@invalid.example.com"
),
array(
array(
EmailValidator::RFC5321_QUOTEDSTRING,
EmailValidator::CFWS_FWS,
EmailValidator::DNSWARN_NO_RECORD
),
"\"\r\"@invalid.example.com"
),
array(
array(
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[127.0.0.1]'
),
array(
array(
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]'
),
array(
array(
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::RFC5321_IPV6DEPRECATED,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370::]'
),
array(
array(
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::RFC5322_IPV6_MAXGRPS,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334::]'
),
array(
array(
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::RFC5322_IPV6_2X2XCOLON,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[IPv6:1::1::1]'
),
array(
array(
EmailValidator::RFC5322_DOMLIT_OBSDTEXT,
EmailValidator::RFC5322_DOMAINLITERAL,
EmailValidator::DNSWARN_NO_RECORD,
),
"example@[\n]"
),
array(
array(
EmailValidator::RFC5322_DOMAINLITERAL,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[::1]'
),
array(
array(
EmailValidator::RFC5322_DOMAINLITERAL,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[::123.45.67.178]'
),
array(
array(
EmailValidator::RFC5322_IPV6_COLONSTRT,
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::RFC5322_IPV6_GRPCOUNT,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[IPv6::2001:0db8:85a3:0000:0000:8a2e:0370:7334]'
),
array(
array(
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::RFC5322_IPV6_BADCHAR,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[IPv6:z001:0db8:85a3:0000:0000:8a2e:0370:7334]'
),
array(
array(
EmailValidator::RFC5321_ADDRESSLITERAL,
EmailValidator::RFC5322_IPV6_COLONEND,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:]'
),
array(
array(
EmailValidator::RFC5321_QUOTEDSTRING,
EmailValidator::DNSWARN_NO_RECORD
),
'"example"@invalid.example.com'
),
array(
array(
EmailValidator::RFC5322_LOCAL_TOOLONG,
EmailValidator::DNSWARN_NO_RECORD
),
'too_long_localpart_too_long_localpart_too_long_localpart_too_long_localpart@invalid.example.com'
),
array(
array(
EmailValidator::RFC5322_LABEL_TOOLONG,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart.co.uk'
),
array(
array(
EmailValidator::RFC5322_DOMAIN_TOOLONG,
EmailValidator::RFC5322_TOOLONG,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocal'.
'parttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'.
'toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'
),
array(
array(
EmailValidator::RFC5322_DOMAIN_TOOLONG,
EmailValidator::RFC5322_TOOLONG,
EmailValidator::DNSWARN_NO_RECORD,
),
'example@toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocal'.
'parttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'.
'toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpar'
),
array(
array(
EmailValidator::DNSWARN_NO_RECORD,
),
'test@test'
),
);
}
public function testInvalidEmailsWithStrict()
{
$this->assertFalse($this->validator->isValid('"test"@test', false, true));
}
}