CborDecoder   B
last analyzed

Complexity

Total Complexity 44

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Test Coverage

Coverage 99.1%

Importance

Changes 0
Metric Value
eloc 112
c 0
b 0
f 0
dl 0
loc 202
ccs 110
cts 111
cp 0.991
rs 8.8798
wmc 44

9 Methods

Rating   Name   Duplication   Size   Complexity  
A decode() 0 11 3
A parseArray() 0 7 2
B parseExtraLength() 0 28 9
A decodeInPlace() 0 9 2
B parseItemData() 0 25 8
A parseMap() 0 16 5
B parseFloatSimple() 0 28 9
A parseSimple() 0 12 4
A parseItem() 0 13 2

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace MadWizard\WebAuthn\Format;
4
5
use MadWizard\WebAuthn\Exception\ByteBufferException;
6
use MadWizard\WebAuthn\Exception\CborException;
7
8
final class CborDecoder
9
{
10
    /**
11
     * @return mixed
12
     *
13
     * @throws CborException
14
     */
15 46
    public static function decode(ByteBuffer $buf)
16
    {
17
        try {
18 46
            $offset = 0;
19 46
            $result = self::parseItem($buf, $offset);
20 38
            if ($offset !== $buf->getLength()) {
21 1
                throw new CborException('Unused bytes after data item.');
22
            }
23 37
            return $result;
24 10
        } catch (ByteBufferException $e) {
25 3
            throw new CborException(sprintf('Error with byte buffer during parsing: %s.', $e->getMessage()), 0, $e);
26
        }
27
    }
28
29
    /**
30
     * @return mixed
31
     *
32
     * @throws CborException
33
     */
34 22
    public static function decodeInPlace(ByteBuffer $buf, int $startOffset, int &$endOffset = null)
35
    {
36
        try {
37 22
            $offset = $startOffset;
38 22
            $data = self::parseItem($buf, $offset);
39 21
            $endOffset = $offset;
40 21
            return $data;
41 1
        } catch (ByteBufferException $e) {
42 1
            throw new CborException(sprintf('Error with byte buffer during parsing: %s.', $e->getMessage()), 0, $e);
43
        }
44
    }
45
46
    /**
47
     * @return mixed
48
     *
49
     * @throws ByteBufferException
50
     * @throws CborException
51
     */
52 57
    private static function parseItem(ByteBuffer $buf, int &$offset)
53
    {
54 57
        $first = $buf->getByteVal($offset++);
55 56
        $type = $first >> 5;
56 56
        $val = $first & 0b11111;
57
58 56
        if ($type === Cbor::MAJOR_FLOAT_SIMPLE) {
59 3
            return self::parseFloatSimple($val, $buf, $offset);
60
        }
61
62 54
        $val = self::parseExtraLength($val, $buf, $offset);
63
64 52
        return self::parseItemData($type, $val, $buf, $offset);
65
    }
66
67
    /**
68
     * @return bool|float|mixed|null
69
     *
70
     * @throws ByteBufferException
71
     * @throws CborException
72
     */
73 3
    private static function parseFloatSimple(int $val, ByteBuffer $buf, int &$offset)
74
    {
75 3
        switch ($val) {
76 3
            case 24:
77 1
                $val = $buf->getByteVal($offset);
78 1
                $offset++;
79 1
                return self::parseSimple($val);
80 3
            case 25:
81 1
                $floatValue = $buf->getHalfFloatVal($offset);
82 1
                $offset += 2;
83 1
                return $floatValue;
84 3
            case 26:
85 1
                $floatValue = $buf->getFloatVal($offset);
86 1
                $offset += 4;
87 1
                return $floatValue;
88 3
            case 27:
89 1
                $floatValue = $buf->getDoubleVal($offset);
90 1
                $offset += 8;
91 1
                return $floatValue;
92 3
            case 28:
93 3
            case 29:
94 3
            case 30:
95 1
                throw new CborException('Reserved value used.');
96 2
            case 31:
97 1
                throw new CborException('Indefinite length is not supported.');
98
        }
99
100 1
        return self::parseSimple($val);
101
    }
102
103
    /**
104
     * @return bool|null
105
     *
106
     * @throws CborException
107
     */
108 1
    private static function parseSimple(int $val)
109
    {
110 1
        if ($val === 20) {
111 1
            return false;
112
        }
113 1
        if ($val === 21) {
114 1
            return true;
115
        }
116 1
        if ($val === 22) {
117 1
            return null;
118
        }
119 1
        throw new CborException(sprintf('Unsupported simple value %d.', $val));
120
    }
121
122 54
    private static function parseExtraLength(int $val, ByteBuffer $buf, int &$offset): int
123
    {
124 54
        switch ($val) {
125 54
            case 24:
126 29
                $val = $buf->getByteVal($offset);
127 29
                $offset++;
128 29
                break;
129 54
            case 25:
130 26
                $val = $buf->getUint16Val($offset);
131 26
                $offset += 2;
132 26
                break;
133 54
            case 26:
134 1
                $val = $buf->getUint32Val($offset);
135 1
                $offset += 4;
136 1
                break;
137 54
            case 27:
138 1
                $val = $buf->getUint64Val($offset);
139 1
                $offset += 8;
140 1
                break;
141 54
            case 28:
142 54
            case 29:
143 54
            case 30:
144 1
                throw new CborException('Reserved value used.');
145 53
            case 31:
146 2
                throw new CborException('Indefinite length is not supported.');
147
        }
148
149 52
        return $val;
150
    }
151
152
    /**
153
     * @return array|bool|float|int|ByteBuffer|CborMap|string|null
154
     *
155
     * @throws ByteBufferException
156
     * @throws CborException
157
     */
158 52
    private static function parseItemData(int $type, int $val, ByteBuffer $buf, int &$offset)
159
    {
160 52
        switch ($type) {
161
            case Cbor::MAJOR_UNSIGNED_INT: // uint
162 31
                return $val;
163
            case Cbor::MAJOR_NEGATIVE_INT:
164 31
                return -1 - $val;
165
            case Cbor::MAJOR_BYTE_STRING:
166 44
                $data = $buf->getBytes($offset, $val);
167 44
                $offset += $val;
168 44
                return new ByteBuffer($data); // bytes
169
            case Cbor::MAJOR_TEXT_STRING:
170 38
                $data = $buf->getBytes($offset, $val);
171 38
                $offset += $val;
172 38
                return $data; // UTF-8
173
            case Cbor::MAJOR_ARRAY:
174 23
                return self::parseArray($buf, $offset, $val);
175
            case Cbor::MAJOR_MAP:
176 47
                return self::parseMap($buf, $offset, $val);
177
            case Cbor::MAJOR_TAG:
178 1
                return self::parseItem($buf, $offset); // 1 embedded data item
179
        }
180
181
        // This should never be reached
182
        throw new CborException(sprintf('Unknown major type %d.', $type));
183
    }
184
185 47
    private static function parseMap(ByteBuffer $buf, int &$offset, int $count): CborMap
186
    {
187 47
        $map = new CborMap();
188
189 47
        for ($i = 0; $i < $count; $i++) {
190 47
            $mapKey = self::parseItem($buf, $offset);
191 47
            $mapVal = self::parseItem($buf, $offset);
192 47
            if (!\is_int($mapKey) && !\is_string($mapKey)) {
193 1
                throw new CborException('Can only use strings or integers as map keys');
194
            }
195 46
            if ($map->has($mapKey)) {
196 1
                throw new CborException('Maps cannot contain duplicate keys');
197
            }
198 46
            $map->set($mapKey, $mapVal);
199
        }
200 45
        return $map;
201
    }
202
203 23
    private static function parseArray(ByteBuffer $buf, int &$offset, int $count): array
204
    {
205 23
        $arr = [];
206 23
        for ($i = 0; $i < $count; $i++) {
207 21
            $arr[] = self::parseItem($buf, $offset);
208
        }
209 21
        return $arr;
210
    }
211
}
212