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

src/Encoder.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
declare(strict_types=1);
3
4
namespace MessagePack;
5
6
use MessagePack\{
7
    Exception\UnsupportedType,
8
    Ext
0 ignored issues
show
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}";
206 8
            case 2: return "\xd5${type}${data}";
207 7
            case 4: return "\xd6${type}${data}";
208 6
            case 8: return "\xd7${type}${data}";
209 5
            case 16: return "\xd8${type}${data}";
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