Test Failed
Pull Request — master (#194)
by
unknown
02:39
created

MailboxCheckValidation::getWarnings()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Egulias\EmailValidator\Validation;
4
5
use Egulias\EmailValidator\EmailLexer;
6
use Egulias\EmailValidator\Exception\InvalidEmail;
7
use Egulias\EmailValidator\Helper\SmtpSocketHelper;
8
use Egulias\EmailValidator\Validation\Error\IllegalMailbox;
9
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
10
use Egulias\EmailValidator\Warning\SocketWarning;
11
use Egulias\EmailValidator\Warning\Warning;
12
13
class MailboxCheckValidation implements EmailValidation
14
{
15
    const END_OF_LINE = "\r\n";
16
17
    /**
18
     * @var InvalidEmail
19
     */
20
    private $error;
21
22
    /**
23
     * @var Warning[]
24
     */
25
    private $warnings = [];
26
27
    /**
28
     * @var SmtpSocketHelper
29
     */
30
    private $socketHelper;
31
32
    /**
33
     * @var string
34
     */
35
    private $fromEmail;
36
37
    /**
38
     * @var int
39
     */
40
    private $lastResponseCode;
41
42
    /**
43
     * MailboxCheckValidation constructor.
44
     *
45
     * @param SmtpSocketHelper $socketHelper
46
     * @param string $fromEmail
47
     */
48
    public function __construct(SmtpSocketHelper $socketHelper, $fromEmail)
49
    {
50
        if (!extension_loaded('intl')) {
51
            throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
52
        }
53
54
        $this->socketHelper = $socketHelper;
55
        $this->fromEmail = $fromEmail;
56
    }
57
58
    /**
59
     * @inheritDoc
60
     */
61
    public function getError()
62
    {
63
        return $this->error;
64
    }
65
66
    /**
67
     * @inheritDoc
68
     */
69
    public function getWarnings()
70
    {
71
        return $this->warnings;
72
    }
73
74
    /**
75
     * @inheritDoc
76
     */
77
    public function isValid($email, EmailLexer $emailLexer)
78
    {
79
        $mxHosts = $this->getMXHosts($email);
80
81
        $isValid = false;
82
        foreach ($mxHosts as $mxHost) {
83
            if ($this->checkMailboxExists($mxHost, $email)) {
84
                $isValid = true;
85
                break;
86
            }
87
        }
88
89
        if (!$isValid) {
90
            $this->error = new IllegalMailbox($this->lastResponseCode);
91
        }
92
93
        return $this->error === null;
94
    }
95
96
    /**
97
     * Gets MX Hosts from email
98
     *
99
     * @param string $email
100
     *
101
     * @return array
102
     */
103
    protected function getMXHosts($email)
104
    {
105
        $hostname = $this->extractHostname($email);
106
107
        $result = getmxrr($hostname, $mxHosts);
108
        if (!$result) {
109
            $this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
110
        }
111
112
        return $mxHosts;
113
    }
114
115
    /**
116
     * Extracts hostname from email
117
     *
118
     * @param string $email
119
     *
120
     * @return string
121
     */
122
    private function extractHostname($email)
123
    {
124
        $variant = defined('INTL_IDNA_VARIANT_UTS46') ? INTL_IDNA_VARIANT_UTS46 : INTL_IDNA_VARIANT_2003;
125
126
        $lastAtPos = strrpos($email, '@');
127
        if ((bool) $lastAtPos) {
128
            $hostname = substr($email, $lastAtPos + 1);
129
            return rtrim(idn_to_ascii($hostname, IDNA_DEFAULT, $variant), '.') . '.';
130
        }
131
132
        return rtrim(idn_to_ascii($email, IDNA_DEFAULT, $variant), '.') . '.';
133
    }
134
135
    /**
136
     * Checks mailbox
137
     *
138
     * @param string $hostname
139
      * @param string $email
140
     * @return bool
141
     */
142
    private function checkMailboxExists($hostname, $email)
143
    {
144
        $this->socketHelper->open($hostname, $errno, $errstr);
145
146
        if (!$this->socketHelper->isResource()) {
147
            $this->warnings[SocketWarning::CODE][] = new SocketWarning($hostname, $errno, $errstr);
148
149
            return false;
150
        }
151
152
        $this->lastResponseCode = $this->socketHelper->getResponseCode();
153
        if ($this->lastResponseCode !== 220) {
154
            return false;
155
        }
156
157
        $this->socketHelper->write("EHLO {$hostname}" . self::END_OF_LINE);
158
        $this->lastResponseCode = $this->socketHelper->getResponseCode();
159
        if ($this->lastResponseCode !== 250) {
160
            return false;
161
        }
162
163
        $this->socketHelper->write("MAIL FROM: <{$this->fromEmail}>" . self::END_OF_LINE);
164
        $this->lastResponseCode = $this->socketHelper->getResponseCode();
165
        if ($this->lastResponseCode !== 250) {
166
            return false;
167
        }
168
169
        $this->socketHelper->write("RCPT TO: <{$email}>" . self::END_OF_LINE);
170
        $this->lastResponseCode = $this->socketHelper->getResponseCode();
171
        if ($this->lastResponseCode !== 250) {
172
            return false;
173
        }
174
175
        $this->socketHelper->write('QUIT' . self::END_OF_LINE);
176
177
        $this->socketHelper->close();
178
179
        return true;
180
    }
181
}