Completed
Push — master ( e1389e...1f6470 )
by Frederik
02:10
created

ClientFactory   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 92.45%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 9
dl 0
loc 263
ccs 98
cts 106
cp 0.9245
rs 9.8
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A withTimeout() 0 6 1
A withAuthentication() 0 12 2
A withEhlo() 0 6 1
A withInsecureConnectionAllowed() 0 6 1
A __construct() 0 5 1
A withStartTls() 0 6 1
A withoutStartTls() 0 6 1
B newClient() 0 37 4
F fromString() 0 92 19
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol\Smtp;
5
6
use Genkgo\Mail\Protocol\ConnectionInterface;
7
use Genkgo\Mail\Protocol\CryptoConstant;
8
use Genkgo\Mail\Protocol\PlainTcpConnection;
9
use Genkgo\Mail\Protocol\AutomaticConnection;
10
use Genkgo\Mail\Protocol\SecureConnectionOptions;
11
use Genkgo\Mail\Protocol\Smtp\Negotiation\AuthNegotiation;
12
use Genkgo\Mail\Protocol\Smtp\Negotiation\ForceTlsUpgradeNegotiation;
13
use Genkgo\Mail\Protocol\Smtp\Negotiation\TryTlsUpgradeNegotiation;
14
use Genkgo\Mail\Protocol\SecureConnection;
15
16
/**
17
 * Class ClientFactory
18
 * @package Genkgo\Mail\Protocol\Smtp
19
 */
