Completed
Push — master ( 3acf41...7af790 )
by Eugene
05:34
created

Packer   C

Complexity

Total Complexity 73

Size/Duplication

Total Lines 276
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 73
lcom 1
cbo 3
dl 0
loc 276
ccs 138
cts 138
cp 1
rs 5.5447
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A registerTransformer() 0 6 1
A __construct() 0 14 3
C pack() 0 52 21
A packNil() 0 4 1
A packBool() 0 4 2
D packInt() 0 34 10
A packFloat() 0 6 2
A packStr() 0 16 4
A packBin() 0 13 3
A packArray() 0 10 2
A packArrayHeader() 0 11 3
D packMap() 0 31 10
A packMapHeader() 0 11 3
B packExt() 0 21 8

How to fix   Complexity   

Complex Class

Complex classes like Packer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Packer, and based on these observations, apply Extract Interface, too.

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