Completed
Pull Request — 1.2 (#120)
by
unknown
06:26
created

EmailValidator::hasWarnings()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Egulias\EmailValidator;
4
5
/**
6
 * EmailValidator
7
 *
8
 * @author Eduardo Gulias Davis <[email protected]>
9
 */
10
class EmailValidator implements EmailValidatorInterface
11
{
12
    /**
13
     * Critical validation errors used to indicate that
14
     * an email address is invalid:
15
     */
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
41
42
    /**
43
     * Informational validation warnings regarding unusual or
44
     * deprecated features found in an email address:
45
     */
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.
69
    const DEPREC_QP               = 36;
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;
79
    const CFWS_FWS                = 18;
80
    // Hostname DNS checks were unsuccessful.
81
    const DNSWARN_NO_MX_RECORD    = 5;
82
    const DNSWARN_NO_RECORD       = 6;
83
84
    /**
85
     * @var EmailParser
86
     */
87
    protected $parser;
88
89
    /**
90
     * Contains any informational warnings regarding unusual/deprecated
91
     * features that were encountered during validation.
92
     *
93
     * @var array
94
     */
95
    protected $warnings = array();
96
97
    /**
98
     * If a critical validation problem is encountered, this will be
99
     * set to the value of one of this class's ERR_* constants.
100
     *
101
     * @var int
102
     */
103
    protected $error;
104
105
    /**
106
     * @var int
107
     */
108
    protected $threshold = 255;
109
110 151
    public function __construct()
111
    {
112 151
        $this->parser = new EmailParser(new EmailLexer());
113 151
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 151
    public function isValid($email, $checkDNS = false, $strict = false)
119
    {
120
        try {
121 151
            $this->parser->parse((string)$email);
122 75
            $this->warnings = $this->parser->getWarnings();
123 151
        } catch (\Exception $e) {
124 76
            $rClass = new \ReflectionClass($this);
125 76
            $this->error = $rClass->getConstant($e->getMessage());
126 76
            return false;
127
        }
128
129 75
        $dnsProblemExists = ($checkDNS ? !$this->checkDNS() : false);
130
131 75
        if ($this->hasWarnings() && ((int) max($this->warnings) > $this->threshold)) {
132
            $this->error = self::ERR_DEPREC_REACHED;
133
            return false;
134
        }
135
136 75
        return ($strict ? (!$this->hasWarnings() && !$dnsProblemExists) : true);
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 75
    public function hasWarnings()
143
    {
144 75
        return !empty($this->warnings);
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 46
    public function getWarnings()
151
    {
152 46
        return $this->warnings;
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158 26
    public function getError()
159
    {
160 26
        return $this->error;
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166
    public function setThreshold($threshold)
167
    {
168
        $this->threshold = (int) $threshold;
169
170
        return $this;
171
    }
172
173
    /**
174
     * {@inheritdoc}
175
     */
176
    public function getThreshold()
177
    {
178
        return $this->threshold;
179
    }
180
181
    /**
182
     * @return bool Whether or not an MX record exists for the
183
     *              email address's host name.
184
     */
185 46
    protected function checkDNS()
186
    {
187 46
        $mxRecordExists = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX');
188
189 46
        if (!$mxRecordExists) {
190 46
            $this->warnings[] = self::DNSWARN_NO_RECORD;
191 46
            $this->addTLDWarnings();
192 46
        }
193
194 46
        return $mxRecordExists;
195
    }
196
197 46
    protected function addTLDWarnings()
198
    {
199 46
        if (!in_array(self::DNSWARN_NO_RECORD, $this->warnings) &&
200 46
            !in_array(self::DNSWARN_NO_MX_RECORD, $this->warnings) &&
201
            in_array(self::RFC5322_DOMAINLITERAL, $this->warnings)
202 46
        ) {
203
            $this->warnings[] = self::RFC5321_TLD;
204
        }
205 46
    }
206
}
207