Completed
Branch coverage (797ea6)
by Eugene
01:35
created

Packer::packMap()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
crap 2
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\Packable;
17
18
class Packer
19
{
20
    private 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 Packable[]|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 (null === $options) {
50 105
            $options = PackOptions::fromDefaults();
51 20
        } elseif (!$options instanceof PackOptions) {
52 20
            $options = PackOptions::fromBitmask($options);
53
        }
54
55 106
        $this->isDetectStrBin = $options->isDetectStrBinMode();
56 106
        $this->isForceStr = $options->isForceStrMode();
57 106
        $this->isDetectArrMap = $options->isDetectArrMapMode();
58 106
        $this->isForceArr = $options->isForceArrMode();
59 106
        $this->isForceFloat32 = $options->isForceFloat32Mode();
60 106
    }
61
62 2
    public function registerTransformer(Packable $transformer) : self
63
    {
64 2
        $this->transformers[] = $transformer;
65
66 2
        return $this;
67
    }
68
69 101
    public function pack($value)
70
    {
71 101
        if (\is_int($value)) {
72 54
            return $this->packInt($value);
73
        }
74 70
        if (\is_string($value)) {
75 33
            if ($this->isDetectStrBin) {
76 27
                return \preg_match(self::UTF8_REGEX, $value)
77 19
                    ? $this->packStr($value)
78 27
                    : $this->packBin($value);
79
            }
80
81 6
            return $this->isForceStr ? $this->packStr($value) : $this->packBin($value);
82
        }
83 47
        if (\is_array($value)) {
84 26
            if ($this->isDetectArrMap) {
85 19
                return \array_values($value) === $value
86 10
                    ? $this->packArray($value)
87 19
                    : $this->packMap($value);
88
            }
89
90 7
            return $this->isForceArr ? $this->packArray($value) : $this->packMap($value);
91
        }
92 25
        if (null === $value) {
93 3
            return "\xc0";
94
        }
95 24
        if (\is_bool($value)) {
96 6
            return $value ? "\xc3" : "\xc2";
97
        }
98 19
        if (\is_float($value)) {
99 6
            return $this->packFloat($value);
100
        }
101 13
        if ($value instanceof Ext) {
102 9
            return $this->packExt($value->type, $value->data);
103
        }
104 4
        if ($this->transformers) {
105 2
            foreach ($this->transformers as $transformer) {
106 2
                if (null !== $packed = $transformer->pack($this, $value)) {
107 2
                    return $packed;
108
                }
109
            }
110
        }
111
112 3
        throw new PackingFailedException($value, 'Unsupported type.');
113
    }
114
115
    public function packNil()
116
    {
117
        return "\xc0";
118
    }
119
120
    public function packBool($bool)
121
    {
122
        return $bool ? "\xc3" : "\xc2";
123
    }
124
125 54
    public function packInt($int)
126
    {
127 54
        if ($int >= 0) {
128 38
            if ($int <= 0x7f) {
129 25
                return \chr($int);
130
            }
131 17
            if ($int <= 0xff) {
132 6
                return "\xcc".\chr($int);
133
            }
134 13
            if ($int <= 0xffff) {
135 6
                return "\xcd".\chr($int >> 8).\chr($int);
136
            }
137 8
            if ($int <= 0xffffffff) {
138 5
                return \pack('CN', 0xce, $int);
139
            }
140
141 4
            return \pack('CJ', 0xcf, $int);
142
        }
143
144 17
        if ($int >= -0x20) {
145 4
            return \chr(0xe0 | $int);
146
        }
147 13
        if ($int >= -0x80) {
148 3
            return "\xd0".\chr($int);
149
        }
150 10
        if ($int >= -0x8000) {
151 3
            return "\xd1".\chr($int >> 8).\chr($int);
152
        }
153 7
        if ($int >= -0x80000000) {
154 3
            return \pack('CN', 0xd2, $int);
155
        }
156
157 4
        return \pack('CJ', 0xd3, $int);
158
    }
159
160 6
    public function packFloat($float)
161
    {
162 6
        return $this->isForceFloat32
163 1
            ? "\xca".\pack('G', $float)
164 6
            : "\xcb".\pack('E', $float);
165
    }
166
167 22
    public function packStr($str)
168
    {
169 22
        $length = \strlen($str);
170
171 22
        if ($length < 32) {
172 15
            return \chr(0xa0 | $length).$str;
173
        }
174 7
        if ($length <= 0xff) {
175 4
            return "\xd9".\chr($length).$str;
176
        }
177 3
        if ($length <= 0xffff) {
178 2
            return "\xda".\chr($length >> 8).\chr($length).$str;
179
        }
180
181 1
        return \pack('CN', 0xdb, $length).$str;
182
    }
183
184 13
    public function packBin($str)
185
    {
186 13
        $length = \strlen($str);
187
188 13
        if ($length <= 0xff) {
189 11
            return "\xc4".\chr($length).$str;
190
        }
191 2
        if ($length <= 0xffff) {
192 1
            return "\xc5".\chr($length >> 8).\chr($length).$str;
193
        }
194
195 1
        return \pack('CN', 0xc6, $length).$str;
196
    }
197
198 13
    public function packArray(array $array)
199
    {
200 13
        $data = $this->packArrayHeader(\count($array));
201
202 13
        foreach ($array as $val) {
203 12
            $data .= $this->pack($val);
204
        }
205
206 13
        return $data;
207
    }
208
209 13
    public function packArrayHeader($size)
210
    {
211 13
        if ($size <= 0xf) {
212 10
            return \chr(0x90 | $size);
213
        }
214 3
        if ($size <= 0xffff) {
215 2
            return "\xdc".\chr($size >> 8).\chr($size);
216
        }
217
218 1
        return \pack('CN', 0xdd, $size);
219
    }
220
221 15
    public function packMap(array $map)
222
    {
223 15
        $data = $this->packMapHeader(\count($map));
224
225 15
        foreach ($map as $key => $val) {
226 15
            $data .= $this->pack($key);
227 15
            $data .= $this->pack($val);
228
        }
229
230 15
        return $data;
231
    }
232
233 15
    public function packMapHeader($size)
234
    {
235 15
        if ($size <= 0xf) {
236 12
            return \chr(0x80 | $size);
237
        }
238 3
        if ($size <= 0xffff) {
239 2
            return "\xde".\chr($size >> 8).\chr($size);
240
        }
241
242 1
        return \pack('CN', 0xdf, $size);
243
    }
244
245 9
    public function packExt($type, $data)
246
    {
247 9
        $length = \strlen($data);
248
249 9
        switch ($length) {
250 9
            case 1: return "\xd4".\chr($type).$data;
251 8
            case 2: return "\xd5".\chr($type).$data;
252 7
            case 4: return "\xd6".\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...
253 6
            case 8: return "\xd7".\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...
254 5
            case 16: return "\xd8".\chr($type).$data;
255
        }
256
257 4
        if ($length <= 0xff) {
258 2
            return "\xc7".\chr($length).\chr($type).$data;
259
        }
260 2
        if ($length <= 0xffff) {
261 1
            return \pack('CnC', 0xc8, $length, $type).$data;
262
        }
263
264 1
        return \pack('CNC', 0xc9, $length, $type).$data;
265
    }
266
}
267