Completed
Push — master ( 5b0b5b...a9e99b )
by Eugene
07:35
created

Packer::packMapHeader()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 11
loc 11
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
crap 3
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\InvalidOptionException;
15
use MessagePack\Exception\PackingFailedException;
16
use MessagePack\TypeTransformer\Collection;
17
18
class Packer
19
{
20
    const UTF8_REGEX = '/\A(?:
21
          [\x00-\x7F]++                      # ASCII
22
        | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
23
        |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
24
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
25
        |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
26
        |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
27
        | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
28
        |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
29
        )*+\z/x';
30
31
    private $isDetectStrBin;
32
    private $isForceStr;
33
    private $isDetectArrMap;
34
    private $isForceArr;
35
    private $isForceFloat32;
36
37
    /**
38
     * @var Collection|null
39
     */
40
    private $transformers;
41
42
    /**
43
     * @param PackOptions|int|null $options
44
     *
45
     * @throws InvalidOptionException
46
     */
47 106
    public function __construct($options = null)
48
    {
49 106
        if (!$options instanceof PackOptions) {
50 106
            $options = PackOptions::fromBitmask($options);
51
        }
52
53 106
        $this->isDetectStrBin = $options->isDetectStrBinMode();
54 106
        $this->isForceStr = $options->isForceStrMode();
55 106
        $this->isDetectArrMap = $options->isDetectArrMapMode();
56 106
        $this->isForceArr = $options->isForceArrMode();
57 106
        $this->isForceFloat32 = $options->isForceFloat32Mode();
58 106
    }
59
60
    /**
61
     * @param Collection|null $transformers
62
     */
63 2
    public function setTransformers(Collection $transformers = null)
64
    {
65 2
        $this->transformers = $transformers;
66 2
    }
67
68
    /**
69
     * @return Collection|null
70
     */
71 1
    public function getTransformers()
72
    {
73 1
        return $this->transformers;
74
    }
75
76 98
    public function pack($value)
77
    {
78 98
        if (\is_int($value)) {
79 53
            return $this->packInt($value);
80
        }
81 69
        if (\is_string($value)) {
82 33
            if ($this->isDetectStrBin) {
83 27
                return \preg_match(self::UTF8_REGEX, $value)
84 19
                    ? $this->packStr($value)
85 27
                    : $this->packBin($value);
86
            }
87
88 6
            return $this->isForceStr ? $this->packStr($value) : $this->packBin($value);
89
        }
90 46
        if (\is_array($value)) {
91 26
            if ($this->isDetectArrMap) {
92 19
                return \array_values($value) === $value
93 10
                    ? $this->packArray($value)
94 19
                    : $this->packMap($value);
95
            }
96
97 7
            return $this->isForceArr ? $this->packArray($value) : $this->packMap($value);
98
        }
99 24
        if (null === $value) {
100 3
            return $this->packNil();
101
        }
102 23
        if (\is_bool($value)) {
103 6
            return $this->packBool($value);
104
        }
105 18
        if (\is_float($value)) {
106 6
            return $this->isForceFloat32
107 1
                ? $this->packFloat32($value)
108 6
                : $this->packFloat64($value);
109
        }
110 12
        if ($value instanceof Ext) {
111 9
            return $this->packExt($value);
112
        }
113
114 3
        if ($this->transformers && $transformer = $this->transformers->match($value)) {
115 1
            $ext = new Ext($transformer->getId(), $this->pack($transformer->transform($value)));
116
117 1
            return $this->packExt($ext);
118
        }
119
120 2
        throw new PackingFailedException($value, 'Unsupported type.');
121
    }
122
123 13
    public function packArray(array $array)
124
    {
125 13
        $size = \count($array);
126
127 13 View Code Duplication
        if ($size <= 0xf) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
128 10
            $data = \chr(0x90 | $size);
129 3
        } elseif ($size <= 0xffff) {
130 2
            $data = "\xdc".\chr($size >> 8).\chr($size);
131
        } else {
132 1
            $data = \pack('CN', 0xdd, $size);
133
        }
134
135 13
        foreach ($array as $val) {
136 12
            $data .= $this->pack($val);
137
        }
138
139 13
        return $data;
140
    }
141
142 15
    public function packMap(array $map)
