Completed
Push — master ( bb30b8...368962 )
by Camilo
02:11
created

Unsubscribe::getPacketIdentifier()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace unreal4u\MQTT\Protocol;
6
7
use unreal4u\MQTT\Application\Topic;
8
use unreal4u\MQTT\Exceptions\MustContainTopic;
9
use unreal4u\MQTT\Internals\ProtocolBase;
10
use unreal4u\MQTT\Internals\WritableContent;
11
use unreal4u\MQTT\Internals\WritableContentInterface;
12
use unreal4u\MQTT\Utilities;
13
14
final class Unsubscribe extends ProtocolBase implements WritableContentInterface
15
{
16
    use WritableContent;
17
18
    const CONTROL_PACKET_VALUE = 10;
19
20
    private $packetIdentifier = 0;
21
22
    /**
23
     * An array of topics on which unsubscribe to
24
     * @var Topic[]
25
     */
26
    private $topics = [];
27
28
    /**
29
     * @return string
30
     * @throws \unreal4u\MQTT\Exceptions\MustContainTopic
31
     * @throws \OutOfRangeException
32
     * @throws \Exception
33
     */
34
    public function createVariableHeader(): string
35
    {
36
        if (count($this->topics) === 0) {
37
            throw new MustContainTopic('An unsubscribe command must contain at least one topic');
38
        }
39
40
        // Unsubscribe must always send a 2 flag
41
        $this->specialFlags = 2;
42
43
        // Assign a packet identifier automatically if none has been assigned yet
44
        if ($this->packetIdentifier === 0) {
45
            $this->setPacketIdentifier(random_int(0, 65535));
46
        }
47
48
        return Utilities::convertNumberToBinaryString($this->packetIdentifier);
49
    }
50
51
    public function createPayload(): string
52
    {
53
        $output = '';
54
        foreach ($this->topics as $topic) {
55
            // chr on QoS level is safe because it will create an 8-bit flag where the first 6 are only 0's
56
            $output .= $this->createUTF8String($topic->getTopicName());
57
        }
58
        return $output;
59
    }
60
61
    /**
62
     * When the Server receives a SUBSCRIBE Packet from a Client, the Server MUST respond with a SUBACK Packet
63
     *
64
     * This can however not be in the same order, as we may be able to receive PUBLISH packets before getting a SUBACK
65
     * back
66
     *
67
     * @see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718134 (MQTT-3.8.4-1)
68
     * @return bool
69
     */
70
    public function shouldExpectAnswer(): bool
71
    {
72
        return true;
73
    }
74
75
    /**
76
     * SUBSCRIBE Control Packets MUST contain a non-zero 16-bit Packet Identifier
77
     *
78
     * @param int $packetIdentifier
79
     * @return Unsubscribe
80
     * @throws \OutOfRangeException
81
     */
82
    public function setPacketIdentifier(int $packetIdentifier): self
83
    {
84
        if ($packetIdentifier > 65535 || $packetIdentifier < 1) {
85
            throw new \OutOfRangeException('Packet identifier must fit within 2 bytes');
86
        }
87
88
        $this->packetIdentifier = $packetIdentifier;
89
        $this->logger->debug('Setting packet identifier', ['current' => $this->packetIdentifier]);
90
91
        return $this;
92
    }
93
94
    public function getPacketIdentifier(): int
95
    {
96
        return $this->packetIdentifier;
97
    }
98
99
    /**
100
     * A subscription is based on filters, this function allows us to pass on filters
101
     *
102
     * @param Topic[] $topics
103
     * @return Unsubscribe
104
     */
105
    public function addTopics(Topic ...$topics): self
106
    {
107
        $this->topics = $topics;
108
        $this->logger->debug('Topics added', ['totalTopics', count($this->topics)]);
109
110
        return $this;
111
    }
112
}
113