ByteBuffer   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Test Coverage

Coverage 97.37%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 59
c 1
b 0
f 0
dl 0
loc 189
ccs 74
cts 76
cp 0.9737
rs 8.96
wmc 43

20 Methods

Rating   Name   Duplication   Size   Complexity  
A fromHex() 0 7 2
A randomBuffer() 0 3 1
A getHalfFloatVal() 0 17 5
A getUint16Val() 0 6 3
A __unserialize() 0 4 1
A getHex() 0 3 1
A getUint32Val() 0 11 4
A getDoubleVal() 0 6 3
A __serialize() 0 3 1
A __construct() 0 4 1
A getUint64Val() 0 16 5
A fromBase64Url() 0 3 1
A getLength() 0 3 1
A getBase64Url() 0 3 1
A getFloatVal() 0 6 3
A isEmpty() 0 3 1
A equals() 0 3 1
A getBinaryString() 0 3 1
A getByteVal() 0 6 3
A getBytes() 0 6 4

How to fix   Complexity   

Complex Class

Complex classes like ByteBuffer 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 ByteBuffer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace MadWizard\WebAuthn\Format;
4
5
use InvalidArgumentException;
6
use MadWizard\WebAuthn\Exception\ByteBufferException;
7
use Serializable;
8
use function bin2hex;
9
use function hex2bin;
10
use const INF;
11
use const PHP_INT_SIZE;
12
13
class ByteBuffer implements Serializable
14
{
15
    use SerializableTrait;
16
17
    /**
18
     * @var string
19
     */
20
    private $data;
21
22
    /**
23
     * @var int
24
     */
25
    private $length;
26
27 187
    public function __construct(string $binaryData)
28
    {
29 187
        $this->data = $binaryData;
30 187
        $this->length = \strlen($binaryData);
31 187
    }
32
33 75
    public static function fromHex(string $hex): ByteBuffer
34
    {
35 75
        $bin = @hex2bin($hex);
36 75
        if ($bin === false) {
37 2
            throw new InvalidArgumentException('Invalid hex string');
38
        }
39 73
        return new ByteBuffer($bin);
40
    }
41
42 42
    public static function fromBase64Url(string $base64url): ByteBuffer
43
    {
44 42
        return new ByteBuffer(Base64UrlEncoding::decode($base64url));
45
    }
46
47 8
    public function isEmpty(): bool
48
    {
49 8
        return $this->length === 0;
50
    }
51
52 108
    public function getLength(): int
53
    {
54 108
        return $this->length;
55
    }
56
57 3
    public static function randomBuffer(int $length): ByteBuffer
58
    {
59 3
        return new ByteBuffer(\random_bytes($length));
60
    }
61
62
    /**
63
     * @throws ByteBufferException
64
     */
65 72
    public function getBytes(int $offset, int $length): string
66
    {
67 72
        if ($offset < 0 || $length < 0 || ($offset + $length > $this->length)) {
68 2
            throw new ByteBufferException('Invalid offset or length');
69
        }
70 70
        return \substr($this->data, $offset, $length);
71
    }
72
73
    /**
74
     * @throws ByteBufferException
75
     */
76 73
    public function getByteVal(int $offset): int
77
    {
78 73
        if ($offset < 0 || $offset >= $this->length) {
79 4
            throw new ByteBufferException('Invalid offset');
80
        }
81 71
        return \ord($this->data[$offset]);
82
    }
83
84
    /**
85
     * @throws ByteBufferException
86
     */
87 40
    public function getUint16Val(int $offset): int
88
    {
89 40
        if ($offset < 0 || ($offset + 2) > $this->length) {
90 4
            throw new ByteBufferException('Invalid offset');
91
        }
92 36
        return unpack('n', $this->data, $offset)[1];
93
    }
94
95
    /**
96
     * @throws ByteBufferException
97
     */
98 36
    public function getUint32Val(int $offset): int
99
    {
100 36
        if ($offset < 0 || ($offset + 4) > $this->length) {
101 2
            throw new ByteBufferException('Invalid offset');
102
        }
103 34
        $val = unpack('N', $this->data, $offset)[1];
104
        // Signed integer overflow causes signed negative numbers
105 34
        if ($val < 0) {
106
            throw new ByteBufferException('Value out of integer range.');
107
        }
108 34
        return $val;
109
    }
110
111
    /**
112
     * @throws ByteBufferException
113
     */
114 5
    public function getUint64Val(int $offset): int
115
    {
116 5
        if (PHP_INT_SIZE < 8) {
117
            throw new ByteBufferException('64-bit values not supported by this system');
118
        }
119 5
        if ($offset < 0 || ($offset + 8) > $this->length) {
120 2
            throw new ByteBufferException('Invalid offset');
121
        }
122 3
        $val = unpack('J', $this->data, $offset)[1];
123
124
        // Signed integer overflow causes signed negative numbers
125 3
        if ($val < 0) {
126 2
            throw new ByteBufferException('Value out of integer range.');
127
        }
128
129 2
        return $val;
130
    }
131
132 4
    public function getHalfFloatVal(int $offset): float
133
    {
134
        //FROM spec pseudo decode_half(unsigned char *halfp)
135 4
        $half = $this->getUint16Val($offset);
136
137 2
        $exp = ($half >> 10) & 0x1f;
138 2
        $mant = $half & 0x3ff;
139
140 2
        if ($exp === 0) {
141 2
            $val = $mant * (2 ** -24);
142 2
        } elseif ($exp !== 31) {
143 2
            $val = ($mant + 1024) * (2 ** ($exp - 25));
144
        } else {
145 2
            $val = ($mant === 0) ? INF : NAN;
146
        }
147
148 2
        return ($half & 0x8000) ? -$val : $val;
149
    }
150
151
    /**
152
     * @throws ByteBufferException
153
     */
154 4
    public function getFloatVal(int $offset): float
155
    {
156 4
        if ($offset < 0 || ($offset + 4) > $this->length) {
157 2
            throw new ByteBufferException('Invalid offset');
158
        }
159 2
        return unpack('G', $this->data, $offset)[1];
160
    }
161
162
    /**
163
     * @throws ByteBufferException
164
     */
165 4
    public function getDoubleVal(int $offset): float
166
    {
167 4
        if ($offset < 0 || ($offset + 8) > $this->length) {
168 2
            throw new ByteBufferException('Invalid offset');
169
        }
170 2
        return unpack('E', $this->data, $offset)[1];
171
    }
172
173 71
    public function getBinaryString(): string
174
    {
175 71
        return $this->data;
176
    }
177
178 20
    public function equals(ByteBuffer $buffer): bool
179
    {
180 20
        return $this->data === $buffer->data; // TODO constant time
181
    }
182
183 32
    public function getHex(): string
184
    {
185 32
        return bin2hex($this->data);
186
    }
187
188 12
    public function getBase64Url(): string
189
    {
190 12
        return Base64UrlEncoding::encode($this->data);
191
    }
192
193 1
    public function __serialize(): array
194
    {
195 1
        return ['d' => $this->data];
196
    }
197
198 1
    public function __unserialize(array $data): void
199
    {
200 1
        $this->data = $data['d'];
201 1
        $this->length = strlen($this->data);
202 1
    }
203
}
204