Completed
Push — master ( f445a9...55498a )
by Camilo
02:27
created

Parameters::setProtocolVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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

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

147
        /** @scrutinizer ignore-call */ 
148
        $this->logger = $logger->withName(str_replace('unreal4u\\MQTT\\', '', \get_class($this)));
Loading history...
148
149
        // Once we have a logger, set the clientId
150 22
        $this->setClientId($clientId);
151 22
        $this->setProtocolVersion(new ProtocolVersion(self::DEFAULT_PROTOCOL_VERSION));
152
153 22
        $this->host = $host;
154 22
    }
155
156 22
    public function setProtocolVersion(ProtocolVersion $protocolVersion): self
157
    {
158 22
        $this->protocolVersion = $protocolVersion;
159 22
        return $this;
160
    }
161
162 1
    public function getProtocolVersionBinaryRepresentation(): string
163
    {
164 1
        return $this->protocolVersion->getProtocolVersionBinaryRepresentation();
165
    }
166
167
    /**
168
     * Handles everything related to setting the ClientId
169
     *
170
     * @param string $clientId
171
     * @return Parameters
172
     */
173 22
    public function setClientId(string $clientId = ''): self
174
    {
175 22
        if ($clientId !== '') {
176 8
            $this->clientId = $clientId;
177 8
            $clientIdSize = \strlen($this->clientId);
178 8
            $utf8ClientIdSize = \mb_strlen($this->clientId);
179
180 8
            if ($clientIdSize !== $utf8ClientIdSize) {
181 1
                $this->logger->warning('The broker MAY reject the connection because of invalid characters');
182
            }
183
184 8
            if ($utf8ClientIdSize > 23) {
185 8
                $this->logger->warning('The broker MAY reject the connection because the ClientId is too long');
186
            }
187
        } else {
188
            /*
189
             * If you ever wind up in this situation, search for MQTT-3.1.3-7 on the following document for more
190
             * information: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718067
191
             */
192 14
            $this->logger->warning('ClientId size is 0 bytes. This has several implications, check comments', [
193 14
                'file' => __FILE__,
194
                'line' => __LINE__,
195
            ]);
196 14
            $this->cleanSession = true;
197
        }
198
199 22
        return $this;
200
    }
201
202 4
    public function getClientId(): string
203
    {
204 4
        return $this->clientId;
205
    }
206
207
    /**
208
     * Returns the connection string
209
     *
210
     * @TODO Currently only TCP connections supported, SSL will come
211
     *
212
     * @return string
213
     */
214 2
    public function getConnectionUrl(): string
215
    {
216 2
        return 'tcp://' . $this->host . ':' . $this->port;
217
    }
218
219
    /**
220
     * Returns the set of flags we are making the connection with
221
     *
222
     * @return int
223
     */
224 14
    public function getFlags(): int
225
    {
226 14
        return (int)$this->bitFlag;
227
    }
228
229
    /**
230
     * Keep alive period is measured in positive seconds. The maximum is 18h, 12m and 15s, equivalent to 65535 seconds
231
     *
232
     * @param int $keepAlivePeriod
233
     * @return Parameters
234
     * @throws \InvalidArgumentException
235
     */
236 2
    public function setKeepAlivePeriod(int $keepAlivePeriod): Parameters
237
    {
238 2
        if ($keepAlivePeriod > 65535 || $keepAlivePeriod < 0) {
239 1
            $this->logger->error('Keep alive period must be between 0 and 65535');
240 1
            throw new \InvalidArgumentException('Keep alive period must be between 0 and 65535');
241
        }
242
243 1
        $this->keepAlivePeriod = $keepAlivePeriod;
244 1
        return $this;
245
    }
246
247
    /**
248
     * Sets the 7th bit of the connect flag
249
     *
250
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
251
     * @param string $username
252
     * @return Parameters
253
     */
254 4
    public function setUsername(string $username): Parameters
255
    {
256 4
        $this->bitFlag &= ~128;
257 4
        if ($username !== '') {
258 4
            $this->logger->debug('Username set, setting username flag');
259 4
            $this->bitFlag |= 128;
260
        }
261 4
        $this->username = $username;
262 4
        return $this;
263
    }
264
265
    /**
266
     * Sets the 6th bit of the connect flag
267
     *
268
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
269
     * @param string $password
270
     * @return Parameters
271
     */
272 5
    public function setPassword(string $password): Parameters
273
    {
274 5
        $this->bitFlag &= ~64;
275 5
        if ($password !== '') {
276 5
            $this->logger->debug('Password set, setting password flag');
277 5
            $this->bitFlag |= 64;
278
        }
279 5
        $this->password = $password;
280 5
        return $this;
281
    }
282
283
    /**
284
     * Is a private method, so can be trusted with just a string name instead of a Topic object
285
     *
286
     * @param string $willTopic
287
     * @return Parameters
288
     */
289 6
    private function setWillTopic(string $willTopic): Parameters
