BinaryDataReader   C
last analyzed

Complexity

Total Complexity 56

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 114
dl 0
loc 303
ccs 134
cts 134
cp 1
rs 5.5199
c 0
b 0
f 0
wmc 56

35 Methods

Rating   Name   Duplication   Size   Complexity  
A pack64bit() 0 5 1
A __construct() 0 3 1
A readLengthString() 0 3 1
A readInt64() 0 5 1
A readUInt48() 0 5 1
A readCodedBinary() 0 17 5
B readUIntBySize() 0 25 8
A readUInt32() 0 3 1
A readUInt8() 0 3 1
A advance() 0 3 1
A readUInt64() 0 3 1
A readUInt24() 0 5 1
A readUInt40() 0 6 1
A readIntBeBySize() 0 19 6
A readInt24() 0 10 2
A readUInt16() 0 3 1
A readInt16() 0 3 1
A unread() 0 4 1
A readUInt56() 0 7 1
A unpackUInt64() 0 5 1
A read() 0 7 1
A isComplete() 0 3 1
A readInt32Be() 0 4 2
A getReadBytes() 0 3 1
A readDouble() 0 3 1
A readTableId() 0 3 1
A getBinaryDataLength() 0 3 1
A getBinarySlice() 0 6 1
A readInt32() 0 3 1
A readInt40Be() 0 6 1
A getData() 0 3 1
A readFloat() 0 3 1
A readInt24Be() 0 5 2
A readInt8() 0 4 2
A readInt16Be() 0 4 2

How to fix   Complexity   

Complex Class

