Completed
Push — master ( 6a9327...c27a0c )
by Eugene
04:54
created

BufferUnpacker::getTransformers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/*
4
 * This file is part of the rybakit/msgpack.php package.
5
 *
6
 * (c) Eugene Leonovich <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace MessagePack;
13
14
use MessagePack\Exception\InsufficientDataException;
15
use MessagePack\Exception\IntegerOverflowException;
16
use MessagePack\Exception\UnpackingFailedException;
17
use MessagePack\TypeTransformer\Collection;
18
19
class BufferUnpacker
20
{
21
    const BIGINT_AS_EXCEPTION = 0;
22
    const BIGINT_AS_STR = 1;
23
    const BIGINT_AS_GMP = 2;
24
25
    /**
26
     * @var int
27
     */
28
    private $bigIntMode = self::BIGINT_AS_EXCEPTION;
29
30
    /**
31
     * @var string
32
     */
33
    private $buffer = '';
34
35
    /**
36
     * @var int
37
     */
38
    private $offset = 0;
39
40
    /**
41
     * @var Collection
42
     */
43
    private $transformers;
44
45
    /**
46
     * @param int|null $bigIntMode
47
     */
48 96
    public function __construct($bigIntMode = null)
49
    {
50 96
        if (null !== $bigIntMode) {
51 2
            $this->bigIntMode = $bigIntMode;
52 2
        }
53 96
    }
54
55
    /**
56
     * @param Collection|null $transformers
57
     */
58 2
    public function setTransformers(Collection $transformers = null)
59
    {
60 2
        $this->transformers = $transformers;
61 2
    }
62
63
    /**
64
     * @return Collection
65
     */
66 1
    public function getTransformers()
67
    {
68 1
        return $this->transformers;
69 1
    }
70
71
    /**
72
     * @param int $bigIntMode
73
     *
74
     * @throws \InvalidArgumentException
75
     */
76 2
    public function setBigIntMode($bigIntMode)
77
    {
78 2
        if (!in_array($bigIntMode, [
79 2
            self::BIGINT_AS_EXCEPTION,
80 2
            self::BIGINT_AS_STR,
81 2
            self::BIGINT_AS_GMP,
82 2
        ], true)) {
83 1
            throw new \InvalidArgumentException(sprintf('Invalid bigint mode: %s.', $bigIntMode));
84
        }
85
86 1
        $this->bigIntMode = $bigIntMode;
87 1
    }
88
89
    /**
90
     * @return int
91
     */
92 1
    public function getBigIntMode()
93
    {
94 1
        return $this->bigIntMode;
95
    }
96
97
    /**
98
     * @param string $data
99
     *
100
     * @return $this
101
     */
102 5
    public function append($data)
103
    {
104 5
        $this->buffer .= $data;
105
106 5
        return $this;
107
    }
108
109
    /**
110
     * @param string|null $buffer
111
     *
112
     * @return $this
113
     */
114 88
    public function reset($buffer = null)
115
    {
116 88
        $this->buffer = (string) $buffer;
117 88
        $this->offset = 0;
118
119 88
        return $this;
120
    }
121
122
    /**
123
     * @return array
124
     */
125 3
    public function tryUnpack()
126
    {
127 3
        $data = [];
128 3
        $offset = $this->offset;
129
130
        try {
131
            do {
132 3
                $data[] = $this->unpack();
133 3
                $offset = $this->offset;
134 3
            } while (isset($this->buffer[$this->offset]));
135 3
        } catch (InsufficientDataException $e) {
136 1
            $this->offset = $offset;
137
        }
138
139 3
        if ($this->offset) {
140 3
            $this->buffer = (string) substr($this->buffer, $this->offset);
141 3
            $this->offset = 0;
142 3
        }
143
144 3
        return $data;
145
    }
146
147
    /**
148
     * @return mixed
149
     *
150
     * @throws UnpackingFailedException
151
     */
152 92
    public function unpack()
