--- /dev/null
+<?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()
+ {
+ $host = $this->parser->getParsedDomainPart();
+ $host = rtrim($host, '.') . '.';
+
+ $mxRecordExists = checkdnsrr($host, '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;
+ }
+ }
+}