20
final class ClientFactory
21
{
22
    /**
23
     *
24
     */
25
    private CONST AUTH_ENUM = [Client::AUTH_NONE, Client::AUTH_PLAIN, Client::AUTH_LOGIN, Client::AUTH_AUTO];
26
    /**
27
     * @var ConnectionInterface
28
     */
29
    private $connection;
30
    /**
31
     * @var string
32
     */
33
    private $password = '';
34
    /**
35
     * @var float
36
     */
37
    private $timeout = 1;
38
    /**
39
     * @var string
40
     */
41
    private $username = '';
42
    /**
43
     * @var string
44
     */
45
    private $ehlo = '127.0.0.1';
46
    /**
47
     * @var int
48
     */
49
    private $authMethod = Client::AUTH_NONE;
50
    /**
51
     * @var bool
52
     */
53
    private $insecureConnectionAllowed = false;
54
    /**
55
     * @var string
56
     */
57
    private $reconnectAfter = 'PT300S';
58
    /**
59
     * @var int
60
     */
61
    private $startTls;
62
63
    /**
64
     * ClientFactory constructor.
65
     * @param ConnectionInterface $connection
66
     */
67 9
    public function __construct(ConnectionInterface $connection)
68
    {
69 9
        $this->connection = $connection;
70 9
        $this->startTls = CryptoConstant::getAdvisedType();
71 9
    }
72
73
    /**
74
     * @param float $connectionTimeout
75
     * @return ClientFactory
76
     */
77 1
    public function withTimeout(float $connectionTimeout): ClientFactory
78
    {
79 1
        $clone = clone $this;
80 1
        $clone->timeout = $connectionTimeout;
81 1
        return $clone;
82
    }
83
84
    /**
85
     * @param int $method
86
     * @param string $password
87
     * @param string $username
88
     * @return ClientFactory
89
     */
90 3
    public function withAuthentication(int $method, string $username, string $password): ClientFactory
91
    {
92 3
        if (!in_array($method, self::AUTH_ENUM)) {
93 1
            throw new \InvalidArgumentException('Invalid authentication method');
94
        }
95
96 2
        $clone = clone $this;
97 2
        $clone->authMethod = $method;
98 2
        $clone->username = $username;
99 2
        $clone->password = $password;
100 2
        return $clone;
101
    }
102
103
    /**
104
     * @param string $ehlo
105
     * @return ClientFactory
106
     */
107 4
    public function withEhlo(string $ehlo): ClientFactory
108
    {
109 4
        $clone = clone $this;
110 4
        $clone->ehlo = $ehlo;
111 4
        return $clone;
112
    }
113
114
    /**
115
     * @return ClientFactory
116
     */
117 2
    public function withInsecureConnectionAllowed(): ClientFactory
118
    {
119 2
        $clone = clone $this;
120 2
        $clone->insecureConnectionAllowed = true;
121 2
        return $clone;
122
    }
123
124
    /**
125
     * @param int $crypto
126
     * @return ClientFactory
127
     */
128
    public function withStartTls(int $crypto): ClientFactory
129
    {
130
        $clone = clone $this;
131
        $clone->startTls = $crypto;
132
        return $clone;
133
    }
134
135
    /**
136
     * @return ClientFactory
137
     */
138 1
    public function withoutStartTls(): ClientFactory
139
    {
140 1
        $clone = clone $this;
141 1
        $clone->startTls = 0;
142 1
        return $clone;
143
    }
144
145
    /**
146
     * @return Client
147
     */
148 7
    public function newClient(): Client
149
    {
150 7
        $negotiators = [];
151
152 7
        if ($this->startTls !== 0) {
153 6
            if ($this->insecureConnectionAllowed) {
154 2
                $negotiators[] = new TryTlsUpgradeNegotiation(
155 2
                    $this->connection,
156 2
                    $this->ehlo,
157 2
                    $this->startTls
158
                );
159
            } else {
160 4
                $negotiators[] = new ForceTlsUpgradeNegotiation(
161 4
                    $this->connection,
162 4
                    $this->ehlo,
163 4
                    $this->startTls
164
                );
165
            }
166
        }
167
168 7
        if ($this->authMethod !== Client::AUTH_NONE) {
169 2
            $negotiators[] = new AuthNegotiation(
170 2
                $this->ehlo,
171 2
                $this->authMethod,
172 2
                $this->username,
173 2
                $this->password
174
            );
175
        }
176
177 7
        return new Client(
178 7
            new AutomaticConnection(
179 7
                $this->connection,
180 7
                new \DateInterval($this->reconnectAfter)
181
            ),
182 7
            $negotiators
183
        );
184
    }
185
186
    /**
187
     * @param string $dataSourceName
188
     * @return ClientFactory
189
     */
190 6
    public static function fromString(string $dataSourceName):ClientFactory
191
    {
192 6
        $components = parse_url($dataSourceName);
193 6
        if (!isset($components['scheme']) || !isset($components['host'])) {
194 1
            throw new \InvalidArgumentException('Scheme and host are required');
195
        }
196
197 5
        $insecureConnectionAllowed = false;
198 5
        switch ($components['scheme']) {
199 5
            case 'smtp+secure':
200 1
                $connection = new SecureConnection(
201 1
                    CryptoConstant::getAdvisedProtocol(),
202 1
                    $components['host'],
203 1
                    $components['port'] ?? 465,
204 1
                    new SecureConnectionOptions()
205
                );
206 1
                break;
207 4
            case 'smtp+ssl':
208 4
            case 'smtp+tls':
209 1
                $connection = new SecureConnection(
210 1
                    CryptoConstant::getSupportProtocol(),
211 1
                    $components['host'],
212 1
                    $components['port'] ?? 465,
213 1
                    new SecureConnectionOptions()
214
                );
215 1
                break;
216 3
            case 'smtp':
217 2
            case 'smtp-starttls':
218 2
                $insecureConnectionAllowed = true;
219 2
                $connection = new PlainTcpConnection(
220 2
                    $components['host'],
221 2
                    $components['port'] ?? 25
222
                );
223 2
                break;
224 1
            case 'smtp+starttls':
225
                $connection = new PlainTcpConnection(
226
                    $components['host'],
227
                    $components['port'] ?? 587
228
                );
229
                break;
230
            default:
231 1
                throw new \InvalidArgumentException(
232 1
                    'Use smtp+secure:// smtp+tls:// smtp+starttls:// smtp+plain://'
233
                );
234
        }
235
236 4
        $factory = new self($connection);
237 4
        $factory->insecureConnectionAllowed = $insecureConnectionAllowed;
238
239 4
        if (isset($components['user']) && isset($components['pass'])) {
240 1
            $factory->authMethod = Client::AUTH_AUTO;
241 1
            $factory->username = urldecode($components['user']);
242 1
            $factory->password = urldecode($components['pass']);
243
        }
244
245 4
        if (isset($components['query'])) {
246 1
            parse_str($components['query'], $query);
247
248 1
            if (isset($query['ehlo'])) {
249 1
                $factory->ehlo = $query['ehlo'];
250
            }
251
252 1
            if (isset($query['timeout'])) {
253 1
                $factory->timeout = (float)$query['timeout'];
254
            }
255
256 1
            if (isset($query['reconnectAfter'])) {
257 1
                $factory->reconnectAfter = $query['reconnectAfter'];
258
            }
259
260 1
            if (isset($query['starttls'])) {
261
                // @codeCoverageIgnoreStart
262
                switch ($query['starttls']) {
263
                    case 'advised':
264
                        $factory->startTls = CryptoConstant::getAdvisedType();
265
                        break;
266
                    case 'support':
267
                        $factory->startTls = CryptoConstant::getSupportType();
268
                        break;
269
                    case 'none':
270
                        $factory->startTls = 0;
271
                        break;
272
                    default:
273
                        $factory->startTls = (int)$query['starttls'];
274
                        break;
275
                }
276
                // @codeCoverageIgnoreEnd
277
            }
278
        }
279
280 4
        return $factory;
281
    }
282
}
283