153
    {
154 92
        $this->ensureLength(1);
155
156 90
        $c = ord($this->buffer[$this->offset]);
157 90
        $this->offset += 1;
158
159
        // fixint
160 90
        if ($c <= 0x7f) {
161 19
            return $c;
162
        }
163
        // fixstr
164 87
        if ($c >= 0xa0 && $c <= 0xbf) {
165 12
            return $this->unpackStr($c & 0x1f);
166
        }
167
        // fixarray
168 82
        if ($c >= 0x90 && $c <= 0x9f) {
169 6
            return $this->unpackArr($c & 0xf);
170
        }
171
        // fixmap
172 79
        if ($c >= 0x80 && $c <= 0x8f) {
173 10
            return $this->unpackMap($c & 0xf);
174
        }
175
        // negfixint
176 75
        if ($c >= 0xe0) {
177 3
            return $c - 256;
178
        }
179
180
        switch ($c) {
181 72
            case 0xc0: return null;
182 71
            case 0xc2: return false;
183 69
            case 0xc3: return true;
184
185
            // MP_BIN
186 64
            case 0xc4: return $this->unpackStr($this->unpackU8());
187 60
            case 0xc5: return $this->unpackStr($this->unpackU16());
188 59
            case 0xc6: return $this->unpackStr($this->unpackU32());
189
190 58
            case 0xca: return $this->unpackFloat();
191 56
            case 0xcb: return $this->unpackDouble();
192
193
            // MP_UINT
194 53
            case 0xcc: return $this->unpackU8();
195 50
            case 0xcd: return $this->unpackU16();
196 46
            case 0xce: return $this->unpackU32();
197 43
            case 0xcf: return $this->unpackU64();
198
199
            // MP_INT
200 36
            case 0xd0: return $this->unpackI8();
201 33
            case 0xd1: return $this->unpackI16();
202 30
            case 0xd2: return $this->unpackI32();
203 27
            case 0xd3: return $this->unpackI64();
204
205
            // MP_STR
206 24
            case 0xd9: return $this->unpackStr($this->unpackU8());
207 20
            case 0xda: return $this->unpackStr($this->unpackU16());
208 18
            case 0xdb: return $this->unpackStr($this->unpackU32());
209
210
            // MP_ARRAY
211 17
            case 0xdc: return $this->unpackArr($this->unpackU16());
212 15
            case 0xdd: return $this->unpackArr($this->unpackU32());
213
214
            // MP_MAP
215 14
            case 0xde: return $this->unpackMap($this->unpackU16());
216 12
            case 0xdf: return $this->unpackMap($this->unpackU32());
217
218
            // MP_EXT
219 11
            case 0xd4: return $this->unpackExt(1);
220 9
            case 0xd5: return $this->unpackExt(2);
221 8
            case 0xd6: return $this->unpackExt(4);
222 7
            case 0xd7: return $this->unpackExt(8);
223 6
            case 0xd8: return $this->unpackExt(16);
224 5
            case 0xc7: return $this->unpackExt($this->unpackU8());
225 3
            case 0xc8: return $this->unpackExt($this->unpackU16());
226 2
            case 0xc9: return $this->unpackExt($this->unpackU32());
227
        }
228
229 1
        throw new UnpackingFailedException(sprintf('Unknown code: 0x%x.', $c));
230
    }
231
232 16
    private function unpackU8()
233
    {
234 16
        $this->ensureLength(1);
235
236 16
        $num = $this->buffer[$this->offset];
237 16
        $this->offset += 1;
238
239 16
        $num = unpack('C', $num);
240
241 16
        return $num[1];
242
    }
243
244 13 View Code Duplication
    private function unpackU16()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
245
    {
246 13
        $this->ensureLength(2);
247
248 13
        $num = $this->buffer[$this->offset].$this->buffer[$this->offset + 1];
249 13
        $this->offset += 2;
250
251 13
        $num = unpack('n', $num);
252
253 13
        return $num[1];
254
    }
255
256 9 View Code Duplication
    private function unpackU32()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
257
    {
258 9
        $this->ensureLength(4);
259
260 9
        $num = substr($this->buffer, $this->offset, 4);
261 9
        $this->offset += 4;
262
263 9
        $num = unpack('N', $num);
264
265 9
        return $num[1];
266
    }
267
268 7
    private function unpackU64()
269
    {
270 7
        $this->ensureLength(8);
271
272 7
        $num = substr($this->buffer, $this->offset, 8);
273 7
        $this->offset += 8;
274
275
        //$num = unpack('J', $num);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
276
277 7
        $set = unpack('N2', $num);
278 7
        $value = $set[1] << 32 | $set[2];
279
280
        // PHP does not support unsigned integers.
281
        // If a number is bigger than 2^63, it will be interpreted as a float.
282
        // @link http://php.net/manual/en/language.types.integer.php#language.types.integer.overflow
283
284 7
        return ($value < 0) ? $this->handleBigInt($value) : $value;
285
    }
