Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding documentation and consolidating some code for readability #89

Merged
merged 1 commit into from
Mar 22, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
115 changes: 86 additions & 29 deletions src/Egulias/EmailValidator/EmailValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,50 @@
*/
class EmailValidator
{
/**
* 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_ATEXT_AFTER_QS = 134;
const ERR_ATEXT_AFTER_DOMLIT = 135;
const ERR_EXPECTING_QPAIR = 136;
const ERR_EXPECTING_ATEXT = 137;
const ERR_EXPECTING_QTEXT = 138;
const ERR_EXPECTING_CTEXT = 139;
const ERR_BACKSLASHEND = 140;
const ERR_DOT_START = 141;
const ERR_DOT_END = 142;
const ERR_DOMAINHYPHENSTART = 143;
const ERR_DOMAINHYPHENEND = 144;
const ERR_UNCLOSEDQUOTEDSTR = 145;
const ERR_UNCLOSEDCOMMENT = 146;
const ERR_UNCLOSEDDOMLIT = 147;
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_TLDNUMERIC = 10;
const RFC5321_QUOTEDSTRING = 11;
const RFC5321_ADDRESSLITERAL = 12;
const RFC5321_IPV6DEPRECATED = 13;
const CFWS_COMMENT = 17;
const CFWS_FWS = 18;
const DEPREC_LOCALPART = 33;
const DEPREC_FWS = 34;
const DEPREC_QTEXT = 35;
const DEPREC_QP = 36;
const DEPREC_COMMENT = 37;
const DEPREC_CTEXT = 38;
const DEPREC_CFWS_NEAR_AT = 49;
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_DOMAIN = 65;
const RFC5322_TOOLONG = 66;
const RFC5322_DOMAIN_TOOLONG = 255;
const RFC5322_DOMAINLITERAL = 70;
Expand All @@ -61,19 +63,77 @@ class EmailValidator
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());
}

/**
* 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
*
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add

which are for SMTPUTF8 international emails.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, will do. In my mind, I had been treating 6530 as the 'parent' to 6531 and 6532, but I suppose that's not really a fair way of looking at those RFCs!

* @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)
{
try {
Expand All @@ -85,18 +145,14 @@ public function isValid($email, $checkDNS = false, $strict = false)
return false;
}

$dns = true;
if ($checkDNS) {
$dns = $this->checkDNS();
}
$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() && $dns);
return ($strict ? (!$this->hasWarnings() && !$dnsProblemExists) : true);
}

/**
Expand Down Expand Up @@ -143,19 +199,20 @@ 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()
{
$checked = true;

$result = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX');
$mxRecordExists = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX');

if (!$result) {
if (!$mxRecordExists) {
$this->warnings[] = self::DNSWARN_NO_RECORD;
$checked = false;
$this->addTLDWarnings();
}

return $checked;
return $mxRecordExists;
}

protected function addTLDWarnings()
Expand Down