Test Setup Failed
Push — master ( 0a30f1...9fb140 )
by Alex
02:52
created

Encoder::encodeMap()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 17
nc 6
nop 1
dl 0
loc 24
ccs 15
cts 15
cp 1
crap 4
rs 8.6845
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace MessagePack;
5
6
use MessagePack\{
7
    Exception\UnsupportedType,
8
    Ext
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, MessagePack\Ext.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
};
10
use const MessagePack\CHR;
11
use function array_values;
12
use function count;
13
use function is_array;
14
use function is_bool;
15
use function is_float;
16
use function is_int;
17
use function is_string;
18
use function pack;
19
use function strlen;
20
21
final class Encoder
22
{
23
    public const FORCE_AUTO = 0b00;
24
    public const FORCE_ARR = 0b01;
25
    public const FORCE_MAP = 0b10;
26
27
    /** @var int */
28
    private $typeDetectionMode = self::FORCE_AUTO;
29
30 71
    public function __construct(int $typeDetectionMode = null)
31
    {
32 71
        if (null !== $typeDetectionMode) {
33 5
            $this->typeDetectionMode |= $typeDetectionMode;
34
        }
35 71
    }
36
37 71
    public function encode($value): string
38
    {
39 71
        if (is_int($value)) {
40 41
            return $this->encodeInt($value);
41
        }
42 45
        if (is_string($value)) {
43 7
            return $this->encodeStr($value);
44
        }
45 38
        if (is_array($value)) {
46 16
            if ($this->typeDetectionMode ^ self::FORCE_AUTO) {
47 3
                return $this->typeDetectionMode & self::FORCE_MAP
48 2
                    ? $this->encodeMap($value)
49 3
                    : $this->encodeArray($value);
50
            }
51 13
            return array_values($value) === $value
52 8
                ? $this->encodeArray($value)
53 13
                : $this->encodeMap($value);
54
        }
55 22
        if (is_float($value)) {
56 6
            return $this->encodeFloat($value);
57
        }
58 16
        if (is_bool($value)) {
59 3
            return $value ? "\xc3" : "\xc2";
60
        }
61 13
        if (null === $value) {
62 1
            return "\xc0";
63
        }
64 12
        if ($value instanceof Ext) {
65 9
            return $this->encodeExt($value);
66
        }
67
68 3
        throw UnsupportedType::withValue($value);
69
    }
70
71
    public function encodeNil(): string
72
    {
73
        return "\xc0";
74
    }
75
76
    public function encodeBool(bool $value): string
77
    {
78
        return $value ? "\xc3" : "\xc2";
79
    }
80
81 6
    public function encodeFloat(float $num): string
82
    {
83 6
        $b1 = pack('E', $num);
84
85 6
        return "\xcb${b1}";
86
    }
87
88
    public function encodeInt(int $num): string
89
    {
90 41
        if ($num >= 0) {
91
            // positive fixint
92 25
            if ($num <= 0x7f) {
93 17
                return CHR[$num];
94
            }
95
            // uint 8
96 10
            if ($num <= 0xff) {
97 4
                $b1 = CHR[$num];
98 4
                return "\xcc${b1}";
99
            }
100
            // uint 16
101 8
            if ($num <= 0xffff) {
102 4
                $b1 = CHR[$num >> 8];
103 4
                $b2 = CHR[$num & 0xff];
104 4
                return "\xcd${b1}${b2}";
105
            }
106
            // uint 32
107 5
            if ($num <= 0xffffffff) {
108 3
                $b1 = CHR[$num >> 24];
109 3
                $b2 = CHR[$num >> 16 & 0xff];
110 3
                $b3 = CHR[$num >> 8 & 0xff];
111 3
                $b4 = CHR[$num & 0xff];
112 3
                return "\xce${b1}${b2}${b3}${b4}";
113
            }
114
            // uint 64
115 2
            $hi = ($num & 0xffffffff00000000) >> 32;
116 2
            $lo = $num & 0x00000000ffffffff;
117
118 2
            $b1 = CHR[$hi >> 24 & 0xff];
119 2
            $b2 = CHR[$hi >> 16 & 0xff];
120 2
            $b3 = CHR[$hi >> 8 & 0xff];
121 2
            $b4 = CHR[$hi & 0xff];
122 2
            $b5 = CHR[$lo >> 24 & 0xff];
123 2
            $b6 = CHR[$lo >> 16 & 0xff];
124 2
            $b7 = CHR[$lo >> 8 & 0xff];
125 2
            $b8 = CHR[$lo & 0xff];
126 2
            return "\xcf${b1}${b2}${b3}${b4}${b5}${b6}${b7}${b8}";
127
        }
128
        // negative fixint
129 16
        if ($num >= -0x20) {
130 2
            return CHR[$num & 0xff];
131
        }
132
        // int 8
133 14
        if ($num >= -0x80) {
134 2
            $b1 = CHR[$num & 0xff];
135 2
            return "\xd0${b1}";
136
        }
137
        // int 16
138 12
        if ($num >= -0x8000) {
139 2
            $b1 = CHR[$num >> 8 & 0xff];
140 2
            $b2 = CHR[$num & 0xff];
141 2
            return "\xd1${b1}${b2}";
142
        }
143
        // int 32
144 10
        if ($num >= -0x80000000) {
145 2
            $num |= 0x100000000;
146 2
            $b1 = CHR[$num >> 24 & 0xff];
147 2
            $b2 = CHR[$num >> 16 & 0xff];
148 2
            $b3 = CHR[$num >> 8 & 0xff];
149 2
            $b4 = CHR[$num & 0xff];
150 2
            return "\xd2${b1}${b2}${b3}${b4}";
151
        }
152
        // int 64
153 8
        $hi = ($num & 0xffffffff00000000) >> 32;
154 8
        $lo = $num & 0x00000000ffffffff;
155
156 8
        $b1 = CHR[$hi >> 24 & 0xff];
157 8
        $b2 = CHR[$hi >> 16 & 0xff];
158 8
        $b3 = CHR[$hi >> 8 & 0xff];
159 8
        $b4 = CHR[$hi & 0xff];
160 8
        $b5 = CHR[$lo >> 24 & 0xff];
161 8
        $b6 = CHR[$lo >> 16 & 0xff];
162 8
        $b7 = CHR[$lo >> 8 & 0xff];
163 8
        $b8 = CHR[$lo & 0xff];
164 8
        return "\xd3${b1}${b2}${b3}${b4}${b5}${b6}${b7}${b8}";
165
    }
166
167
    public function encodeStr(string $str): string
168
    {
169 7
        $len = strlen($str);
170
        // fixstr
171 7
        if ($len < 0x20) {
172 2
            $b0 = CHR[0xa0 | $len];
173 2
            return "${b0}${str}";
174
        }
175
        // str 8
176 5
        if ($len <= 0xff) {
177 2
            $b1 = CHR[$len];
178 2
            return "\xd9${b1}${str}";
179
        }
180
        // str 16
181 3
        if ($len <= 0xffff) {
182 2
            $b1 = CHR[$len >> 8];
183 2
            $b2 = CHR[$len & 0xff];
184 2
            return "\xda${b1}${b2}${str}";
185
        }
186
        // str 32
187 1
        $b1 = CHR[$len >> 24];
188 1
        $b2 = CHR[$len >> 16];
189 1
        $b3 = CHR[$len >> 8];
190 1
        $b4 = CHR[$len & 0xff];
191 1
        return "\xdb${b1}${b2}${b3}${b4}${str}";
192
    }
193
194
    public function encodeArray(array $array): string
195
    {
196 9
        $size = count($array);
197
198 9
        if ($size <= 0xf) { // fixarray
199 6
            $data = CHR[0x90 | $size];
200 3
        } elseif ($size <= 0xffff) { // array 16
201 2
            $b1 = CHR[$size >> 8];
202 2
            $b2 = CHR[$size & 0xff];
203 2
            $data = "\xdc${b1}${b2}";
204
        } else { // array 32
205 1
            $b1 = CHR[$size >> 24];
206 1
            $b2 = CHR[$size >> 16];
207 1
            $b3 = CHR[$size >> 8];
208 1
            $b4 = CHR[$size & 0xff];
209 1
            $data = "\xdd${b1}${b2}${b3}${b4}";
210
        }
211
212 9
        foreach ($array as $value) {
213 8
            $data .= $this->encode($value);
214
        }
215
216 9
        return $data;
217
    }
218
219
    public function encodeMap(array $map): string
220
    {
221 7
        $size = count($map);
222
223 7
        if ($size <= 0xf) { // fixmap
224 4
            $data = CHR[0x80 | $size];
225 3
        } elseif ($size <= 0xffff) { // map 16
226 2
            $b1 = CHR[$size >> 8];
227 2
            $b2 = CHR[$size & 0xff];
228 2
            $data = "\xde${b1}${b2}";
229
        } else { // map 32
230 1
            $b1 = CHR[$size >> 24];
231 1
            $b2 = CHR[$size >> 16];
232 1
            $b3 = CHR[$size >> 8];
233 1
            $b4 = CHR[$size & 0xff];
234 1
            $data = "\xdf${b1}${b2}${b3}${b4}";
235
        }
236
237 7
        foreach ($map as $key => $value) {
238 7
            $data .= "{$this->encode($key)}{$this->encode($value)}";
239
        }
240
241 7
        return $data;
242
    }
243
244
    public function encodeExt(Ext $ext): string
245
    {
246 9
        $type = CHR[$ext->type() & 0x7f];
247 9
        $data = $ext->data();
248
249 9
        $len = strlen($data);
250
251
        // fixext 1/2/4/8/16
252 9
        switch ($len) {
253 9
            case 1: return "\xd4${type}${data}";
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
254 8
            case 2: return "\xd5${type}${data}";
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
255 7
            case 4: return "\xd6${type}${data}";
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
256 6
            case 8: return "\xd7${type}${data}";
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
257 5
            case 16: return "\xd8${type}${data}";
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
258
        }
259
        // ext 8
260 4
        if ($len <= 0xff) {
261 1
            $b1 = CHR[$len];
262 1
            return "\xc7${b1}${type}${data}";
263
        }
264
        // ext 16
265 3
        if ($len <= 0xffff) {
266 2
            $b1 = CHR[$len >> 8];
267 2
            $b2 = CHR[$len & 0xff];
268 2
            return "\xc8${b1}${b2}${type}${data}";
269
        }
270
        // ext 32
271 1
        $b1 = CHR[$len >> 24 & 0xff];
272 1
        $b2 = CHR[$len >> 16 & 0xff];
273 1
        $b3 = CHR[$len >> 8 & 0xff];
274 1
        $b4 = CHR[$len & 0xff];
275 1
        return "\xc9${b1}${b2}${b3}${b4}${type}${data}";
276
    }
277
}
278