ClientFactory::fromString()   F
last analyzed

Complexity

Conditions 15
Paths 387

Size

Total Lines 93

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 53
CRAP Score 15.3575

Importance

Changes 0
Metric Value
dl 0
loc 93
ccs 53
cts 60
cp 0.8833
rs 2.3493
c 0
b 0
f 0
cc 15
nc 387
nop 2
crap 15.3575

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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