Passed
Push — master ( d27c94...6659fb )
by Camilo
02:22
created

WritableContent   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Test Coverage

Coverage 80%

Importance

Changes 0
Metric Value
wmc 11
dl 0
loc 144
ccs 32
cts 40
cp 0.8
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getProtocolLevel() 0 8 2
A createSendableMessage() 0 10 1
A createFixedHeader() 0 13 1
A getRemainingLength() 0 19 4
A createUTF8String() 0 3 1
A expectAnswer() 0 6 1
A getControlPacketValue() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace unreal4u\MQTT\Internals;
6
7
use Psr\Log\LoggerInterface;
8
use unreal4u\MQTT\Exceptions\MessageTooBig;
9
use unreal4u\MQTT\Utilities;
10
11
/**
12
 * Trait WritableContent
13
 * @package unreal4u\MQTT\Internals
14
 */
15
trait WritableContent
16
{
17
    /**
18
     * @var LoggerInterface
19
     */
20
    protected $logger;
21
22
    /**
23
     * Any special flags that are set on runtime
24
     *
25
     * PUBLISH for example needs to know QoS, the retain bit and duplicate delivery settings
26
     * PUBREL, SUBSCRIBE and UNSUBSCRIBE has always bit 1 set to true
27
     *
28
     * @var int
29
     */
30
    protected $specialFlags = 0;
31
32
    /**
33
     * The protocol version we are talking with. Currently only v3.1.1 is supported
34
     * @var string
35
     */
36
    public $protocolLevel = '3.1.1';
37
38
    /**
39
     * Returns the fixed header part needed for all methods
40
     *
41
     * This takes into account the basic control packet value, any special flags and, in the second byte, the variable
42
     * header length
43
     *
44
     * @param int $variableHeaderLength
45
     * @return string
46
     * @throws \unreal4u\MQTT\Exceptions\MessageTooBig
47
     */
48 2
    final public function createFixedHeader(int $variableHeaderLength): string
49
    {
50 2
        $this->logger->debug('Creating fixed header with values', [
51 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...
52 2
            'specialFlags' => $this->specialFlags,
53 2
            'variableHeaderLength' => $variableHeaderLength,
54 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

54
            'composed' => decbin(/** @scrutinizer ignore-type */ \chr((static::CONTROL_PACKET_VALUE << 4) | $this->specialFlags)),
Loading history...
55
        ]);
56
57
        // Binary OR is safe to do because the first 4 bits are always 0 after shifting
58
        return
59 2
            \chr((static::CONTROL_PACKET_VALUE << 4) | $this->specialFlags) .
60 2
            $this->getRemainingLength($variableHeaderLength);
61
    }
62
63
    /**
64
     * Returns the correct format for the length in bytes of the remaining bytes
65
     *
66
     * @param int $lengthInBytes
67
     * @return string
68
     * @throws \unreal4u\MQTT\Exceptions\MessageTooBig
69
     */
70 2
    final public function getRemainingLength(int $lengthInBytes): string
71
    {
72 2
        if ($lengthInBytes > 268435455) {
73
            throw new MessageTooBig('The message cannot exceed 268435455 bytes in length');
74
        }
75
76 2
        $x = $lengthInBytes;
77 2
        $outputString = '';
78
        do {
79 2
            $encodedByte = $x % 128;
80 2
            $x >>= 7; // Shift 7 bytes
81
            // if there are more data to encode, set the top bit of this byte
82 2
            if ($x > 0) {
83
                $encodedByte |= 128;
84
            }
85 2
            $outputString .= \chr($encodedByte);
86 2
        } while ($x > 0);
87
88 2
        return $outputString;
89
    }
90
91
    /**
92
     * Creates the entire message
93
     * @return string
94
     * @throws \unreal4u\MQTT\Exceptions\MessageTooBig
95
     */
96 1
    final public function createSendableMessage(): string
97
    {
98 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

98
        /** @scrutinizer ignore-call */ 
99
        $variableHeader = $this->createVariableHeader();
Loading history...
99 1
        $this->logger->debug('Creating variable header', ['variableHeader' => base64_encode($variableHeader)]);
100 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

100
        /** @scrutinizer ignore-call */ 
101
        $payload = $this->createPayload();
Loading history...
101 1
        $this->logger->debug('Creating payload', ['payload' => base64_encode($payload)]);
102 1
        $fixedHeader = $this->createFixedHeader(\strlen($variableHeader . $payload));
103 1
        $this->logger->debug('Created fixed header', ['fixedHeader' => base64_encode($fixedHeader)]);
104
105 1
        return $fixedHeader . $variableHeader . $payload;
106
    }
107
108
    /**
109
     * Gets the current protocol lvl bit
110
     * @return string
111
     */
112 3
    final public function getProtocolLevel(): string
113
    {
114 3
        if ($this->protocolLevel === '3.1.1') {
115 2
            return \chr(4);
116
        }
117
118
        // Return a default of 0, which will be invalid anyway (but data will be sent to the broker this way)
119 1
        return \chr(0);
120
    }
121
122
    /**
123
     * Creates a UTF8 big-endian representation of the given string
124
     *
125
     * @param string $data
126
     * @return string
127
     * @throws \OutOfRangeException
128
     */
129 8
    final public function createUTF8String(string $data): string
130
    {
131 8
        return Utilities::convertNumberToBinaryString(\strlen($data)) . $data;
132
    }
133
134
    /**
135
     * Will return an object of the type the broker has returned to us
136
     *
137
     * @param string $data
138
     * @param ClientInterface $client
139
     *
140
     * @return ReadableContentInterface
141
     * @throws \DomainException
142
     */
143
    public function expectAnswer(string $data, ClientInterface $client): ReadableContentInterface
144
    {
145
        $this->logger->info('String of incoming data confirmed, returning new object', ['callee' => \get_class($this)]);
146
147
        $eventManager = new EventManager($this->logger);
148
        return $eventManager->analyzeHeaders($data, $client);
149
    }
150
151
    /**
152
     * Gets the control packet value for this object
153
     *
154
     * @return int
155
     */
156
    final public static function getControlPacketValue(): int
157
    {
158
        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...
159
    }
160
}
161