Completed
Pull Request — master (#21)
by Eugene
09:41
created

Packer   C

Complexity

Total Complexity 62

Size/Duplication

Total Lines 258
Duplicated Lines 8.53 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 62
c 0
b 0
f 0
lcom 1
cbo 4
dl 22
loc 258
ccs 125
cts 125
cp 1
rs 5.9493

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 2
A registerTransformer() 0 4 1
D pack() 0 47 18
A packArray() 0 10 2
A packArrayLength() 11 11 3
A packMap() 0 11 2
A packMapLength() 11 11 3
A packStr() 0 16 4
A packBin() 0 13 3
B packExt() 0 21 8
A packNil() 0 4 1
A packBool() 0 4 2
A packFloat32() 0 4 1
A packFloat64() 0 4 1
D packInt() 0 34 10
A packUint64() 0 7 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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
    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 (!$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
    public function registerTransformer(Packable $transformer)
61
    {
62
        $this->transformers[] = $transformer;
63 2
    }
64
65 2
    public function pack($value)
66 2
    {
67
        if (\is_int($value)) {
68
            return $this->packInt($value);
69
        }
70
        if (\is_string($value)) {
71 1
            if ($this->isDetectStrBin) {
72
                return \preg_match(self::UTF8_REGEX, $value)
73 1
                    ? $this->packStr($value)
74
                    : $this->packBin($value);
75
            }
76 98
77
            return $this->isForceStr ? $this->packStr($value) : $this->packBin($value);
78 98
        }
79 53
        if (\is_array($value)) {
80
            if ($this->isDetectArrMap) {
81 69
                return \array_values($value) === $value
82 33
                    ? $this->packArray($value)
83 27
                    : $this->packMap($value);
84 19
            }
85 27
86
            return $this->isForceArr ? $this->packArray($value) : $this->packMap($value);
87
        }
88 6
        if (null === $value) {
89
            return $this->packNil();
90 46
        }
91 26
        if (\is_bool($value)) {
92 19
            return $this->packBool($value);
93 10
        }
94 19
        if (\is_float($value)) {
95
            return $this->isForceFloat32
96
                ? $this->packFloat32($value)
97 7
                : $this->packFloat64($value);
98
        }
99 24
        if ($value instanceof Ext) {
100 3
            return $this->packExt($value->type, $value->data);
101
        }
102 23
        if ($this->transformers) {
103 6
            foreach ($this->transformers as $transformer) {
104
                if (null !== $packed = $transformer->pack($this, $value)) {
105 18
                    return $packed;
106 6
                }
107 1
            }
108 6
        }
109
110 12
        throw new PackingFailedException($value, 'Unsupported type.');
111 9
    }
112
113
    public function packArray(array $array)
114 3
    {
115 1
        $data = $this->packArrayLength(\count($array));
116
117 1
        foreach ($array as $val) {
118
            $data .= $this->pack($val);
119
        }
120 2
121
        return $data;
122
    }
123 13
124 View Code Duplication
    public function packArrayLength($length)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
125 13
    {
126
        if ($length <= 0xf) {
127 13
            return \chr(0x90 | $length);
128 10
        }
129 3
        if ($length <= 0xffff) {
130 2
            return "\xdc".\chr($length >> 8).\chr($length);
131
        }
132 1
133
        return \pack('CN', 0xdd, $length);
134
    }
135 13
136 12
    public function packMap(array $map)
137
    {
138
        $data = $this->packMapLength(\count($map));
139 13
140
        foreach ($map as $key => $val) {
141
            $data .= $this->pack($key);
142 15
            $data .= $this->pack($val);
143
        }
144 15
145
        return $data;
146 15
    }
147 12
148 3 View Code Duplication
    public function packMapLength($length)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
149 2
    {
150
        if ($length <= 0xf) {
151 1
            return \chr(0x80 | $length);
152
        }
153
        if ($length <= 0xffff) {
154 15
            return "\xde".\chr($length >> 8).\chr($length);
155 15
        }
156 15
157
        return \pack('CN', 0xdf, $length);
158
    }
159 15
160
    public function packStr($str)
161
    {
162 22
        $length = \strlen($str);
163
164 22
        if ($length < 32) {
165
            return \chr(0xa0 | $length).$str;
166 22
        }
167 15
        if ($length <= 0xff) {
168
            return "\xd9".\chr($length).$str;
169 7
        }
170 4
        if ($length <= 0xffff) {
171
            return "\xda".\chr($length >> 8).\chr($length).$str;
172 3
        }
173 2
174
        return \pack('CN', 0xdb, $length).$str;
175
    }
176 1
177
    public function packBin($str)
178
    {
179 13
        $length = \strlen($str);
180
181 13
        if ($length <= 0xff) {
182
            return "\xc4".\chr($length).$str;
183 13
        }
184 11
        if ($length <= 0xffff) {
185
            return "\xc5".\chr($length >> 8).\chr($length).$str;
186 2
        }
187 1
188
        return \pack('CN', 0xc6, $length).$str;
189
    }
190 1
191
    public function packExt($type, $data)
192
    {
193 10
        $length = \strlen($data);
194
195 10
        switch ($length) {
196
            case 1: return "\xd4".\chr($type).$data;
197 10
            case 2: return "\xd5".\chr($type).$data;
198 10
            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...
199 8
            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...
200 7
            case 16: return "\xd8".\chr($type).$data;
201 6
        }
202 5
203
        if ($length <= 0xff) {
204
            return "\xc7".\chr($length).\chr($type).$data;
205 4
        }
206 2
        if ($length <= 0xffff) {
207
            return \pack('CnC', 0xc8, $length, $type).$data;
208 2
        }
209 1
210
        return \pack('CNC', 0xc9, $length, $type).$data;
211
    }
212 1
213
    public function packNil()
214
    {
215 3
        return "\xc0";
216
    }
217 3
218
    public function packBool($val)
219
    {
220 6
        return $val ? "\xc3" : "\xc2";
221
    }
222 6
223
    public function packFloat32($num)
224
    {
225 3
        return "\xca".\strrev(\pack('f', $num));
226
    }
227 3
228
    public function packFloat64($num)
229
    {
230 5
        return "\xcb".\strrev(\pack('d', $num));
231
    }
232 5
233
    public function packInt($num)
234
    {
235 53
        if ($num >= 0) {
236
            if ($num <= 0x7f) {
237 53
                return \chr($num);
238 38
            }
239 26
            if ($num <= 0xff) {
240
                return "\xcc".\chr($num);
241 16
            }
242 6
            if ($num <= 0xffff) {
243
                return "\xcd".\chr($num >> 8).\chr($num);
244 12
            }
245 6
            if ($num <= 0xffffffff) {
246
                return \pack('CN', 0xce, $num);
247 7
            }
248 5
249
            return self::packUint64(0xcf, $num);
250
        }
251 3
252
        if ($num >= -0x20) {
253
            return \chr(0xe0 | $num);
254 16
        }
255 4
        if ($num >= -0x80) {
256
            return "\xd0".\chr($num);
257 12
        }
258 3
        if ($num >= -0x8000) {
259
            return "\xd1".\chr($num >> 8).\chr($num);
260 9
        }
261 3
        if ($num >= -0x80000000) {
262
            return \pack('CN', 0xd2, $num);
263 6
        }
264 3
265
        return self::packUint64(0xd3, $num);
266
    }
267 3
268
    private static function packUint64($code, $num)
269
    {
270 6
        $hi = ($num & 0xffffffff00000000) >> 32;
271
        $lo = $num & 0x00000000ffffffff;
272 6
273 6
        return \pack('CNN', $code, $hi, $lo);
274
    }
275
}
276