Test Setup Failed
Push — master ( 68fc3d...8f864d )
by Alex
02:33
created

Encoder::encodeExt()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 22
nc 8
nop 1
dl 0
loc 33
ccs 21
cts 21
cp 1
crap 8
rs 5.3846
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace MessagePack;
5
6
use MessagePack\{Exception\EncodingFailed, 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...
7
use const MessagePack\CHR;
8
use function array_values;
9
use function count;
10
use function is_array;
11
use function is_bool;
12
use function is_float;
13
use function is_int;
14
use function is_string;
15
use function pack;
16
use function strlen;
17
18
final class Encoder
19
{
20
    public const FORCE_AUTO = 0b00;
21
    public const FORCE_ARR = 0b01;
22
    public const FORCE_MAP = 0b10;
23
24
    /** @var int */
25
    private $typeDetectionMode = self::FORCE_AUTO;
26
27 65
    public function __construct(int $typeDetectionMode = null)
28
    {
29 65
        if (null !== $typeDetectionMode) {
30 5
            $this->typeDetectionMode |= $typeDetectionMode;
31
        }
32 65
    }
33
34 65
    public function encode($value): string
35
    {
36 65
        if (is_int($value)) {
37 35
            return $this->encodeInt($value);
38
        }
39 45
        if (is_string($value)) {
40 7
            return $this->encodeStr($value);
41
        }
42 38
        if (is_array($value)) {
43 16
            if ($this->typeDetectionMode ^ self::FORCE_AUTO) {
44 3
                return $this->typeDetectionMode & self::FORCE_MAP
45 2
                    ? $this->encodeMap($value)
46 3
                    : $this->encodeArray($value);
47
            }
48 13
            return array_values($value) === $value
49 8
                ? $this->encodeArray($value)
50 13
                : $this->encodeMap($value);
51
        }
52 22
        if (is_float($value)) {
53 6
            return $this->encodeFloat($value);
54
        }
55 16
        if (is_bool($value)) {
56 3
            return $this->encodeBool($value);
57
        }
58 13
        if (null === $value) {
59 1
            return $this->encodeNil();
60
        }
61 12
        if ($value instanceof Ext) {
62 9
            return $this->encodeExt($value);
63
        }
64
65 3
        throw EncodingFailed::unsupportedType($value);
66
    }
67
68 1
    public function encodeNil(): string
69
    {
70 1
        return "\xc0";
71
    }
72
73 3
    public function encodeBool(bool $value): string
74
    {
75 3
        return $value ? "\xc3" : "\xc2";
76
    }
77
78 6
    public function encodeFloat(float $num): string
79
    {
80 6
        $b1 = pack('E', $num);
81
82 6
        return "\xcb${b1}";
83
    }
84
85
    public function encodeInt(int $num): string
86
    {
87 35
        if ($num >= 0) {
88
            // positive fixint
89 25
            if ($num <= 0x7f) {
90 17
                return CHR[$num];
91
            }
92
            // uint 8
93 10
            if ($num <= 0xff) {
94 4
                $b1 = CHR[$num];
95 4
                return "\xcc${b1}";
96
            }
97
            // uint 16
98 8
            if ($num <= 0xffff) {
99 4
                $b1 = CHR[$num >> 8];
100 4
                $b2 = CHR[$num & 0xff];
101 4
                return "\xcd${b1}${b2}";
102
            }
103
            // uint 32
104 5
            if ($num <= 0xffffffff) {
105 3
                $b1 = CHR[$num >> 24];
106 3
                $b2 = CHR[$num >> 16 & 0xff];
107 3
                $b3 = CHR[$num >> 8 & 0xff];
108 3
                $b4 = CHR[$num & 0xff];
109 3
                return "\xce${b1}${b2}${b3}${b4}";
110
            }
111
            // uint 64
112 2
            return self::encodeUint64("\xcf", $num);
113
        }
114
        // negative fixint
115 10
        if ($num >= -0x20) {
116 2
            return CHR[$num & 0xff];
117
        }
118
        // int 8
119 8
        if ($num >= -0x80) {
120 2
            $b1 = CHR[$num & 0xff];
121 2
            return "\xd0${b1}";
122
        }
123
        // int 16
124 6
        if ($num >= -0x8000) {
125 2
            $b1 = CHR[$num >> 8 & 0xff];
126 2
            $b2 = CHR[$num & 0xff];
127 2
            return "\xd1${b1}${b2}";
128
        }
129
        // int 32
130 4
        if ($num >= -0x80000000) {
131 2
            $num |= 0x100000000;
132 2
            $b1 = CHR[$num >> 24 & 0xff];
133 2
            $b2 = CHR[$num >> 16 & 0xff];
134 2
            $b3 = CHR[$num >> 8 & 0xff];
135 2
            $b4 = CHR[$num & 0xff];
136 2
            return "\xd2${b1}${b2}${b3}${b4}";
137
        }
138
        // int 64
139 2
        return self::encodeUint64("\xd3", $num);
140
    }
141
142
    public function encodeStr(string $str): string
143
    {
144 7
        $len = strlen($str);
145
        // fixstr
146 7
        if ($len < 0x20) {
147 2
            $b0 = CHR[0xa0 | $len];
148 2
            return "${b0}${str}";
149
        }
150
        // str 8
151 5
        if ($len <= 0xff) {
152 2
            $b1 = CHR[$len];
153 2
            return "\xd9${b1}${str}";
154
        }
155
        // str 16
156 3
        if ($len <= 0xffff) {
157 2
            $b1 = CHR[$len >> 8];
158 2
            $b2 = CHR[$len & 0xff];
159 2
            return "\xda${b1}${b2}${str}";
160
        }
161
        // str 32
162 1
        $b1 = CHR[$len >> 24];
163 1
        $b2 = CHR[$len >> 16];
164 1
        $b3 = CHR[$len >> 8];
165 1
        $b4 = CHR[$len & 0xff];
166 1
        return "\xdb${b1}${b2}${b3}${b4}${str}";
167
    }
168
169
    public function encodeArray(array $array): string
170
    {
171 9
        $size = count($array);
172 9
        $data = self::encodeArrayHeader($size);
173
174 9
        foreach ($array as $value) {
175 8
            $data .= $this->encode($value);
176
        }
177
178 9
        return $data;
179
    }
180
181
    public function encodeMap(array $map): string
182
    {
183 7
        $size = count($map);
184 7
        $data = self::encodeMapHeader($size);
185
186 7
        foreach ($map as $key => $value) {
187 7
            $data .= "{$this->encode($key)}{$this->encode($value)}";
188
        }
189
190 7
        return $data;
191
    }
192
193
    public function encodeExt(Ext $ext): string
194
    {
195 9
        $type = CHR[$ext->type()];
196 9
        $data = $ext->data();
197
198 9
        $len = strlen($data);
199
200
        // fixext 1-16
201 9
        switch ($len) {
202 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...
203 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...
204 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...
205 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...
206 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...
207
        }
208
        // ext 8
209 4
        if ($len <= 0xff) {
210 1
            $b1 = CHR[$len];
211 1
            return "\xc7${b1}${type}${data}";
212
        }
213
        // ext 16
214 3
        if ($len <= 0xffff) {
215 2
            $b2 = CHR[$len & 0xff];
216 2
            $b1 = CHR[$len >> 8];
217 2
            return "\xc8${b1}${b2}${type}${data}";
218
        }
219
        // ext 32
220 1
        $b1 = CHR[$len >> 24 & 0xff];
221 1
        $b2 = CHR[$len >> 16 & 0xff];
222 1
        $b3 = CHR[$len >> 8 & 0xff];
223 1
        $b4 = CHR[$len & 0xff];
224 1
        return "\xc9${b1}${b2}${b3}${b4}${type}${data}";
225
    }
226
227
    private static function encodeArrayHeader(int $size): string
228
    {
229
        // fixarray
230 9
        if ($size <= 0xf) {
231 6
            return CHR[0x90 | $size];
232
        }
233
        // array 16
234 3
        if ($size <= 0xffff) {
235 2
            $b1 = CHR[$size >> 8];
236 2
            $b2 = CHR[$size & 0xff];
237 2
            return "\xdc${b1}${b2}";
238
        }
239
        // array 32
240 1
        $b1 = CHR[$size >> 24];
241 1
        $b2 = CHR[$size >> 16];
242 1
        $b3 = CHR[$size >> 8];
243 1
        $b4 = CHR[$size & 0xff];
244 1
        return "\xdd${b1}${b2}${b3}${b4}";
245
    }
246
247
    private static function encodeMapHeader(int $size): string
248
    {
249
        // fixmap
250 7
        if ($size <= 0xf) {
251 4
            return CHR[0x80 | $size];
252
        }
253
        // map 16
254 3
        if ($size <= 0xffff) {
255 2
            $b1 = CHR[$size >> 8];
256 2
            $b2 = CHR[$size & 0xff];
257 2
            return "\xde${b1}${b2}";
258
        }
259
        // map 32
260 1
        $b1 = CHR[$size >> 24];
261 1
        $b2 = CHR[$size >> 16];
262 1
        $b3 = CHR[$size >> 8];
263 1
        $b4 = CHR[$size & 0xff];
264 1
        return "\xdf${b1}${b2}${b3}${b4}";
265
    }
266
267
    private static function encodeUint64(string $byte, int $value): string
268
    {
269 4
        $hi = ($value & 0xffffffff00000000) >> 32;
270 4
        $lo = $value & 0x00000000ffffffff;
271
272 4
        $b1 = CHR[$hi >> 24 & 0xff];
273 4
        $b2 = CHR[$hi >> 16 & 0xff];
274 4
        $b3 = CHR[$hi >> 8 & 0xff];
275 4
        $b4 = CHR[$hi & 0xff];
276
277 4
        $b5 = CHR[$lo >> 24 & 0xff];
278 4
        $b6 = CHR[$lo >> 16 & 0xff];
279 4
        $b7 = CHR[$lo >> 8 & 0xff];
280 4
        $b8 = CHR[$lo & 0xff];
281
282 4
        return "${byte}${b1}${b2}${b3}${b4}${b5}${b6}${b7}${b8}";
283
    }
284
}
285