Test Setup Failed
Push — master ( 9ec3c0...c630a6 )
by Alex
41:24
created

Encoder::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
ccs 4
cts 4
cp 1
crap 2
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 65
    public function __construct(int $typeDetectionMode = null)
31
    {
32 65
        if (null !== $typeDetectionMode) {
33 5
            $this->typeDetectionMode |= $typeDetectionMode;
34
        }
35 65
    }
36
37 65
    public function encode($value): string
38
    {
39 65
        if (is_int($value)) {
40 35
            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 $this->encodeBool($value);
60
        }
61 13
        if (null === $value) {
62 1
            return $this->encodeNil();
63
        }
64 12
        if ($value instanceof Ext) {
65 9
            return $this->encodeExt($value);
66
        }
67
68 3
        throw UnsupportedType::withValue($value);
69
    }
70
71 1
    public function encodeNil(): string
72
    {
73 1
        return "\xc0";
74
    }
75
76 3
    public function encodeBool(bool $value): string
77
    {
78 3
        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 35
        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
            return self::encodeUint64("\xcf", $num);
116
        }
117
        // negative fixint
118 10
        if ($num >= -0x20) {
119 2
            return CHR[$num & 0xff];
120
        }
121
        // int 8
122 8
        if ($num >= -0x80) {
123 2
            $b1 = CHR[$num & 0xff];
124 2
            return "\xd0${b1}";
125
        }
126
        // int 16
127 6
        if ($num >= -0x8000) {
128 2
            $b1 = CHR[$num >> 8 & 0xff];
129 2
            $b2 = CHR[$num & 0xff];
130 2
            return "\xd1${b1}${b2}";
131
        }
132
        // int 32
133 4
        if ($num >= -0x80000000) {
134 2
            $num |= 0x100000000;
135 2
            $b1 = CHR[$num >> 24 & 0xff];
136 2
            $b2 = CHR[$num >> 16 & 0xff];
137 2
            $b3 = CHR[$num >> 8 & 0xff];
138 2
            $b4 = CHR[$num & 0xff];
139 2
            return "\xd2${b1}${b2}${b3}${b4}";
140
        }
141
        // int 64
142 2
        return self::encodeUint64("\xd3", $num);
143
    }
144
145
    public function encodeStr(string $str): string
146
    {
147 7
        $len = strlen($str);
148
        // fixstr
149 7
        if ($len < 0x20) {
150 2
            $b0 = CHR[0xa0 | $len];
151 2
            return "${b0}${str}";
152
        }
153
        // str 8
154 5
        if ($len <= 0xff) {
155 2
            $b1 = CHR[$len];
156 2
            return "\xd9${b1}${str}";
157
        }
158
        // str 16
159 3
        if ($len <= 0xffff) {
160 2
            $b1 = CHR[$len >> 8];
161 2
            $b2 = CHR[$len & 0xff];
162 2
            return "\xda${b1}${b2}${str}";
163
        }
164
        // str 32
165 1
        $b1 = CHR[$len >> 24];
166 1
        $b2 = CHR[$len >> 16];
167 1
        $b3 = CHR[$len >> 8];
168 1
        $b4 = CHR[$len & 0xff];
169 1
        return "\xdb${b1}${b2}${b3}${b4}${str}";
170
    }
171
172
    public function encodeArray(array $array): string
173
    {
174 9
        $size = count($array);
175 9
        $data = self::encodeArrayHeader($size);
176
177 9
        foreach ($array as $value) {
178 8
            $data .= $this->encode($value);
179
        }
180
181 9
        return $data;
182
    }
183
184
    public function encodeMap(array $map): string
185
    {
186 7
        $size = count($map);
187 7
        $data = self::encodeMapHeader($size);
188
189 7
        foreach ($map as $key => $value) {
190 7
            $data .= "{$this->encode($key)}{$this->encode($value)}";
191
        }
192
193 7
        return $data;
194
    }
195
196
    public function encodeExt(Ext $ext): string
197
    {
198 9
        $type = CHR[$ext->type()];
199 9
        $data = $ext->data();
200
201 9
        $len = strlen($data);
202
203
        // fixext 1/2/4/8/16
204 9
        switch ($len) {
205 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...
206 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...
207 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...
208 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...
209 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...
210
        }
211
        // ext 8
212 4
        if ($len <= 0xff) {
213 1
            $b1 = CHR[$len];
214 1
            return "\xc7${b1}${type}${data}";
215
        }
216
        // ext 16
217 3
        if ($len <= 0xffff) {
218 2
            $b2 = CHR[$len & 0xff];
219 2
            $b1 = CHR[$len >> 8];
220 2
            return "\xc8${b1}${b2}${type}${data}";
221
        }
222
        // ext 32
223 1
        $b1 = CHR[$len >> 24 & 0xff];
224 1
        $b2 = CHR[$len >> 16 & 0xff];
225 1
        $b3 = CHR[$len >> 8 & 0xff];
226 1
        $b4 = CHR[$len & 0xff];
227 1
        return "\xc9${b1}${b2}${b3}${b4}${type}${data}";
228
    }
229
230
    private static function encodeArrayHeader(int $size): string
231
    {
232
        // fixarray
233 9
        if ($size <= 0xf) {
234 6
            return CHR[0x90 | $size];
235
        }
236
        // array 16
237 3
        if ($size <= 0xffff) {
238 2
            $b1 = CHR[$size >> 8];
239 2
            $b2 = CHR[$size & 0xff];
240 2
            return "\xdc${b1}${b2}";
241
        }
242
        // array 32
243 1
        $b1 = CHR[$size >> 24];
244 1
        $b2 = CHR[$size >> 16];
245 1
        $b3 = CHR[$size >> 8];
246 1
        $b4 = CHR[$size & 0xff];
247 1
        return "\xdd${b1}${b2}${b3}${b4}";
248
    }
249
250
    private static function encodeMapHeader(int $size): string
251
    {
252
        // fixmap
253 7
        if ($size <= 0xf) {
254 4
            return CHR[0x80 | $size];
255
        }
256
        // map 16
257 3
        if ($size <= 0xffff) {
258 2
            $b1 = CHR[$size >> 8];
259 2
            $b2 = CHR[$size & 0xff];
260 2
            return "\xde${b1}${b2}";
261
        }
262
        // map 32
263 1
        $b1 = CHR[$size >> 24];
264 1
        $b2 = CHR[$size >> 16];
265 1
        $b3 = CHR[$size >> 8];
266 1
        $b4 = CHR[$size & 0xff];
267 1
        return "\xdf${b1}${b2}${b3}${b4}";
268
    }
269
270
    private static function encodeUint64(string $byte, int $value): string
271
    {
272 4
        $hi = ($value & 0xffffffff00000000) >> 32;
273 4
        $lo = $value & 0x00000000ffffffff;
274
275 4
        $b1 = CHR[$hi >> 24 & 0xff];
276 4
        $b2 = CHR[$hi >> 16 & 0xff];
277 4
        $b3 = CHR[$hi >> 8 & 0xff];
278 4
        $b4 = CHR[$hi & 0xff];
279
280 4
        $b5 = CHR[$lo >> 24 & 0xff];
281 4
        $b6 = CHR[$lo >> 16 & 0xff];
282 4
        $b7 = CHR[$lo >> 8 & 0xff];
283 4
        $b8 = CHR[$lo & 0xff];
284
285 4
        return "${byte}${b1}${b2}${b3}${b4}${b5}${b6}${b7}${b8}";
286
    }
287
}
288