Completed
Push — master ( accf18...c40548 )
by Camilo
02:13
created

WritableContent::getRemainingLength()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4.074

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 10
cts 12
cp 0.8333
rs 9.2
c 0
b 0
f 0
cc 4
eloc 12
nc 3
nop 1
crap 4.074
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 2
    final public function createFixedHeader(int $variableHeaderLength): string
55
    {
56 2
        $this->logger->debug('Creating fixed header with values', [
57 2
            '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 2
            'specialFlags' => $this->specialFlags,
59 2
            'variableHeaderLength' => $variableHeaderLength,
60 2
            '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 2
            \chr((static::CONTROL_PACKET_VALUE << 4) | $this->specialFlags) .
66 2
            $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 2
    final public function getRemainingLength(int $lengthInBytes): string
77
    {
78 2
        if ($lengthInBytes > 268435455) {
79
            throw new MessageTooBig('The message cannot exceed 268435455 bytes in length');
80
        }
81
82 2
        $x = $lengthInBytes;
83 2
        $outputString = '';
84
        do {
85 2
            $encodedByte = $x % 128;
86 2
            $x >>= 7; // Shift 7 bytes
87
            // if there are more data to encode, set the top bit of this byte
88 2
            if ($x > 0) {
89
                $encodedByte |= 128;
90
            }
91 2
            $outputString .= \chr($encodedByte);
92 2
        } while ($x > 0);
93
94 2
        return $outputString;
95
    }
96
97
    /**
98
     * Creates the entire message
99
     * @return string
100
     * @throws \unreal4u\MQTT\Exceptions\MessageTooBig
101
     */
102 1
    final public function createSendableMessage(): string
103
    {
104 1
        $variableHeader = $this->createVariableHeader();
0 ignored issues
show
Bug introduced by
It seems like createVariableHeader() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

104
        /** @scrutinizer ignore-call */ 
105
        $variableHeader = $this->createVariableHeader();
Loading history...
105 1
        $this->logger->debug('Created variable header', ['variableHeader' => base64_encode($variableHeader)]);
106 1
        $payload = $this->createPayload();
0 ignored issues
show
Bug introduced by
It seems like createPayload() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

106
        /** @scrutinizer ignore-call */ 
107
        $payload = $this->createPayload();
Loading history...
107 1
        $this->logger->debug('Created payload', ['payload' => base64_encode($payload)]);
108 1
        $fixedHeader = $this->createFixedHeader(\strlen($variableHeader . $payload));
109 1
        $this->logger->debug('Created fixed header', ['fixedHeader' => base64_encode($fixedHeader)]);
110
111 1
        return $fixedHeader . $variableHeader . $payload;
112
    }
113
114
    final public function setProtocolVersion(ProtocolVersion $protocolVersion): self
115
    {
116
        $this->protocolVersion = $protocolVersion;
117
        return $this;
118
    }
119
120
    /**
121
     * Gets the current protocol lvl bit
122
     * @return string
123
     */
124 1
    final public function getProtocolVersion(): string
125
    {
126 1
        if ($this->protocolVersion->getProtocolVersion() === '3.1.1') {
127 1
            return \chr(4);
128
        }
129
130
        // Return a default of 0, which will be invalid anyway (but data will be sent to the broker this way)
131
        return \chr(0);
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 8
    final public function createUTF8String(string $data): string
142
    {
143 8
        return Utilities::convertNumberToBinaryString(\strlen($data)) . $data;
144
    }
145
146
    /**
147
     * Will return an object of the type the broker has returned to us
148
     *
149
     * @param string $data
150
     * @param ClientInterface $client
151
     *
152
     * @return ReadableContentInterface
153
     * @throws \DomainException
154
     */
155
    public function expectAnswer(string $data, ClientInterface $client): ReadableContentInterface
156
    {
157
        $this->logger->info('String of incoming data confirmed, returning new object', ['callee' => \get_class($this)]);
158
159
        $eventManager = new EventManager($this->logger);
160
        return $eventManager->analyzeHeaders($data, $client);
161
    }
162
163
    /**
164
     * Gets the control packet value for this object
165
     *
166
     * @return int
167
     */
168
    final public static function getControlPacketValue(): int
169
    {
170
        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...
171
    }
172
}
173