286
287 13
    private function unpackI8()
288
    {
289 13
        $this->ensureLength(1);
290
291 13
        $num = $this->buffer[$this->offset];
292 13
        $this->offset += 1;
293
294 13
        $num = unpack('c', $num);
295
296 13
        return $num[1];
297
    }
298
299 3 View Code Duplication
    private function unpackI16()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300
    {
301 3
        $this->ensureLength(2);
302
303 3
        $num = $this->buffer[$this->offset].$this->buffer[$this->offset + 1];
304 3
        $this->offset += 2;
305
306 3
        $num = unpack('s', strrev($num));
307
308 3
        return $num[1];
309
    }
310
311 3
    private function unpackI32()
312
    {
313 3
        $this->ensureLength(4);
314
315 3
        $num = substr($this->buffer, $this->offset, 4);
316 3
        $this->offset += 4;
317
318 3
        $num = unpack('i', strrev($num));
319
320 3
        return $num[1];
321
    }
322
323 3 View Code Duplication
    private function unpackI64()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
324
    {
325 3
        $this->ensureLength(8);
326
327 3
        $num = substr($this->buffer, $this->offset, 8);
328 3
        $this->offset += 8;
329
330 3
        $set = unpack('N2', $num);
331
332 3
        return $set[1] << 32 | $set[2];
333
    }
334
335 2
    private function unpackFloat()
336
    {
337 2
        $this->ensureLength(4);
338
339 2
        $num = substr($this->buffer, $this->offset, 4);
340 2
        $this->offset += 4;
341
342 2
        $num = unpack('f', strrev($num));
343
344 2
        return $num[1];
345
    }
346
347 3
    private function unpackDouble()
348
    {
349 3
        $this->ensureLength(8);
350
351 3
        $num = substr($this->buffer, $this->offset, 8);
352 3
        $this->offset += 8;
353
354 3
        $num = unpack('d', strrev($num));
355
356 3
        return $num[1];
357
    }
358
359 25
    private function unpackStr($length)
360
    {
361 25
        if (!$length) {
362 1
            return '';
363
        }
364
365 24
        $this->ensureLength($length);
366
367 24
        $str = substr($this->buffer, $this->offset, $length);
368 24
        $this->offset += $length;
369
370 24
        return $str;
371
    }
372
373 9
    private function unpackArr($size)
374
    {
375 9
        $array = [];
376
377 9
        for ($i = $size; $i; $i--) {
378 8
            $array[] = $this->unpack();
379 8
        }
380
381 9
        return $array;
382
    }
383
384
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
385
    private function unpackArrSpl($size)
386
    {
387
        $array = new \SplFixedArray($size);
388
389
        for ($i = 0; $i < $size; $i++) {
390
            $array[$i] = $this->unpack();
391
        }
392
393
        return $array;
394
    }
395
    */
396
397 13
    private function unpackMap($size)
398
    {
399 13
        $map = [];
400
401 13
        for ($i = $size; $i; $i--) {
402 12
            $key = $this->unpack();
403 12
            $value = $this->unpack();
404
405 12
            $map[$key] = $value;
406 12
        }
407
408 13
        return $map;
409
    }
410
411 10
    private function unpackExt($length)
412
    {
413 10
        $this->ensureLength($length);
414
415 10
        $type = $this->unpackI8();
416 10
        $data = substr($this->buffer, $this->offset, $length);
417
418 10
        if ($this->transformers && $transformer = $this->transformers->find($type)) {
419 1
            return $transformer->reverseTransform($this->unpack());
420
        }
421
422 9
        $this->offset += $length;
423
424 9
        return new Ext($type, $data);
425
    }
426
427 92
    private function ensureLength($length)
428
    {
429 92
        if (!isset($this->buffer[$this->offset + $length - 1])) {
430 4
            throw new InsufficientDataException($length, strlen($this->buffer) - $this->offset);
431
        }
432 90
    }
433
434 3
    private function handleBigInt($value)
435
    {
436 3
        if (self::BIGINT_AS_STR === $this->bigIntMode) {
437 1
            return sprintf('%u', $value);
438
        }
439 2
        if (self::BIGINT_AS_GMP === $this->bigIntMode) {
440 1
            return gmp_init(sprintf('%u', $value));
441
        }
442
443 1
        throw new IntegerOverflowException($value);
444
    }
445
}
446