Completed
Push — master ( f3eba8...54b800 )
by Camilo
03:34
created

Connect   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Test Coverage

Coverage 69.44%

Importance

Changes 0
Metric Value
wmc 11
dl 0
loc 110
ccs 25
cts 36
cp 0.6944
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getConnectionParameters() 0 7 2
A setConnectionParameters() 0 4 1
A createVariableHeader() 0 7 1
A expectAnswer() 0 19 3
A shouldExpectAnswer() 0 3 1
A createPayload() 0 23 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace unreal4u\MQTT\Protocol;
6
7
use unreal4u\MQTT\Exceptions\Connect\IdentifierRejected;
8
use unreal4u\MQTT\Exceptions\Connect\NoConnectionParametersDefined;
9
use unreal4u\MQTT\Exceptions\MustProvideUsername;
10
use unreal4u\MQTT\Internals\ClientInterface;
11
use unreal4u\MQTT\Internals\EventManager;
12
use unreal4u\MQTT\Internals\ProtocolBase;
13
use unreal4u\MQTT\Internals\ReadableContentInterface;
14
use unreal4u\MQTT\Internals\WritableContent;
15
use unreal4u\MQTT\Internals\WritableContentInterface;
16
use unreal4u\MQTT\Protocol\Connect\Parameters;
17
use unreal4u\MQTT\Utilities;
18
19
/**
20
 * After a Network Connection is established by a Client to a Server, the first Packet sent from the Client to the
21
 * Server MUST be a CONNECT Packet
22
 */
23
final class Connect extends ProtocolBase implements WritableContentInterface
24
{
25
    use WritableContent;
26
27
    const CONTROL_PACKET_VALUE = 1;
28
29
    /**
30
     * @var Parameters
31
     */
32
    private $connectionParameters;
33
34
    /**
35
     * Saves the mandatory connection parameters onto this object
36
     * @param Parameters $connectionParameters
37
     *
38
     * @return Connect
39
     */
40 5
    public function setConnectionParameters(Parameters $connectionParameters): self
41
    {
42 5
        $this->connectionParameters = $connectionParameters;
43 5
        return $this;
44
    }
45
46
    /**
47
     * Get the connection parameters from the private object
48
     *
49
     * @return Parameters
50
     * @throws \unreal4u\MQTT\Exceptions\Connect\NoConnectionParametersDefined
51
     */
52 2
    public function getConnectionParameters(): Parameters
53
    {
54 2
        if ($this->connectionParameters === null) {
55 1
            throw new NoConnectionParametersDefined('You must pass on the connection parameters before connecting');
56
        }
57
58 1
        return $this->connectionParameters;
59
    }
60
61
    /**
62
     * @return string
63
     * @throws \OutOfRangeException
64
     */
65 2
    public function createVariableHeader(): string
66
    {
67 2
        $bitString = $this->createUTF8String('MQTT'); // Connect MUST begin with MQTT
68 2
        $bitString .= $this->connectionParameters->getProtocolVersionBinaryRepresentation(); // Protocol level
69 2
        $bitString .= \chr($this->connectionParameters->getFlags());
70 2
        $bitString .= Utilities::convertNumberToBinaryString($this->connectionParameters->keepAlivePeriod);
71 2
        return $bitString;
72
    }
73
74 3
    public function createPayload(): string
75
    {
76
        // The order in a connect string is clientId first
77 3
        $output = $this->createUTF8String((string)$this->connectionParameters->getClientId());
78
79
        // Then the willTopic if it is set
80 3
        $output .= $this->createUTF8String($this->connectionParameters->getWillTopic());
81
82
        // The willMessage will come next
83 3
        $output .= $this->createUTF8String($this->connectionParameters->getWillMessage());
84
85
        // If the username is set, it will come next
86 3
        $output .= $this->createUTF8String($this->connectionParameters->getUsername());
87
88
        // And finally the password as last parameter
89 3
        if ($this->connectionParameters->getPassword() !== '') {
90 2
            if ($this->connectionParameters->getUsername() === '') {
91 1
                throw new MustProvideUsername('A password can not be set without a username! Please set username');
92
            }
93 1
            $output .= $this->createUTF8String($this->connectionParameters->getPassword());
94
        }
95
96 2
        return $output;
97
    }
98
99 1
    public function shouldExpectAnswer(): bool
100
    {
101 1
        return true;
102
    }
103
104
    /**
105
     * Special handling of the ConnAck object: be able to inject more information into the object before throwing it
106
     *
107
     * @param string $data
108
     * @param ClientInterface $client
109
     *
110
     * @return ReadableContentInterface
111
     * @throws \DomainException
112
     * @throws \unreal4u\MQTT\Exceptions\Connect\IdentifierRejected
113
     */
114
    public function expectAnswer(string $data, ClientInterface $client): ReadableContentInterface
115
    {
116
        $this->logger->info('String of incoming data confirmed, returning new object', ['callee' => \get_class($this)]);
117
118
        $eventManager = new EventManager($this->logger);
119
        try {
120
            $connAck = $eventManager->analyzeHeaders($data, $client);
121
        } catch (IdentifierRejected $e) {
122
            $possibleReasons = '';
123
            foreach ($this->connectionParameters->getClientId()->performStrictValidationCheck() as $errorMessage) {
124
                $possibleReasons .= $errorMessage . PHP_EOL;
125
            }
126
127
            $e->fillPossibleReason($possibleReasons);
128
            // Re-throw the exception with all information filled in
129
            throw $e;
130
        }
131
132
        return $connAck;
133
    }
134
}
135