143
    {
144 15
        $size = \count($map);
145
146 15 View Code Duplication
        if ($size <= 0xf) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
147 12
            $data = \chr(0x80 | $size);
148 3
        } elseif ($size <= 0xffff) {
149 2
            $data = "\xde".\chr($size >> 8).\chr($size);
150
        } else {
151 1
            $data = \pack('CN', 0xdf, $size);
152
        }
153
154 15
        foreach ($map as $key => $val) {
155 15
            $data .= $this->pack($key);
156 15
            $data .= $this->pack($val);
157
        }
158
159 15
        return $data;
160
    }
161
162 22
    public function packStr($str)
163
    {
164 22
        $len = \strlen($str);
165
166 22
        if ($len < 32) {
167 15
            return \chr(0xa0 | $len).$str;
168
        }
169 7
        if ($len <= 0xff) {
170 4
            return "\xd9".\chr($len).$str;
171
        }
172 3
        if ($len <= 0xffff) {
173 2
            return "\xda".\chr($len >> 8).\chr($len).$str;
174
        }
175
176 1
        return \pack('CN', 0xdb, $len).$str;
177
    }
178
179 13
    public function packBin($str)
180
    {
181 13
        $len = \strlen($str);
182
183 13
        if ($len <= 0xff) {
184 11
            return "\xc4".\chr($len).$str;
185
        }
186 2
        if ($len <= 0xffff) {
187 1
            return "\xc5".\chr($len >> 8).\chr($len).$str;
188
        }
189
190 1
        return \pack('CN', 0xc6, $len).$str;
191
    }
192
193 10
    public function packExt(Ext $ext)
194
    {
195 10
        $len = \strlen($ext->data);
196
197 10
        switch ($len) {
198 10
            case 1: return "\xd4".\chr($ext->type).$ext->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...
199 8
            case 2: return "\xd5".\chr($ext->type).$ext->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...
200 7
            case 4: return "\xd6".\chr($ext->type).$ext->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...
201 6
            case 8: return "\xd7".\chr($ext->type).$ext->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...
202 5
            case 16: return "\xd8".\chr($ext->type).$ext->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
        }
204
205 4
        if ($len <= 0xff) {
206 2
            return "\xc7".\chr($len).\chr($ext->type).$ext->data;
207
        }
208 2
        if ($len <= 0xffff) {
209 1
            return \pack('CnC', 0xc8, $len, $ext->type).$ext->data;
210
        }
211
212 1
        return \pack('CNC', 0xc9, $len, $ext->type).$ext->data;
213
    }
214
215 3
    public function packNil()
216
    {
217 3
        return "\xc0";
218
    }
219
220 6
    public function packBool($val)
221
    {
222 6
        return $val ? "\xc3" : "\xc2";
223
    }
224
225 3
    public function packFloat32($num)
226
    {
227 3
        return "\xca".\strrev(\pack('f', $num));
228
    }
229
230 5
    public function packFloat64($num)
231
    {
232 5
        return "\xcb".\strrev(\pack('d', $num));
233
    }
234
235 53
    public function packInt($num)
236
    {
237 53
        if ($num >= 0) {
238 38
            if ($num <= 0x7f) {
239 26
                return \chr($num);
240
            }
241 16
            if ($num <= 0xff) {
242 6
                return "\xcc".\chr($num);
243
            }
244 12
            if ($num <= 0xffff) {
245 6
                return "\xcd".\chr($num >> 8).\chr($num);
246
            }
247 7
            if ($num <= 0xffffffff) {
248 5
                return \pack('CN', 0xce, $num);
249
            }
250
251 3
            return self::packUint64(0xcf, $num);
252
        }
253
254 16
        if ($num >= -0x20) {
255 4
            return \chr(0xe0 | $num);
256
        }
257 12
        if ($num >= -0x80) {
258 3
            return "\xd0".\chr($num);
259
        }
260 9
        if ($num >= -0x8000) {
261 3
            return "\xd1".\chr($num >> 8).\chr($num);
262
        }
263 6
        if ($num >= -0x80000000) {
264 3
            return \pack('CN', 0xd2, $num);
265
        }
266
267 3
        return self::packUint64(0xd3, $num);
268
    }
269
270 6
    private static function packUint64($code, $num)
271
    {
272 6
        $hi = ($num & 0xffffffff00000000) >> 32;
273 6
        $lo = $num & 0x00000000ffffffff;
274
275 6
        return \pack('CNN', $code, $hi, $lo);
276
    }
277
}
278