290
    {
291 6
        $this->willTopic = $willTopic;
292 6
        $this->logger->debug('Setting will topic');
293 6
        return $this;
294
    }
295
296
    /**
297
     * @param string $willMessage
298
     * @return Parameters
299
     */
300 6
    private function setWillMessage(string $willMessage): Parameters
301
    {
302 6
        $this->willMessage = $willMessage;
303 6
        $this->logger->debug('Setting will message');
304 6
        return $this;
305
    }
306
307
    /**
308
     * Sets the 5th bit of the connect flag
309
     *
310
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
311
     * @param bool $willRetain
312
     * @return Parameters
313
     */
314 6
    private function setWillRetain(bool $willRetain): Parameters
315
    {
316 6
        $this->bitFlag &= ~32;
317 6
        if ($willRetain === true) {
318 1
            $this->logger->debug('Setting will retain flag');
319 1
            $this->bitFlag |= 32;
320
        }
321 6
        $this->willRetain = $willRetain;
322 6
        return $this;
323
    }
324
325
    /**
326
     * Determines and sets the 3rd and 4th bits of the connect flag
327
     *
328
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
329
     * @param QoSLevel $QoSLevel
330
     * @return Parameters
331
     */
332 6
    private function setWillQoS(QoSLevel $QoSLevel): Parameters
333
    {
334
        // Reset first the will QoS bits and proceed to set them
335 6
        $this->bitFlag &= ~8; // Third bit: 8
336 6
        $this->bitFlag &= ~16; // Fourth bit: 16
337
338 6
        $this->willQoS = $QoSLevel;
339 6
        switch ($this->willQoS->getQoSLevel()) {
340 6
            case 0:
341
                // Do nothing as the relevant bits will already have been reset
342 4
                break;
343 2
            case 1:
344 1
                $this->logger->debug('Setting will QoS level 1 flag');
345 1
                $this->bitFlag |= 8;
346 1
                break;
347 1
            case 2:
348 1
                $this->logger->debug('Setting will QoS level 2 flag');
349 1
                $this->bitFlag |= 16;
350 1
                break;
351
        }
352
353 6
        return $this;
354
    }
355
356
    /**
357
     * Sets the given will. Will also set the 2nd bit of the connect flags if a message is provided
358
     *
359
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
360
     * @param Message $message
361
     * @return Parameters
362
     * @throws \unreal4u\MQTT\Exceptions\InvalidQoSLevel
363
     * @throws \unreal4u\MQTT\Exceptions\MissingTopicName
364
     * @throws \unreal4u\MQTT\Exceptions\MessageTooBig
365
     */
366 6
    public function setWill(Message $message): Parameters
367
    {
368
        // Proceed only if we have a valid message
369 6
        if ($message->validateMessage()) {
370 6
            $this->bitFlag &= ~4;
371 6
            if ($message->getTopicName() !== '') {
372 6
                $this->logger->debug('Setting will flag');
373 6
                $this->bitFlag |= 4;
374
            }
375
376
            $this
377 6
                ->setWillMessage($message->getPayload())
378 6
                ->setWillRetain($message->isRetained())
379 6
                ->setWillTopic($message->getTopicName())
380 6
                ->setWillQoS(new QoSLevel($message->getQoSLevel()));
381
        }
382
383 6
        return $this;
384
    }
385
386
    /**
387
     * Sets the 1st bit of the connect flags
388
     *
389
     * @param bool $cleanSession
390
     * @return Parameters
391
     */
392 3
    public function setCleanSession(bool $cleanSession): Parameters
393
    {
394 3
        $this->bitFlag &= ~2;
395 3
        if ($cleanSession === true) {
396 3
            $this->logger->debug('Clean session flag set');
397 3
            $this->bitFlag |= 2;
398
        }
399 3
        $this->cleanSession = $cleanSession;
400 3
        return $this;
401
    }
402
403
    /**
404
     * @return int
405
     */
406 1
    public function getKeepAlivePeriod(): int
407
    {
408 1
        return $this->keepAlivePeriod;
409
    }
410
411
    /**
412
     * @return bool
413
     */
414 2
    public function getCleanSession(): bool
415
    {
416 2
        return $this->cleanSession;
417
    }
418
419
    /**
420
     * @return string
421
     */
422 5
    public function getUsername(): string
423
    {
424 5
        return $this->username;
425
    }
426
427
    /**
428
     * @return string
429
     */
430 5
    public function getPassword(): string
431
    {
432 5
        return $this->password;
433
    }
434
435
    /**
436
     * @return string
437
     */
438 3
    public function getWillTopic(): string
439
    {
440 3
        return $this->willTopic;
441
    }
442
443
    /**
444
     * @return string
445
     */
446 3
    public function getWillMessage(): string
447
    {
448 3
        return $this->willMessage;
449
    }
450
451
    /**
452
     * @return bool
453
     */
454 2
    public function getWillRetain(): bool
455
    {
456 2
        return $this->willRetain;
457
    }
458
}
459