CborEncoder::encodeSingleValue()   B
last analyzed

Complexity

Conditions 8
Paths 8

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 8.125

Importance

Changes 0
Metric Value
cc 8
eloc 15
c 0
b 0
f 0
nc 8
nop 1
dl 0
loc 25
ccs 14
cts 16
cp 0.875
crap 8.125
rs 8.4444
1
<?php
2
3
namespace MadWizard\WebAuthn\Format;
4
5
use MadWizard\WebAuthn\Exception\CborException;
6
7
final class CborEncoder
8
{
9 6
    public static function encodeInteger(int $i): string
10
    {
11 6
        if ($i < 0) {
12 5
            $i = -($i + 1);
13 5
            $major = Cbor::MAJOR_NEGATIVE_INT;
14
        } else {
15 6
            $major = Cbor::MAJOR_UNSIGNED_INT;
16
        }
17
18 6
        $bytes = self::integerBytes($i, $minorVal);
19
20 6
        return chr(($major << 5) | $minorVal) . $bytes;
21
    }
22
23 4
    public static function encodeTextString(string $text): string
24
    {
25 4
        $lengthBytes = self::integerBytes(\strlen($text), $minorVal);
26 4
        return chr((Cbor::MAJOR_TEXT_STRING << 5) | $minorVal) . $lengthBytes . $text;
27
    }
28
29 6
    public static function encodeByteString(ByteBuffer $bytes): string
30
    {
31 6
        $lengthBytes = self::integerBytes($bytes->getLength(), $minorVal);
32 6
        return chr((Cbor::MAJOR_BYTE_STRING << 5) | $minorVal) . $lengthBytes . $bytes->getBinaryString();
33
    }
34
35 6
    private static function sortAndEncodeMapEntries(array $map): string
36
    {
37
        // Use canonical sorting. Shorter keys (as CBOR bytes) always go before longer keys. When length is the same
38
        // a byte for byte comparison is done.
39
        uksort($map, function (string $key1, string $key2) {
40 6
            $cmp = \strlen($key1) <=> \strlen($key2);
41 6
            return ($cmp === 0) ? ($key1 <=> $key2) : $cmp;
42 6
        });
43
44 6
        $mapContent = '';
45 6
        foreach ($map as $k => $v) {
46 6
            $mapContent .= $k . $v;
47
        }
48 6
        $lengthBytes = self::integerBytes(count($map), $minorVal);
49 6
        return chr((Cbor::MAJOR_MAP << 5) | $minorVal) . $lengthBytes . $mapContent;
50
    }
51
52 6
    public static function encodeMap(CborMap $map): string
53
    {
54 6
        $mapValues = [];
55
56 6
        foreach ($map->getEntries() as $entry) {
57 6
            $mapValues[self::encodeSingleValue($entry[0])] = self::encodeSingleValue($entry[1]);
58
        }
59
60 6
        return self::sortAndEncodeMapEntries($mapValues);
61
    }
62
63 9
    private static function integerBytes(int $i, ?int &$minorVal): string
64
    {
65 9
        if ($i < 24) {
66 9
            $minorVal = $i;
67 9
            return '';
68
        }
69
70 5
        if ($i < 2 ** 8) {
71 4
            $minorVal = 24;
72 4
            return pack('C', $i);
73
        }
74
75 2
        if ($i < 2 ** 16) {
76 2
            $minorVal = 25;
77 2
            return pack('n', $i);
78
        }
79
80 1
        if (PHP_INT_SIZE < 8 || $i < 2 ** 32) {
81 1
            $minorVal = 26;
82 1
            return pack('N', $i);
83
        }
84
85 1
        $minorVal = 27;
86 1
        return pack('J', $i);
87
    }
88
89
    /**
90
     * @param int|string|ByteBuffer|CborMap|bool|null $value
91
     *
92
     * @throws CborException
93
     */
94 6
    public static function encodeSingleValue($value): string
95
    {
96 6
        if (\is_int($value)) {
97 5
            return self::encodeInteger($value);
98
        }
99 6
        if (\is_string($value)) {
100 3
            return self::encodeTextString($value);
101
        }
102 6
        if ($value instanceof ByteBuffer) {
103 5
            return self::encodeByteString($value);
104
        }
105 2
        if ($value instanceof CborMap) {
106 1
            return self::encodeMap($value);
107
        }
108 1
        if ($value === false) {
109 1
            return chr(0xF4);
110
        }
111 1
        if ($value === true) {
0 ignored issues
show
introduced by
The condition $value === true is always true.
Loading history...
112
            return chr(0xF5);
113
        }
114 1
        if ($value === null) {
115 1
            return chr(0xF6);
116
        }
117
        // @phpstan-ignore-next-line
118
        throw new CborException(sprintf('Unsupported type "%s" for encodeSingleValue', gettype($value)));
119
    }
120
}
121