Completed
Pull Request — master (#37)
by Frederik
02:05
created

Server   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 94
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 97.3%

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 9
dl 0
loc 94
c 0
b 0
f 0
rs 10
ccs 36
cts 37
cp 0.973

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
D start() 0 35 9
A handleCommand() 0 13 3
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol\Smtp;
5
6
use Genkgo\Mail\Exception\ConnectionBrokenException;
7
use Genkgo\Mail\Exception\ConnectionTimeoutException;
8
use Genkgo\Mail\Exception\ConnectionClosedException;
9
use Genkgo\Mail\Exception\UnknownSmtpCommandException;
10
use Genkgo\Mail\Protocol\AppendCrlfConnection;
11
use Genkgo\Mail\Protocol\ConnectionInterface;
12
use Genkgo\Mail\Protocol\ConnectionListenerInterface;
13
use Genkgo\Mail\Protocol\Smtp\Capability\EhloCapability;
14
use Genkgo\Mail\Protocol\Smtp\Capability\QuitCapability;
15
use Genkgo\Mail\Protocol\Smtp\Capability\ResetCapability;
16
use Genkgo\Mail\Protocol\TrimCrlfConnection;
17
18
final class Server
19
{
20
    /**
21
     * @var ConnectionListenerInterface
22
     */
23
    private $listener;
24
    /**
25
     * @var CapabilityInterface[]
26
     */
27
    private $capabilities = [];
28
    /**
29
     * @var string
30
     */
31
    private $serverName;
32
33
    /**
34
     * Client constructor.
35
     * @param ConnectionListenerInterface $connection
36
     * @param array $capabilities
37
     * @param string $serverName
38
     */
39 6
    public function __construct(ConnectionListenerInterface $connection, array $capabilities, string $serverName) {
40 6
        $this->listener = $connection;
41 6
        $this->serverName = $serverName;
42
43 6
        $this->capabilities = array_merge(
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($capabilitie...ity\ResetCapability())) of type array is incompatible with the declared type array<integer,object<Gen...p\CapabilityInterface>> of property $capabilities.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
44 6
            $capabilities,
45
            [
46 6
                new EhloCapability($this->serverName, $capabilities),
47 6
                new QuitCapability(),
48 6
                new ResetCapability()
49
            ]
50
        );
51 6
    }
52
53
    /**
54
     *
55
     */
56 6
    public function start(): void
57
    {
58 6
        while ($connection = $this->listener->listen()) {
59 6
            $connection = new TrimCrlfConnection(new AppendCrlfConnection($connection));
60 6
            $connection->send('220 Welcome to Genkgo Mail Server');
61
62
            try {
63 6
                $session = new Session();
64
65 6
                while ($session = $session->withCommand($connection->receive())) {
66
                    try {
67 2
                        $session = $this->handleCommand($connection, $session);
68 1
                    } catch (UnknownSmtpCommandException $e) {
69 1
                        $connection->send('500 unrecognized command');
70
                    }
71
72 2
                    if ($session->getState() === Session::STATE_DISCONNECT) {
73 2
                        break;
74
                    }
75
                }
76 4
            } catch (ConnectionTimeoutException $e) {
77 1
                $connection->send('421 command timeout - closing connection');
78 1
                $connection->disconnect();
79 3
            } catch (ConnectionBrokenException $e) {
80
                try {
81 2
                    $connection->send('554 transaction failed, unexpected value - closing connection');
82 1
                } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
83
                }
84
85 2
                $connection->disconnect();
86 1
            } catch (ConnectionClosedException $e) {
87 1
                $connection->disconnect();
88
            }
89
        }
90
    }
91
92
    /**
93
     * @param ConnectionInterface $connection
94
     * @param Session $session
95
     * @return Session
96
     * @throws UnknownSmtpCommandException
97
     */
98 2
    private function handleCommand(ConnectionInterface $connection, Session $session): Session
99
    {
100 2
        $command = $session->getCommand();
101
102 2
        foreach ($this->capabilities as $capability) {
103 2
            $advertised = $capability->advertise();
104 2
            if (substr($command, 0, strlen($advertised)) === $advertised) {
105 2
                return $capability->manifest($connection, $session);
106
            }
107
        }
108
109 1
        throw new UnknownSmtpCommandException('Cannot handle command');
110
    }
111
}