Complex classes like BinaryDataReader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BinaryDataReader, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
4
namespace MySQLReplication\BinaryDataReader;
5
6
class BinaryDataReader
7
{
8
    public const NULL_COLUMN = 251;
9
    public const UNSIGNED_CHAR_COLUMN = 251;
10
    public const UNSIGNED_SHORT_COLUMN = 252;
11
    public const UNSIGNED_INT24_COLUMN = 253;
12
    public const UNSIGNED_INT64_COLUMN = 254;
13
    public const UNSIGNED_CHAR_LENGTH = 1;
14
    public const UNSIGNED_SHORT_LENGTH = 2;
15
    public const UNSIGNED_INT24_LENGTH = 3;
16
    public const UNSIGNED_INT32_LENGTH = 4;
17
    public const UNSIGNED_FLOAT_LENGTH = 4;
18
    public const UNSIGNED_DOUBLE_LENGTH = 8;
19
    public const UNSIGNED_INT40_LENGTH = 5;
20
    public const UNSIGNED_INT48_LENGTH = 6;
21
    public const UNSIGNED_INT56_LENGTH = 7;
22
    public const UNSIGNED_INT64_LENGTH = 8;
23
24
    private $data;
25
26
    /**
27
     * @var int
28
     */
29
    private $readBytes = 0;
30
31 88
    public function __construct(string $data)
32
    {
33 88
        $this->data = $data;
34 88
    }
35
36 3
    public static function pack64bit(int $value): string
37
    {
38 3
        return pack(
39 3
            'C8', ($value >> 0) & 0xFF, ($value >> 8) & 0xFF, ($value >> 16) & 0xFF, ($value >> 24) & 0xFF,
40 3
            ($value >> 32) & 0xFF, ($value >> 40) & 0xFF, ($value >> 48) & 0xFF, ($value >> 56) & 0xFF
41
        );
42
    }
43
44 60
    public function advance(int $length): void
45
    {
46 60
        $this->read($length);
47 60
    }
48
49 2
    public function readInt16(): int
50
    {
51 2
        return unpack('s', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
52
    }
53
54 85
    public function read(int $length): string
55
    {
56 85
        $return = substr($this->data, 0, $length);
57 85
        $this->readBytes += $length;
58 85
        $this->data = substr($this->data, $length);
59
60 85
        return $return;
61
    }
62
63 11
    public function unread(string $data): void
64
    {
65 11
        $this->readBytes -= strlen($data);
66 11
        $this->data = $data . $this->data;
67 11
    }
68
69
    /**
70
     * @throws BinaryDataReaderException
71
     */
72 56
    public function readCodedBinary(): ?int
73
    {
74 56
        $c = ord($this->read(self::UNSIGNED_CHAR_LENGTH));
75 56
        if ($c === self::NULL_COLUMN) {
76 1
            return null;
77
        }
78 56
        if ($c < self::UNSIGNED_CHAR_COLUMN) {
79 55
            return $c;
80
        }
81 2
        if ($c === self::UNSIGNED_SHORT_COLUMN) {
82 1
            return $this->readUInt16();
83
        }
84 2
        if ($c === self::UNSIGNED_INT24_COLUMN) {
85 1
            return $this->readUInt24();
86
        }
87
88 1
        throw new BinaryDataReaderException('Column num ' . $c . ' not handled');
89
    }
90
91 60
    public function readUInt16(): int
92
    {
93 60
        return unpack('v', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
94
    }
95
96 8
    public function readUInt24(): int
97
    {
98 8
        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
99
100 8
        return $data[1] + ($data[2] << 8) + ($data[3] << 16);
101
    }
102
103 6
    public function readUInt64(): string
104
    {
105 6
        return $this->unpackUInt64($this->read(self::UNSIGNED_INT64_LENGTH));
106
    }
107
108 57
    public function unpackUInt64(string $binary): string
109
    {
110 57
        $data = unpack('V*', $binary);
111
112 57
        return bcadd((string)$data[1], bcmul((string)$data[2], bcpow('2', '32')));
113
    }
114
115 2
    public function readInt24(): int
116
    {
117 2
        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
118
119 2
        $res = $data[1] | ($data[2] << 8) | ($data[3] << 16);
120 2
        if ($res >= 0x800000) {
121 2
            $res -= 0x1000000;
122
        }
123
124 2
        return $res;
125
    }
126
127 3
    public function readInt64(): string
128
    {
129 3
        $data = unpack('V*', $this->read(self::UNSIGNED_INT64_LENGTH));
130
131 3
        return bcadd((string)$data[1], (string)($data[2] << 32));
132
    }
133
134
    /**
135
     * @throws BinaryDataReaderException
136
     */
137 14
    public function readLengthString(int $size): string
138
    {
139 14
        return $this->read($this->readUIntBySize($size));
140
    }
141
142
    /**
143
     * @throws BinaryDataReaderException
144
     */
145 24
    public function readUIntBySize(int $size): int
146
    {
147 24
        if ($size === self::UNSIGNED_CHAR_LENGTH) {
148 12
            return $this->readUInt8();
149
        }
150 12
        if ($size === self::UNSIGNED_SHORT_LENGTH) {
151 3
            return $this->readUInt16();
152
        }
153 9
        if ($size === self::UNSIGNED_INT24_LENGTH) {
154 2
            return $this->readUInt24();
155
        }
156 7
        if ($size === self::UNSIGNED_INT32_LENGTH) {
157 3
            return $this->readUInt32();
158
        }
159 4
        if ($size === self::UNSIGNED_INT40_LENGTH) {
160 1
            return $this->readUInt40();
161
        }
162 3
        if ($size === self::UNSIGNED_INT48_LENGTH) {
163 1
            return $this->readUInt48();
164
        }
165 2
        if ($size === self::UNSIGNED_INT56_LENGTH) {
166 1
            return $this->readUInt56();
167
        }
168
169 1
        throw new BinaryDataReaderException('$size ' . $size . ' not handled');
170
    }
171
172 60
    public function readUInt8(): int
173
    {
174 60
        return unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
175
    }
176
177 59
    public function readUInt32(): int
178
    {
179 59
        return unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
180
    }
181
182 1
    public function readUInt40(): int
183
    {
184 1
        $data1 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
185 1
        $data2 = unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
186
187 1
        return $data1 + ($data2 << 8);
188
    }
189
190 1
    public function readUInt48(): int
191
    {
192 1
        $data = unpack('v3', $this->read(self::UNSIGNED_INT48_LENGTH));
193
194 1
        return $data[1] + ($data[2] << 16) + ($data[3] << 32);
195
    }
196
197 1
    public function readUInt56(): int
198
    {
199 1
        $data1 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
200 1
        $data2 = unpack('S', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
201 1
        $data3 = unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
202
203 1
        return $data1 + ($data2 << 8) + ($data3 << 24);
204
    }
205
206
    /**
207
     * @throws BinaryDataReaderException
208
     */
209 21
    public function readIntBeBySize(int $size): int
210
    {
211 21
        if ($size === self::UNSIGNED_CHAR_LENGTH) {
212 7
            return $this->readInt8();
213
        }
214 16
        if ($size === self::UNSIGNED_SHORT_LENGTH) {
215 3
            return $this->readInt16Be();
216
        }
217 15
        if ($size === self::UNSIGNED_INT24_LENGTH) {
218 7
            return $this->readInt24Be();
219
        }
220 8
        if ($size === self::UNSIGNED_INT32_LENGTH) {
221 2
            return $this->readInt32Be();
222
        }
223 6
        if ($size === self::UNSIGNED_INT40_LENGTH) {
224 5
            return $this->readInt40Be();
225
        }
226
227 1
        throw new BinaryDataReaderException('$size ' . $size . ' not handled');
228
    }
229
230 11
    public function readInt8(): int
231
    {
232 11
        $re = unpack('c', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
233 11
        return $re >= 0x80 ? $re - 0x100 : $re;
234
    }
235
236 3
    public function readInt16Be(): int
237
    {
238 3
        $re = unpack('n', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
239 3
        return $re >= 0x8000 ? $re - 0x10000 : $re;
240
    }
241
242 9
    public function readInt24Be(): int
243
    {
244 9
        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
245 9
        $re = ($data[1] << 16) | ($data[2] << 8) | $data[3];
246 9
        return $re >= 0x800000 ? $re - 0x1000000 : $re;
247
    }
248
249 12
    public function readInt32Be(): int
250
    {
251 12
        $re = unpack('N', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
252 12
        return $re >= 0x80000000 ? $re - 0x100000000 : $re;
253
    }
254
255 5
    public function readInt40Be(): int
256
    {
257 5
        $data1 = unpack('N', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
258 5
        $data2 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
259
260 5
        return $data2 + ($data1 << 8);
261
    }
262
263 59
    public function readInt32(): int
264
    {
265 59
        return unpack('i', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
266
    }
267
268 2
    public function readFloat(): float
269
    {
270 2
        return unpack('f', $this->read(self::UNSIGNED_FLOAT_LENGTH))[1];
271
    }
272
273 2
    public function readDouble(): float
274
    {
275 2
        return unpack('d', $this->read(self::UNSIGNED_DOUBLE_LENGTH))[1];
276
    }
277
278 55
    public function readTableId(): string
279
    {
280 55
        return $this->unpackUInt64($this->read(self::UNSIGNED_INT48_LENGTH) . chr(0) . chr(0));
281
    }
282
283 53
    public function isComplete(int $size): bool
284
    {
285 53
        return !($this->readBytes - 20 < $size);
286
    }
287
288 1
    public function getBinaryDataLength(): int
289
    {
290 1
        return strlen($this->data);
291
    }
292
293 1
    public function getData(): string
294
    {
295 1
        return $this->data;
296
    }
297
298 6
    public function getBinarySlice(int $binary, int $start, int $size, int $binaryLength): int
299
    {
300 6
        $binary >>= $binaryLength - ($start + $size);
301 6
        $mask = ((1 << $size) - 1);
302
303 6
        return $binary & $mask;
304
    }
305
306 1
    public function getReadBytes(): int
307
    {
308 1
        return $this->readBytes;
309
    }
310
}