Completed
Push — master ( d13485...5b0b5b )
by Eugene
07:33
created

src/Packer.php (4 issues)

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
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 13
        $data = self::packArrayHeader($size);
127
128 13
        foreach ($array as $val) {
129 12
            $data .= $this->pack($val);
130
        }
131
132 13
        return $data;
133
    }
134
135 13 View Code Duplication
    private static function packArrayHeader($size)
136
    {
137 13
        if ($size <= 0xf) {
138 10
            return \chr(0x90 | $size);
139
        }
140 3
        if ($size <= 0xffff) {
141 2
            return "\xdc".\chr($size >> 8).\chr($size);
142
        }
143
144 1
        return \pack('CN', 0xdd, $size);
145
    }
146
147 15
    public function packMap(array $map)
148
    {
149 15
        $size = \count($map);
150 15
        $data = self::packMapHeader($size);
151
152 15
        foreach ($map as $key => $val) {
153 15
            $data .= $this->pack($key);
154 15
            $data .= $this->pack($val);
155
        }
156
157 15
        return $data;
158
    }
159
160 15 View Code Duplication
    private static function packMapHeader($size)
161
    {
162 15
        if ($size <= 0xf) {
163 12
            return \chr(0x80 | $size);
164
        }
165 3
        if ($size <= 0xffff) {
166 2
            return "\xde".\chr($size >> 8).\chr($size);
167
        }
168
169 1
        return \pack('CN', 0xdf, $size);
170
    }
171
172 22
    public function packStr($str)
173
    {
174 22
        $len = \strlen($str);
175
176 22
        if ($len < 32) {
177 15
            return \chr(0xa0 | $len).$str;
178
        }
179 7
        if ($len <= 0xff) {
180 4
            return "\xd9".\chr($len).$str;
181
        }
182 3
        if ($len <= 0xffff) {
183 2
            return "\xda".\chr($len >> 8).\chr($len).$str;
184
        }
185
186 1
        return \pack('CN', 0xdb, $len).$str;
187
    }
188
189 13
    public function packBin($str)
190
    {
191 13
        $len = \strlen($str);
192
193 13
        if ($len <= 0xff) {
194 11
            return "\xc4".\chr($len).$str;
195
        }
196 2
        if ($len <= 0xffff) {
197 1
            return "\xc5".\chr($len >> 8).\chr($len).$str;
198
        }
199
200 1
        return \pack('CN', 0xc6, $len).$str;
201
    }
202
203 10
    public function packExt(Ext $ext)
204
    {
205 10
        $type = $ext->getType();
206 10
        $data = $ext->getData();
207 10
        $len = \strlen($data);
208
209 10
        switch ($len) {
210 10
            case 1: return "\xd4".\chr($type).$data;
0 ignored issues
show
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...
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...
211 8
            case 2: return "\xd5".\chr($type).$data;
0 ignored issues
show
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...
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...
212 7
            case 4: return "\xd6".\chr($type).$data;
213 6
            case 8: return "\xd7".\chr($type).$data;
214 5
            case 16: return "\xd8".\chr($type).$data;
215
        }
216
217 4
        if ($len <= 0xff) {
218 2
            return "\xc7".\chr($len).\chr($type).$data;
219
        }
220 2
        if ($len <= 0xffff) {
221 1
            return \pack('CnC', 0xc8, $len, $type).$data;
222
        }
223
224 1
        return \pack('CNC', 0xc9, $len, $type).$data;
225
    }
226
227 3
    public function packNil()
228
    {
229 3
        return "\xc0";
230
    }
231
232 6
    public function packBool($val)
233
    {
234 6
        return $val ? "\xc3" : "\xc2";
235
    }
236
237 3
    public function packFloat32($num)
238
    {
239 3
        return "\xca".\strrev(\pack('f', $num));
240
    }
241
242 5
    public function packFloat64($num)
243
    {
244 5
        return "\xcb".\strrev(\pack('d', $num));
245
    }
246
247 53
    public function packInt($num)
248
    {
249 53
        if ($num >= 0) {
250 38
            if ($num <= 0x7f) {
251 26
                return \chr($num);
252
            }
253 16
            if ($num <= 0xff) {
254 6
                return "\xcc".\chr($num);
255
            }
256 12
            if ($num <= 0xffff) {
257 6
                return "\xcd".\chr($num >> 8).\chr($num);
258
            }
259 7
            if ($num <= 0xffffffff) {
260 5
                return \pack('CN', 0xce, $num);
261
            }
262
263 3
            return self::packUint64(0xcf, $num);
264
        }
265
266 16
        if ($num >= -0x20) {
267 4
            return \chr(0xe0 | $num);
268
        }
269 12
        if ($num >= -0x80) {
270 3
            return "\xd0".\chr($num);
271
        }
272 9
        if ($num >= -0x8000) {
273 3
            return "\xd1".\chr($num >> 8).\chr($num);
274
        }
275 6
        if ($num >= -0x80000000) {
276 3
            return \pack('CN', 0xd2, $num);
277
        }
278
279 3
        return self::packUint64(0xd3, $num);
280
    }
281
282 6
    private static function packUint64($code, $num)
283
    {
284 6
        $hi = ($num & 0xffffffff00000000) >> 32;
285 6
        $lo = $num & 0x00000000ffffffff;
286
287 6
        return \pack('CNN', $code, $hi, $lo);
288
    }
289
}
290