Parameters::setWill()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 15
c 0
b 0
f 0
ccs 9
cts 9
cp 1
rs 9.9666
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace unreal4u\MQTT\Protocol\Connect;
6
7
use InvalidArgumentException;
8
use Psr\Log\LoggerInterface;
9
use unreal4u\Dummy\Logger;
10
use unreal4u\MQTT\DataTypes\BrokerPort;
11
use unreal4u\MQTT\DataTypes\ClientId;
12
use unreal4u\MQTT\DataTypes\Message;
13
use unreal4u\MQTT\DataTypes\ProtocolVersion;
14
use unreal4u\MQTT\Exceptions\Connect\UnacceptableProtocolVersion;
15
use unreal4u\MQTT\Exceptions\InvalidBrokerPort;
16
use unreal4u\MQTT\Exceptions\InvalidBrokerProtocol;
17
use unreal4u\MQTT\Exceptions\InvalidQoSLevel;
18
use unreal4u\MQTT\Exceptions\MessageTooBig;
19
use unreal4u\MQTT\Exceptions\MissingTopicName;
20
21
use function get_class;
22
use function sprintf;
23
use function str_replace;
24
25
/**
26
 * Special connection parameters will be defined in this class
27
 */
28
final class Parameters
29
{
30
    /**
31
     * The default protocol version this library will be talking with
32
     */
33
    private const DEFAULT_PROTOCOL_VERSION = '3.1.1';
34
35
    /**
36
     * @var LoggerInterface
37
     */
38
    private $logger;
39
40
    /**
41
     * The host we'll be connecting to
42
     *
43
     * @var string
44
     */
45
    private $host;
46
47
    /**
48
     * The port we will connect to
49
     * @var BrokerPort
50
     */
51
    private $brokerPort;
52
53
    /**
54
     * Unique (per broker) client Id. Can be empty if $cleanSession is set to true.
55
     *
56
     * @var ClientId
57
     */
58
    private $clientId;
59
60
    /**
61
     * The keep alive is a time interval in seconds (defaults to 60), the clients commits to by sending regular PING
62
     * Request messages to the broker.
63
     *
64
     * The broker response with PING Response and this mechanism will allow both sides to determine if the other one is
65
     * still alive and reachable.
66
     *
67
     * @var int
68
     */
69
    private $keepAlivePeriod = 60;
70
71
    /**
72
     * Whether to create a persistent session (default = false).
73
     *
74
     * It means that the broker will store all subscriptions for the client and also all missed messages, when
75
     * subscribing with Quality of Service (QoS) 1 or 2
76
     * @var bool
77
     */
78
    private $cleanSession = false;
79
80
    /**
81
     * The corresponding field for the username flag
82
     * @var string
83
     */
84
    private $username = '';
85
86
    /**
87
     * The corresponding field for the password flag
88
     * @var string
89
     */
90
    private $password = '';
91
92
    /**
93
     * @var Message
94
     */
95
    private $will;
96
97
    /**
98
     * @var ProtocolVersion
99
     */
100
    private $protocolVersion;
101
102
    /**
103
     * The 10th byte of the Connect call will contain a series of flags
104
     *
105
     * The order of these flags are:
106
     *
107
     *   7-6-5-4-3-2-1-0
108
     * b'0-0-0-0-0-0-0-0'
109
     *
110
     * Where
111
     * Bit 7: if username is set, this bit is true
112
     * Bit 6: if password is set, this bit is true
113
     * Bit 5: This bit specifies if the Will Message is to be Retained when it is published
114
     * Bits 4 & 3: These two bits specify the QoS level to be used when publishing the Will Message
115
     * Bit 2: If the Will Flag is set to 1 this indicates that, if the Connect request is accepted, a Will Message MUST
116
     *        be stored on the Server and associated with the Network Connection
117
     * Bit 1: This bit specifies the handling of the Session state
118
     * Bit 0: Reserved
119
     *
120
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180843
121
     */
122
    private $bitFlag = b'00000000';
123
124
    /**
125
     * Builds up the connection parameters
126
     *
127
     * @param ClientId $clientId Will default to a clientId set by the broker
128
     * @param string $host Will default to localhost
129
     * @param LoggerInterface $logger
130
     * @throws InvalidBrokerProtocol
131
     * @throws InvalidBrokerPort
132
     * @throws UnacceptableProtocolVersion
133
     */
134 20
    public function __construct(ClientId $clientId = null, string $host = 'localhost', LoggerInterface $logger = null)
135
    {
136 20
        if ($logger === null) {
137 20
            $logger = new Logger();
138
        }
139
        // Insert name of class within the logger
140 20
        $this->logger = $logger->withName(str_replace('unreal4u\\MQTT\\', '', get_class($this)));
0 ignored issues
show
Bug introduced by
The method withName() does not exist on Psr\Log\LoggerInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Psr\Log\AbstractLogger or Psr\Log\NullLogger. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
        /** @scrutinizer ignore-call */ 
141
        $this->logger = $logger->withName(str_replace('unreal4u\\MQTT\\', '', get_class($this)));
Loading history...
141
142
        // Once we have a logger, set the clientId
143 20
        if ($clientId === null) {
144 6
            $clientId = new ClientId('');
145
        }
146 20
        $this->setClientId($clientId);
147 20
        $this->setProtocolVersion(new ProtocolVersion(self::DEFAULT_PROTOCOL_VERSION));
148
        // Set 1883 as the default port on a non-secured channel
149 20
        $this->setBrokerPort(new BrokerPort(1883, 'tcp'));
150
151 20
        $this->host = $host;
152 20
    }
153
154
    /**
155
     * Use this function to change the default broker port
156
     *
157
     * @param BrokerPort $brokerPort
158
     * @return Parameters
159
     */
160 20
    public function setBrokerPort(BrokerPort $brokerPort): self
161
    {
162 20
        $this->brokerPort = $brokerPort;
163 20
        return $this;
164
    }
165
166 20
    public function setProtocolVersion(ProtocolVersion $protocolVersion): self
167
    {
168 20
        $this->protocolVersion = $protocolVersion;
169 20
        return $this;
170
    }
171
172 2
    public function getProtocolVersionBinaryRepresentation(): string
173
    {
174 2
        return $this->protocolVersion->getProtocolVersionBinaryRepresentation();
175
    }
176
177
    /**
178
     * Handles everything related to setting the ClientId
179
     *
180
     * @param ClientId $clientId
181
     * @return Parameters
182
     */
183 20
    public function setClientId(ClientId $clientId): self
184
    {
185 20
        $this->clientId = $clientId;
186 20
        $this->logger->debug('Set clientId', ['actualClientString' => (string)$clientId]);
187 20
        if ($this->clientId->isEmptyClientId()) {
188 6
            $this->logger->debug('Empty clientId detected, forcing clean session bit to true');
189 6
            $this->setCleanSession(true);
190
        }
191
192 20
        return $this;
193
    }
194
195 4
    public function getClientId(): ClientId
196
    {
197 4
        return $this->clientId;
198
    }
199
200
    /**
201
     * Returns the connection string
202
     *
203
     * @return string
204
     */
205 3
    public function getConnectionUrl(): string
206
    {
207 3
        return sprintf(
208 3
            '%s://%s:%d',
209 3
            $this->brokerPort->getTransmissionProtocol(),
210 3
            $this->host,
211 3
            $this->brokerPort->getBrokerPort()
212
        );
213
    }
214
215
    /**
216
     * Returns the set of flags we are making the connection with
217
     *
218
     * @return int
219
     */
220 12
    public function getFlags(): int
221
    {
222 12
        return (int)$this->bitFlag;
223
    }
224
225
    /**
226
     * Keep alive period is measured in positive seconds. The maximum is 18h, 12m and 15s, equivalent to 65535 seconds
227
     *
228
     * @param int $keepAlivePeriod
229
     * @return Parameters
230
     * @throws InvalidArgumentException
231
     */
232 2
    public function setKeepAlivePeriod(int $keepAlivePeriod): self
233
    {
234 2
        if ($keepAlivePeriod > 65535 || $keepAlivePeriod < 0) {
235 1
            $this->logger->error('Keep alive period must be between 0 and 65535');
236 1
            throw new InvalidArgumentException('Keep alive period must be between 0 and 65535');
237
        }
238
239 1
        $this->keepAlivePeriod = $keepAlivePeriod;
240 1
        return $this;
241
    }
242
243
    /**
244
     * Sets the 6th and 7th bit of the connect flag
245
     *
246
     * @param string $username
247
     * @param string $password
248
     * @return Parameters
249
     */
250 4
    public function setCredentials(string $username, string $password): self
251
    {
252 4
        $this->bitFlag &= ~64;
253 4
        $this->bitFlag &= ~128;
254
255 4
        if ($username !== '') {
256 3
            $this->logger->debug('Username set, setting username flag');
257 3
            $this->bitFlag |= 128;
258 3
            $this->username = $username;
259
        }
260
261 4
        if ($password !== '') {
262 4
            $this->logger->debug('Password set, setting password flag');
263 4
            $this->bitFlag |= 64;
264 4
            $this->password = $password;
265
        }
266
267 4
        return $this;
268
    }
269
270
    /**
271
     * Sets the 5th bit of the connect flag
272
     *
273
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
274
     * @param bool $willRetain
275
     * @return Parameters
276
     */
277 6
    private function setWillRetainBit(bool $willRetain): self
278
    {
279 6
        $this->bitFlag &= ~32;
280 6
        if ($willRetain === true) {
281 1
            $this->logger->debug('Setting will retain flag');
282 1
            $this->bitFlag |= 32;
283
        }
284 6
        return $this;
285
    }
286
287
    /**
288
     * Determines and sets the 3rd and 4th bits of the connect flag
289
     *
290
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
291
     * @param int $QoSLevel
292
     * @return Parameters
293
     */
294 6
    private function setWillQoSLevelBit(int $QoSLevel): self
295
    {
296
        // Reset first the will QoS bits and proceed to set them
297 6
        $this->bitFlag &= ~8; // Third bit: 8
298 6
        $this->bitFlag &= ~16; // Fourth bit: 16
299
300 6
        if ($QoSLevel !== 0) {
301 2
            $this->logger->debug(sprintf(
302 2
                'Setting will QoS level %d flag (bit %d)',
303 2
                $QoSLevel,
304 2
                $QoSLevel * 8
305
            ));
306
307 2
            $this->bitFlag |= ($QoSLevel * 8);
308
        }
309
310 6
        return $this;
311
    }
312
313
    /**
314
     * Sets the given will. Will also set the 2nd bit of the connect flags if a message is provided
315
     *
316
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
317
     * @param Message $message
318
     * @return Parameters
319
     * @throws InvalidQoSLevel
320
     * @throws MissingTopicName
321
     * @throws MessageTooBig
322
     */
323 6
    public function setWill(Message $message): self
324
    {
325
        // Proceed only if we have a valid message
326 6
        $this->bitFlag &= ~4;
327 6
        if ($message->getTopicName() !== '') {
328 6
            $this->logger->debug('Setting will flag');
329 6
            $this->bitFlag |= 4;
330
        }
331
332 6
        $this->will = $message;
333
        $this
334 6
            ->setWillRetainBit($message->isRetained())
335 6
            ->setWillQoSLevelBit($message->getQoSLevel());
336
337 6
        return $this;
338
    }
339
340
    /**
341
     * Sets the 1st bit of the connect flags
342
     *
343
     * @param bool $cleanSession
344
     * @return Parameters
345
     */
346 8
    public function setCleanSession(bool $cleanSession): self
347
    {
348 8
        $this->bitFlag &= ~2;
349 8
        if ($cleanSession === true) {
350 8
            $this->logger->debug('Clean session flag set');
351 8
            $this->bitFlag |= 2;
352
        }
353 8
        $this->cleanSession = $cleanSession;
354 8
        return $this;
355
    }
356
357
    /**
358
     * @return int
359
     */
360 3
    public function getKeepAlivePeriod(): int
361
    {
362 3
        return $this->keepAlivePeriod;
363
    }
364
365
    /**
366
     * @return bool
367
     */
368 2
    public function getCleanSession(): bool
369
    {
370 2
        return $this->cleanSession;
371
    }
372
373
    /**
374
     * @return string
375
     */
376 4
    public function getUsername(): string
377
    {
378 4
        return $this->username;
379
    }
380
381
    /**
382
     * @return string
383
     */
384 4
    public function getPassword(): string
385
    {
386 4
        return $this->password;
387
    }
388
389
    /**
390
     * @return string
391
     */
392 3
    public function getWillTopic(): string
393
    {
394 3
        if ($this->will === null) {
395 2
            return '';
396
        }
397
398 1
        return $this->will->getTopicName();
399
    }
400
401
    /**
402
     * @return string
403
     */
404 3
    public function getWillMessage(): string
405
    {
406 3
        if ($this->will === null) {
407 2
            return '';
408
        }
409
410 1
        return $this->will->getPayload();
411
    }
412
413
    /**
414
     * @return bool
415
     */
416 2
    public function getWillRetain(): bool
417
    {
418 2
        return $this->will->isRetained();
419
    }
420
}
421