Completed
Push — master ( b2b084...94f4ef )
by Camilo
02:18
created

WritableContent::setProtocolVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace unreal4u\MQTT\Internals;
6
7
use Psr\Log\LoggerInterface;
8
use unreal4u\MQTT\DataTypes\ProtocolVersion;
9
use unreal4u\MQTT\Exceptions\MessageTooBig;
10
use unreal4u\MQTT\Utilities;
11
12
/**
13
 * Trait WritableContent
14
 * @package unreal4u\MQTT\Internals
15
 */
16
trait WritableContent
17
{
18
    /**
19
     * @var LoggerInterface
20
     */
21
    protected $logger;
22
23
    /**
24
     * Any special flags that are set on runtime
25
     *
26
     * PUBLISH for example needs to know QoS, the retain bit and duplicate delivery settings
27
     * PUBREL, SUBSCRIBE and UNSUBSCRIBE has always bit 1 set to true
28
     *
29
     * @var int
30
     */
31
    protected $specialFlags = 0;
32
33
    /**
34
     * The protocol version we are talking with. Currently only v3.1.1 is supported
35
     * @var string
36
     */
37
    public $protocolLevel = '3.1.1';
38
39
    /**
40
     * @var ProtocolVersion
41
     */
42
    private $protocolVersion;
43
44
    /**
45
     * Returns the fixed header part needed for all methods
46
     *
47
     * This takes into account the basic control packet value, any special flags and, in the second byte, the variable
48
     * header length
49
     *
50
     * @param int $variableHeaderLength
51
     * @return string
52
     * @throws \unreal4u\MQTT\Exceptions\MessageTooBig
53
     */
54 3
    final public function createFixedHeader(int $variableHeaderLength): string
55
    {
56 3
        $this->logger->debug('Creating fixed header with values', [
57 3
            'controlPacketValue' => static::CONTROL_PACKET_VALUE,
0 ignored issues
show
Bug introduced by
The constant unreal4u\MQTT\Internals\...t::CONTROL_PACKET_VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
58 3
            'specialFlags' => $this->specialFlags,
59 3
            'variableHeaderLength' => $variableHeaderLength,
60 3
            'composed' => decbin(\chr((static::CONTROL_PACKET_VALUE << 4) | $this->specialFlags)),
0 ignored issues
show
Bug introduced by
chr(static::CONTROL_PACK... | $this->specialFlags) of type string is incompatible with the type integer expected by parameter $number of decbin(). ( Ignorable by Annotation )

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

60
            'composed' => decbin(/** @scrutinizer ignore-type */ \chr((static::CONTROL_PACKET_VALUE << 4) | $this->specialFlags)),
Loading history...
61
        ]);
62
63
        // Binary OR is safe to do because the first 4 bits are always 0 after shifting
64
        return
65 3
            \chr((static::CONTROL_PACKET_VALUE << 4) | $this->specialFlags) .
66 3
            $this->getRemainingLength($variableHeaderLength);
67
    }
68
69
    /**
70
     * Returns the correct format for the length in bytes of the remaining bytes
71
     *
72
     * @param int $lengthInBytes
73
     * @return string
74
     * @throws \unreal4u\MQTT\Exceptions\MessageTooBig
75
     */
76 3
    final public function getRemainingLength(int $lengthInBytes): string
77
    {
78 3
        if ($lengthInBytes > 268435455) {
79
            throw new MessageTooBig('The message cannot exceed 268435455 bytes in length');
80
        }
81
82 3
        $x = $lengthInBytes;
83 3
        $outputString = '';
84
        do {
85 3
            $encodedByte = $x % 128;
86 3
            $x >>= 7; // Shift 7 bytes
87
            // if there are more data to encode, set the top bit of this byte
88 3
            if ($x > 0) {
89
                $encodedByte |= 128;
90
            }
91 3
            $outputString .= \chr($encodedByte);
92 3
        } while ($x > 0);
93
94 3
        return $outputString;
95
    }
96
97
    /**
98
     * Creates the entire message
99
     * @return string
100
     * @throws \unreal4u\MQTT\Exceptions\MessageTooBig
101
     */
102 2
    final public function createSendableMessage(): string
103
    {
104 2
        $variableHeader = $this->createVariableHeader();
105 2
        $this->logger->debug('Created variable header', ['variableHeader' => base64_encode($variableHeader)]);
106 2
        $payload = $this->createPayload();
107 2
        $this->logger->debug('Created payload', ['payload' => base64_encode($payload)]);
108 2
        $fixedHeader = $this->createFixedHeader(\strlen($variableHeader . $payload));
109 2
        $this->logger->debug('Created fixed header', ['fixedHeader' => base64_encode($fixedHeader)]);
110
111 2
        return $fixedHeader . $variableHeader . $payload;
112
    }
113
114
    /**
115
     * Creates the variable header that each method has
116
     *
117
     * @return string
118
     */
119
    abstract public function createVariableHeader(): string;
120
121
    /**
122
     * Creates the actual payload to be sent
123
     *
124
     * @return string
125
     */
126
    abstract public function createPayload(): string;
127
128
    final public function setProtocolVersion(ProtocolVersion $protocolVersion): self
129
    {
130
        $this->protocolVersion = $protocolVersion;
131
        return $this;
132
    }
133
134
    /**
135
     * Creates a UTF8 big-endian representation of the given string
136
     *
137
     * @param string $data
138
     * @return string
139
     * @throws \OutOfRangeException
140
     */
141 9
    final public function createUTF8String(string $data): string
142
    {
143 9
        $returnString = '';
144 9
        if ($data !== '') {
145 8
            $returnString = Utilities::convertNumberToBinaryString(\strlen($data)) . $data;
146
        }
147
148 9
        return $returnString;
149
    }
150
151
    /**
152
     * Will return an object of the type the broker has returned to us
153
     *
154
     * @param string $data
155
     * @param ClientInterface $client
156
     *
157
     * @return ReadableContentInterface
158
     * @throws \DomainException
159
     */
160
    public function expectAnswer(string $data, ClientInterface $client): ReadableContentInterface
161
    {
162
        $this->logger->info('String of incoming data confirmed, returning new object', ['callee' => \get_class($this)]);
163
164
        $eventManager = new EventManager($this->logger);
165
        return $eventManager->analyzeHeaders($data, $client);
166
    }
167
168
    /**
169
     * Gets the control packet value for this object
170
     *
171
     * @return int
172
     */
173
    final public static function getControlPacketValue(): int
174
    {
175
        return static::CONTROL_PACKET_VALUE;
0 ignored issues
show
Bug introduced by
The constant unreal4u\MQTT\Internals\...t::CONTROL_PACKET_VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
176
    }
177
}
178