Completed
Pull Request — master (#12)
by Eugene
02:39
created

Packer::packUint64()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
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\PackingFailedException;
15
use MessagePack\TypeTransformer\Collection;
16
17
class Packer
18
{
19
    const FORCE_STR = 0b0001;
20
    const FORCE_BIN = 0b0010;
21
    const FORCE_ARR = 0b0100;
22
    const FORCE_MAP = 0b1000;
23
24
    private $strDetectionMode;
25
    private $arrDetectionMode;
26
27
    const NON_UTF8_REGEX = '/(
28
        [\xC0-\xC1] # Invalid UTF-8 Bytes
29
        | [\xF5-\xFF] # Invalid UTF-8 Bytes
30
        | \xE0[\x80-\x9F] # Overlong encoding of prior code point
31
        | \xF0[\x80-\x8F] # Overlong encoding of prior code point
32
        | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
33
        | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
34
        | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
35
        | (?<=[\x0-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
36
        | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
37
        | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
38
        | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
39
        | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
40
    )/x';
41
42
    /**
43
     * @var Collection
44
     */
45
    private $transformers;
46
47
    /**
48
     * @param int|null $typeDetectionMode
49
     */
50 112
    public function __construct($typeDetectionMode = null)
51
    {
52 112
        if (null !== $typeDetectionMode) {
53 12
            $this->setTypeDetectionMode($typeDetectionMode);
54 12
        }
55 112
    }
56
57
    /**
58
     * @param int $mode
59
     */
60 34
    public function setTypeDetectionMode($mode)
61
    {
62 34
        if ($mode > 0b1010 || $mode < 0 || 0b11 === ($mode & 0b11)) {
63 10
            throw new \InvalidArgumentException('Invalid type detection mode.');
64
        }
65
66 24
        $this->strDetectionMode = $mode & 0b0011;
67 24
        $this->arrDetectionMode = $mode & 0b1100;
68 24
    }
69
70
    /**
71
     * @param Collection|null $transformers
72
     */
73 2
    public function setTransformers(Collection $transformers = null)
74
    {
75 2
        $this->transformers = $transformers;
76 2
    }
77
78
    /**
79
     * @return Collection|null
80
     */
81 1
    public function getTransformers()
82
    {
83 1
        return $this->transformers;
84
    }
85
86 101
    public function pack($value)
87
    {
88 101
        if (\is_int($value)) {
89 55
            return $this->packInt($value);
90
        }
91 72
        if (\is_string($value)) {
92 39
            if (self::FORCE_STR === $this->strDetectionMode) {
93 6
                return $this->packStr($value);
94
            }
95 33
            if (self::FORCE_BIN === $this->strDetectionMode) {
96 6
                return $this->packBin($value);
97
            }
98 27
            return \preg_match(self::NON_UTF8_REGEX, $value)
99 27
                ? $this->packBin($value)
100 27
                : $this->packStr($value);
101
        }
102 47
        if (\is_array($value)) {
103 30
            if (self::FORCE_ARR === $this->arrDetectionMode) {
104 6
                return $this->packArray($value);
105
            }
106 24
            if (self::FORCE_MAP === $this->arrDetectionMode) {
107 6
                return $this->packMap($value);
108
            }
109 18
            return \array_values($value) === $value
110 18
                ? $this->packArray($value)
111 18
                : $this->packMap($value);
112
        }
113 21
        if (null === $value) {
114 3
            return $this->packNil();
115
        }
116 20
        if (\is_bool($value)) {
117 6
            return $this->packBool($value);
118
        }
119 15
        if (\is_double($value)) {
120 3
            return $this->packFloat($value);
121
        }
122 12
        if ($value instanceof Ext) {
123 9
            return $this->packExt($value);
124
        }
125
126 3
        if ($this->transformers && $transformer = $this->transformers->match($value)) {
127 1
            $ext = new Ext($transformer->getId(), $this->pack($transformer->transform($value)));
128
129 1
            return $this->packExt($ext);
130
        }
131
132 2
        throw new PackingFailedException($value, 'Unsupported type.');
133
    }
134
135 15
    public function packArray(array $array)
136
    {
137 15
        $size = \count($array);
138 15
        $data = self::packArrayHeader($size);
139
140 15
        foreach ($array as $val) {
141 14
            $data .= $this->pack($val);
142 15
        }
143
144 15
        return $data;
145
    }
146
147 15 View Code Duplication
    private static function packArrayHeader($size)
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...
148
    {
149 15
        if ($size <= 0xf) {
150 12
            return \chr(0x90 | $size);
151
        }
152 3
        if ($size <= 0xffff) {
153 2
            return "\xdc".\chr($size >> 8).\chr($size);
154
        }
155
156 1
        return \pack('CN', 0xdd, $size);
157
    }
158
159 17
    public function packMap(array $map)
160
    {
161 17
        $size = \count($map);
162 17
        $data = self::packMapHeader($size);
163
164 17
        foreach ($map as $key => $val) {
165 17
            $data .= $this->pack($key);
166 17
            $data .= $this->pack($val);
167 17
        }
168
169 17
        return $data;
170
    }
171
172 17 View Code Duplication
    private static function packMapHeader($size)
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...
173
    {
174 17
        if ($size <= 0xf) {
175 14
            return \chr(0x80 | $size);
176
        }
177 3
        if ($size <= 0xffff) {
178 2
            return "\xde".\chr($size >> 8).\chr($size);
179
        }
180
181 1
        return \pack('CN', 0xdf, $size);
182
    }
183
184 25
    public function packStr($str)
185
    {
186 25
        $len = \strlen($str);
187
188 25
        if ($len < 32) {
189 18
            return \chr(0xa0 | $len).$str;
190
        }
191 7
        if ($len <= 0xff) {
192 4
            return "\xd9".\chr($len).$str;
193
        }
194 3
        if ($len <= 0xffff) {
195 2
            return "\xda".\chr($len >> 8).\chr($len).$str;
196
        }
197
198 1
        return \pack('CN', 0xdb, $len).$str;
199
    }
200
201 16
    public function packBin($str)
202
    {
203 16
        $len = \strlen($str);
204
205 16
        if ($len <= 0xff) {
206 14
            return "\xc4".\chr($len).$str;
207
        }
208 2
        if ($len <= 0xffff) {
209 1
            return "\xc5".\chr($len >> 8).\chr($len).$str;
210
        }
211
212 1
        return \pack('CN', 0xc6, $len).$str;
213
    }
214
215 10
    public function packExt(Ext $ext)
216
    {
217 10
        $type = $ext->getType();
218 10
        $data = $ext->getData();
219 10
        $len = \strlen($data);
220
221
        switch ($len) {
222 10
            case 1: return "\xd4".\chr($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...
223 8
            case 2: return "\xd5".\chr($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...
224 7
            case 4: return "\xd6".\chr($type).$data;
225 6
            case 8: return "\xd7".\chr($type).$data;
226 5
            case 16: return "\xd8".\chr($type).$data;
227
        }
228
229 4
        if ($len <= 0xff) {
230 2
            return "\xc7".\chr($len).\chr($type).$data;
231
        }
232 2
        if ($len <= 0xffff) {
233 1
            return \pack('CnC', 0xc8, $len, $type).$data;
234
        }
235
236 1
        return \pack('CNC', 0xc9, $len, $type).$data;
237
    }
238
239 3
    public function packNil()
240
    {
241 3
        return "\xc0";
242
    }
243
244 6
    public function packBool($val)
245
    {
246 6
        return $val ? "\xc3" : "\xc2";
247
    }
248
249 3
    public function packFloat($num)
250
    {
251 3
        return "\xcb".strrev(pack('d', $num));
252
    }
253
254 55
    public function packInt($num)
255
    {
256 55
        if ($num >= 0) {
257 40
            if ($num <= 0x7f) {
258 28
                return \chr($num);
259
            }
260 16
            if ($num <= 0xff) {
261 6
                return "\xcc".\chr($num);
262
            }
263 12
            if ($num <= 0xffff) {
264 6
                return "\xcd".\chr($num >> 8).\chr($num);
265
            }
266 7
            if ($num <= 0xffffffff) {
267 5
                return \pack('CN', 0xce, $num);
268
            }
269
270 3
            return self::packUint64(0xcf, $num);
271
        }
272
273 16
        if ($num >= -0x20) {
274 4
            return \chr(0xe0 | $num);
275
        }
276 12
        if ($num >= -0x80) {
277 3
            return "\xd0".\chr($num);
278
        }
279 9
        if ($num >= -0x8000) {
280 3
            return "\xd1".\chr($num >> 8).\chr($num);
281
        }
282 6
        if ($num >= -0x80000000) {
283 3
            return \pack('CN', 0xd2, $num);
284
        }
285
286 3
        return self::packUint64(0xd3, $num);
287
    }
288
289 6
    private static function packUint64($code, $num)
290
    {
291 6
        $hi = ($num & 0xffffffff00000000) >> 32;
292 6
        $lo = $num & 0x00000000ffffffff;
293
294 6
        return \pack('CNN', $code, $hi, $lo);
295
    }
296
}
297