Completed
Branch transformers (fd9f11)
by Eugene
01:40
created

Packer::pack()   C

Complexity

Conditions 18
Paths 17

Size

Total Lines 49
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 18.0109

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 49
ccs 30
cts 31
cp 0.9677
rs 5.1048
cc 18
eloc 31
nc 17
nop 1
crap 18.0109

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
    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[]
39
     */
40
    private $transformers = [];
41
42
    /**
43
     * @param PackOptions|int|null $options
44
     *
45
     * @throws InvalidOptionException
46
     */
47 109
    public function __construct($options = null)
48
    {
49 109
        if (!$options instanceof PackOptions) {
50 109
            $options = PackOptions::fromBitmask($options);
51
        }
52
53 109
        $this->isDetectStrBin = $options->isDetectStrBinMode();
54 109
        $this->isForceStr = $options->isForceStrMode();
55 109
        $this->isDetectArrMap = $options->isDetectArrMapMode();
56 109
        $this->isForceArr = $options->isForceArrMode();
57 109
        $this->isForceFloat32 = $options->isForceFloat32Mode();
58 109
    }
59
60 1
    public function registerTransformer(Packable $transformer)
61
    {
62 1
        $this->transformers[] = $transformer;
63 1
    }
64
65 98
    public function pack($value)
66
    {
67 98
        if (\is_int($value)) {
68 52
            return $this->packInt($value);
69
        }
70 69
        if (\is_string($value)) {
71 33
            if ($this->isDetectStrBin) {
72 27
                return \preg_match(self::UTF8_REGEX, $value)
73 19
                    ? $this->packStr($value)
74 27
                    : $this->packBin($value);
75
            }
76
77 6
            return $this->isForceStr ? $this->packStr($value) : $this->packBin($value);
78
        }
79 46
        if (\is_array($value)) {
80 26
            if ($this->isDetectArrMap) {
81 19
                return \array_values($value) === $value
82 10
                    ? $this->packArray($value)
83 19
                    : $this->packMap($value);
84
            }
85
86 7
            return $this->isForceArr ? $this->packArray($value) : $this->packMap($value);
87
        }
88 24
        if (null === $value) {
89 3
            return $this->packNil();
90
        }
91 23
        if (\is_bool($value)) {
92 6
            return $this->packBool($value);
93
        }
94 18
        if (\is_float($value)) {
95 6
            return $this->isForceFloat32
96 1
                ? $this->packFloat32($value)
97 6
                : $this->packFloat64($value);
98
        }
99 12
        if ($value instanceof Ext) {
100 9
            return $this->packExt($value->type, $value->data);
101
        }
102 3
        if ($this->transformers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->transformers of type MessagePack\TypeTransformer\Packable[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
103 1
            foreach ($this->transformers as $transformer) {
104 1
                if (null === $packed = $transformer->pack($this, $value)) {
105
                    continue;
106
                }
107
108 1
                return $packed;
109
            }
110
        }
111
112 2
        throw new PackingFailedException($value, 'Unsupported type.');
113
    }
114
115 13
    public function packArray(array $array)
116
    {
117 13
        $size = \count($array);
118
119 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...
120 10
            $data = \chr(0x90 | $size);
121 3
        } elseif ($size <= 0xffff) {
122 2
            $data = "\xdc".\chr($size >> 8).\chr($size);
123
        } else {
124 1
            $data = \pack('CN', 0xdd, $size);
125
        }
126
127 13
        foreach ($array as $val) {
128 12
            $data .= $this->pack($val);
129
        }
130
131 13
        return $data;
132
    }
133
134 15
    public function packMap(array $map)
135
    {
136 15
        $size = \count($map);
137
138 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...
139 12
            $data = \chr(0x80 | $size);
140 3
        } elseif ($size <= 0xffff) {
141 2
            $data = "\xde".\chr($size >> 8).\chr($size);
142
        } else {
143 1
            $data = \pack('CN', 0xdf, $size);
144
        }
