Completed
Push — master ( 325ba3...57475a )
by Camilo
98:41 queued 96:24
created

Parameters::setCleanSession()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

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

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