Passed
Push — master ( ec7bc7...bfdde1 )
by Camilo
02:25
created

Parameters::setClientId()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4.0072

Importance

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