3 namespace Egulias\EmailValidator;
8 * @author Eduardo Gulias Davis <me@egulias.com>
10 class EmailValidator implements EmailValidatorInterface
13 * Critical validation errors used to indicate that
14 * an email address is invalid:
16 const ERR_CONSECUTIVEATS = 128;
17 const ERR_EXPECTING_DTEXT = 129;
18 const ERR_NOLOCALPART = 130;
19 const ERR_NODOMAIN = 131;
20 const ERR_CONSECUTIVEDOTS = 132;
21 const ERR_ATEXT_AFTER_CFWS = 133;
22 const ERR_EXPECTING_QPAIR = 136;
23 const ERR_EXPECTING_ATEXT = 137;
24 const ERR_EXPECTING_CTEXT = 139;
25 const ERR_DOT_START = 141;
26 const ERR_DOT_END = 142;
27 const ERR_DOMAINHYPHENEND = 144;
28 const ERR_UNCLOSEDQUOTEDSTR = 145;
29 const ERR_UNCLOSEDCOMMENT = 146;
30 const ERR_FWS_CRLF_X2 = 148;
31 const ERR_FWS_CRLF_END = 149;
32 const ERR_CR_NO_LF = 150;
33 const ERR_DEPREC_REACHED = 151;
34 const ERR_UNOPENEDCOMMENT = 152;
35 const ERR_ATEXT_AFTER_QS = 134; // not in use
36 const ERR_ATEXT_AFTER_DOMLIT = 135; // not in use
37 const ERR_EXPECTING_QTEXT = 138; // not in use
38 const ERR_BACKSLASHEND = 140; // not in use
39 const ERR_DOMAINHYPHENSTART = 143; // not in use
40 const ERR_UNCLOSEDDOMLIT = 147; // not in use
43 * Informational validation warnings regarding unusual or
44 * deprecated features found in an email address:
46 // Address is valid for SMTP (RFC-5321), but has unusual elements.
47 const RFC5321_TLD = 9;
48 const RFC5321_QUOTEDSTRING = 11;
49 const RFC5321_ADDRESSLITERAL = 12;
50 const RFC5321_IPV6DEPRECATED = 13;
51 const RFC5321_TLDNUMERIC = 10; // not in use
52 // Address is only valid according to the broad
53 // definition of RFC-5322. It is otherwise invalid.
54 const RFC5322_LOCAL_TOOLONG = 64;
55 const RFC5322_LABEL_TOOLONG = 63;
56 const RFC5322_TOOLONG = 66;
57 const RFC5322_DOMAIN_TOOLONG = 255;
58 const RFC5322_DOMAINLITERAL = 70;
59 const RFC5322_DOMLIT_OBSDTEXT = 71;
60 const RFC5322_IPV6_GRPCOUNT = 72;
61 const RFC5322_IPV6_2X2XCOLON = 73;
62 const RFC5322_IPV6_BADCHAR = 74;
63 const RFC5322_IPV6_MAXGRPS = 75;
64 const RFC5322_IPV6_COLONSTRT = 76;
65 const RFC5322_IPV6_COLONEND = 77;
66 const RFC5322_DOMAIN = 65; // not in use
67 // Address contains deprecated elements, but may
68 // still be valid in restricted contexts.
70 const DEPREC_COMMENT = 37;
71 const DEPREC_CFWS_NEAR_AT = 49;
72 const DEPREC_LOCALPART = 33; // not in use
73 const DEPREC_FWS = 34; // not in use
74 const DEPREC_QTEXT = 35; // not in use
75 const DEPREC_CTEXT = 38; // not in use
76 // Address is valid within the message,
77 // but cannot be used unmodified in the envelope.
78 const CFWS_COMMENT = 17;
80 // Hostname DNS checks were unsuccessful.
81 const DNSWARN_NO_MX_RECORD = 5;
82 const DNSWARN_NO_RECORD = 6;
90 * Contains any informational warnings regarding unusual/deprecated
91 * features that were encountered during validation.
95 protected $warnings = array();
98 * If a critical validation problem is encountered, this will be
99 * set to the value of one of this class's ERR_* constants.
108 protected $threshold = 255;
110 public function __construct()
112 $this->parser = new EmailParser(new EmailLexer());
118 public function isValid($email, $checkDNS = false, $strict = false)
121 $this->parser->parse((string)$email);
122 $this->warnings = $this->parser->getWarnings();
123 } catch (\Exception $e) {
124 $rClass = new \ReflectionClass($this);
125 $this->error = $rClass->getConstant($e->getMessage());
129 $dnsProblemExists = ($checkDNS ? !$this->checkDNS() : false);
131 if ($this->hasWarnings() && ((int) max($this->warnings) > $this->threshold)) {
132 $this->error = self::ERR_DEPREC_REACHED;
136 return !($dnsProblemExists || $strict && $this->hasWarnings());
142 public function hasWarnings()
144 return !empty($this->warnings);
150 public function getWarnings()
152 return $this->warnings;
158 public function getError()
166 public function setThreshold($threshold)
168 $this->threshold = (int) $threshold;
176 public function getThreshold()
178 return $this->threshold;
182 * @return bool Whether or not an MX record exists for the
183 * email address's host name.
185 protected function checkDNS()
187 $host = $this->parser->getParsedDomainPart();
188 $host = rtrim($host, '.') . '.';
190 $mxRecordExists = checkdnsrr($host, 'MX');
192 if (!$mxRecordExists) {
193 $this->warnings[] = self::DNSWARN_NO_RECORD;
194 $this->addTLDWarnings();
197 return $mxRecordExists;
200 protected function addTLDWarnings()
202 if (!in_array(self::DNSWARN_NO_RECORD, $this->warnings) &&
203 !in_array(self::DNSWARN_NO_MX_RECORD, $this->warnings) &&
204 in_array(self::RFC5322_DOMAINLITERAL, $this->warnings)
206 $this->warnings[] = self::RFC5321_TLD;