145
146 15
        foreach ($map as $key => $val) {
147 15
            $data .= $this->pack($key);
148 15
            $data .= $this->pack($val);
149
        }
150
151 15
        return $data;
152
    }
153
154 22
    public function packStr($str)
155
    {
156 22
        $len = \strlen($str);
157
158 22
        if ($len < 32) {
159 15
            return \chr(0xa0 | $len).$str;
160
        }
161 7
        if ($len <= 0xff) {
162 4
            return "\xd9".\chr($len).$str;
163
        }
164 3
        if ($len <= 0xffff) {
165 2
            return "\xda".\chr($len >> 8).\chr($len).$str;
166
        }
167
168 1
        return \pack('CN', 0xdb, $len).$str;
169
    }
170
171 13
    public function packBin($str)
172
    {
173 13
        $len = \strlen($str);
174
175 13
        if ($len <= 0xff) {
176 11
            return "\xc4".\chr($len).$str;
177
        }
178 2
        if ($len <= 0xffff) {
179 1
            return "\xc5".\chr($len >> 8).\chr($len).$str;
180
        }
181
182 1
        return \pack('CN', 0xc6, $len).$str;
183
    }
184
185 9
    public function packExt($type, $data)
186
    {
187 9
        $len = \strlen($data);
188
189 9
        switch ($len) {
190 9
            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...
191 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...
192 7
            case 4: return "\xd6".\chr($type).$data;
193 6
            case 8: return "\xd7".\chr($type).$data;
194 5
            case 16: return "\xd8".\chr($type).$data;
195
        }
196
197 4
        if ($len <= 0xff) {
198 2
            return "\xc7".\chr($len).\chr($type).$data;
199
        }
200 2
        if ($len <= 0xffff) {
201 1
            return \pack('CnC', 0xc8, $len, $type).$data;
202
        }
203
204 1
        return \pack('CNC', 0xc9, $len, $type).$data;
205
    }
206
207 3
    public function packNil()
208
    {
209 3
        return "\xc0";
210
    }
211
212 6
    public function packBool($val)
213
    {
214 6
        return $val ? "\xc3" : "\xc2";
215
    }
216
217 3
    public function packFloat32($num)
218
    {
219 3
        return "\xca".\strrev(\pack('f', $num));
220
    }
221
222 5
    public function packFloat64($num)
223
    {
224 5
        return "\xcb".\strrev(\pack('d', $num));
225
    }
226
227 52
    public function packInt($num)
228
    {
229 52
        if ($num >= 0) {
230 37
            if ($num <= 0x7f) {
231 25
                return \chr($num);
232
            }
233 16
            if ($num <= 0xff) {
234 6
                return "\xcc".\chr($num);
235
            }
236 12
            if ($num <= 0xffff) {
237 6
                return "\xcd".\chr($num >> 8).\chr($num);
238
            }
239 7
            if ($num <= 0xffffffff) {
240 5
                return \pack('CN', 0xce, $num);
241
            }
242
243 3
            return self::packUint64(0xcf, $num);
244
        }
245
246 16
        if ($num >= -0x20) {
247 4
            return \chr(0xe0 | $num);
248
        }
249 12
        if ($num >= -0x80) {
250 3
            return "\xd0".\chr($num);
251
        }
252 9
        if ($num >= -0x8000) {
253 3
            return "\xd1".\chr($num >> 8).\chr($num);
254
        }
255 6
        if ($num >= -0x80000000) {
256 3
            return \pack('CN', 0xd2, $num);
257
        }
258
259 3
        return self::packUint64(0xd3, $num);
260
    }
261
262 6
    private static function packUint64($code, $num)
263
    {
264 6
        $hi = ($num & 0xffffffff00000000) >> 32;
265 6
        $lo = $num & 0x00000000ffffffff;
266
267 6
        return \pack('CNN', $code, $hi, $lo);
268
    }
269
}
270