Test Setup Failed
Push — master ( f2b9b8...38e491 )
by Nikita
02:19
created

BinnList::_binnLoad()   C

Complexity

Conditions 16
Paths 44

Size

Total Lines 90
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 60
nc 44
nop 1
dl 0
loc 90
rs 5.5666
c 0
b 0
f 0

How to fix   Long Method    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
namespace Knik\Binn;
4
5
use Knik\Binn\Exceptions\InvalidArrayException;
6
7
/**
8
 * @method BinnList addBool(boolean $value)
9
 * @method BinnList addUint8(integer $value)
10
 * @method BinnList addUint16(integer $value)
11
 * @method BinnList addUint32(integer $value)
12
 * @method BinnList addUint64(integer $value)
13
 * @method BinnList addInt8(integer $value)
14
 * @method BinnList addInt16(integer $value)
15
 * @method BinnList addInt32(integer $value)
16
 * @method BinnList addInt64(integer $value)
17
 * @method BinnList addFloat(string $value)
18
 * @method BinnList addDouble(string $value)
19
 * @method BinnList addStr(string $value)
20
 * @method BinnList addList(Binn $value)
21
 * @method BinnList addMap(Binn $value)
22
 * @method BinnList addObject(Binn $value)
23
 *
24
 */
25
class BinnList extends BinnAbstract
26
{
27
    protected $binnType = self::BINN_LIST;
28
29
    /**
30
     * @var array
31
     */
32
    private $methodsAssignments = [
33
        'addBool'      => self::BINN_BOOL,
34
        'addUint8'     => self::BINN_UINT8,
35
        'addUint16'    => self::BINN_UINT16,
36
        'addUint32'    => self::BINN_UINT32,
37
        'addUint64'    => self::BINN_UINT64,
38
        'addInt8'      => self::BINN_INT8,
39
        'addInt16'     => self::BINN_INT16,
40
        'addInt32'     => self::BINN_INT32,
41
        'addInt64'     => self::BINN_INT64,
42
        'addFloat'     => self::BINN_FLOAT32,
43
        'addDouble'    => self::BINN_FLOAT64,
44
        'addStr'       => self::BINN_STRING,
45
        'addList'      => self::BINN_LIST,
46
        'addMap'       => self::BINN_MAP,
47
        'addObject'    => self::BINN_OBJECT,
48
    ];
49
50
    public function __construct($binnString = '')
51
    {
52
        $this->binnClass = self::class;
53
54
        if ($binnString != '') {
55
            $this->_binnLoad($binnString);
56
        }
57
58
        return $this;
59
    }
60
61
    /**
62
     * @param $name
63
     * @param $arguments
64
     * @return $this
65
     *
66
     * @throws \Exception
67
     */
68
    public function __call($name, $arguments)
69
    {
70
        if (array_key_exists($name, $this->methodsAssignments)) {
71
            $this->_addVal($this->methodsAssignments[$name], $arguments[0]);
72
            return $this;
73
        }
74
75
        throw new \Exception("Call to undefined method {$name}");
76
    }
77
78
    /**
79
     * @param string $binnString
80
     */
81
    public function binnOpen($binnString = '')
82
    {
83
        if ($binnString != '') {
84
            $this->_binnLoad($binnString);
85
        }
86
    }
87
88
    /**
89
     * Get binary string
90
     *
91
     * @return string
92
     */
93
    public function getBinnVal()
94
    {
95
        $this->calculateSize();
96
97
        $this->binnString = '';
98
        $this->binnString .= $this->pack(self::BINN_UINT8, $this->binnType);
99
100
        $this->binnString .= $this->packSize($this->size);
101
102
        $count = count($this->binnArr);
103
        $this->binnString .= $this->packSize($count);
104
105
        foreach ($this->binnArr as &$arr) {
106
            $type = $arr[self::KEY_TYPE];
107
            $storageType = $this->storageType($type);
108
109
            if ($type === self::BINN_BOOL) {
110
                $this->binnString .= $arr[self::KEY_VAL]
111
                    ? $this->packType(self::BINN_TRUE)
112
                    : $this->packType(self::BINN_FALSE);
113
114
                continue;
115
            }
116
117
            if ($storageType === self::BINN_STORAGE_QWORD
118
                || $storageType === self::BINN_STORAGE_DWORD
119
                || $storageType === self::BINN_STORAGE_WORD
120
                || $storageType === self::BINN_STORAGE_BYTE
121
            ) {
122
                $this->binnString .= $this->packType($arr[self::KEY_TYPE]);
123
                $this->binnString .= $this->pack($arr[self::KEY_TYPE], $arr[self::KEY_VAL]);
124
            } else if ($storageType === self::BINN_STORAGE_NOBYTES) {
125
                $this->binnString .= $this->packType($arr[self::KEY_TYPE]);
126
            } else if ($storageType === self::BINN_STORAGE_STRING) {
127
                $this->binnString .= $this->packType(self::BINN_STRING);
128
                $this->binnString .= $this->packSize($arr[self::KEY_SIZE]);
129
                $this->binnString .= $this->pack(self::BINN_STRING, $arr[self::KEY_VAL]);
130
                $this->binnString .= $this->pack(self::BINN_NULL);
131
            } else if ($storageType === self::BINN_STORAGE_CONTAINER) {
132
                $this->binnString .= $arr[self::KEY_VAL]->getBinnVal();
133
            }
134
        }
135
136
        return $this->binnString;
137
    }
138
139
    /**
140
     * Check is valid array to serialize
141
     *
142
     * @param $array
143
     * @return bool
144
     */
145
    public static function validArray($array)
146
    {
147
        $array = (array)$array;
148
        if (self::isArrayAssoc($array)) {
149
            return false;
150
        }
151
152
        return true;
153
    }
154
155
    /**
156
     * @param array $array
157
     * @return string
158
     */
159
    public function serialize($array = [])
160
    {
161
        if (empty($array)) {
162
            return $this->getBinnVal();
163
        }
164
165
        $this->binnFree();
166
167
        if ($this->isArrayAssoc($array)) {
168
            throw new InvalidArrayException('Array should be sequential');
169
        }
170
171
        foreach ($array as $item) {
172
            $type = $this->detectType($item);
173
            $storageType = $this->storageType($type);
174
175
            if ($storageType === self::BINN_STORAGE_CONTAINER) {
176
                foreach ($this->containersClasses as $contanerType => $containersClass)
177
                {
178
                    if ($containersClass::validArray($item)) {
179
                        $container = new $containersClass();
180
                        $container->serialize($item);
181
                        $item = $container;
182
                        break;
183
                    }
184
                }
185
            }
186
187
            $this->_addVal($type, $item);
188
        }
189
190
        return $this->getBinnVal();
191
    }
192
193
    /**
194
     * @param string $binnString
195
     * @return array
196
     */
197
    public function unserialize($binnString = '')
198
    {
199
        if (empty($binnString)) {
200
            return $this->getBinnArr();
201
        }
202
203
        $this->binnFree();
204
205
        $this->binnOpen($binnString);
206
        return $this->getBinnArr();
207
    }
208
209
    /**
210
     * @param int   $type
211
     * @param mixed $value
212
     */
213
    private function _addVal($type, $value)
214
    {
215
        if (in_array($type,
216
            [self::BINN_INT64, self::BINN_INT32, self::BINN_INT16,
217
                self::BINN_UINT64,self::BINN_UINT32, self::BINN_UINT16])
218
        ) {
219
            $type = $this->compressInt($type, $value);
220
        }
221
222
        $size = $this->getTypeSize($type, $value);
223
224
        $this->dataSize += $size['data'];
225
        $this->metaSize += $size['meta'];
226
227
        $this->count++;
228
229
        $this->binnArr[] = [
230
            self::KEY_TYPE      => $type,
231
            self::KEY_VAL       => $value,
232
            self::KEY_SIZE      => $size['data']
233
        ];
234
    }
235
236
    /**
237
     * @param string
238
     */
239
    private function _binnLoad($binnString)
240
    {
241
        $pos = 1; // Position
242
        $sizeBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]);
243
244
        // Size
245
        if ($sizeBytes & 1 << 7) {
246
            $sizeBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4));
247
            $this->size = ($sizeBytes &~ (1 << 31)); // Cut bit
248
            $pos += 4;
249
        } else {
250
            $this->size = $sizeBytes;
0 ignored issues
show
Documentation Bug introduced by
It seems like $sizeBytes can also be of type boolean. However, the property $size is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
251
            $pos += 1;
252
        }
253
254
        unset($sizeBytes);
255
256
        $countBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]);
257
258
        // Size
259
        if ($countBytes & 1 << 7) {
260
            $countBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4));
261
            $this->count = ($countBytes &~ (1 << 31)); // Cut bit
262
            $pos += 4;
263
        } else {
264
            $this->count = $countBytes;
0 ignored issues
show
Documentation Bug introduced by
It seems like $countBytes can also be of type boolean. However, the property $count is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
265
            $pos += 1;
266
        }
267
268
        unset($countBytes);
269
270
        // Data
271
        $stop_while = false;
272
        while ($pos < $this->size && !$stop_while) {
273
            $varType = $this->unpack(self::BINN_UINT8, $binnString[$pos]);
274
            $varStorageType = $this->storageType($varType);
275
            $pos += 1;
276
277
            if ($varStorageType === self::BINN_STORAGE_QWORD
278
                || $varStorageType === self::BINN_STORAGE_DWORD
279
                || $varStorageType === self::BINN_STORAGE_WORD
280
                || $varStorageType === self::BINN_STORAGE_BYTE
281
                || $varStorageType === self::BINN_STORAGE_NOBYTES
282
            ) {
283
                $varSize = $this->getTypeSize($varType);
284
                $val = $this->unpack($varType, substr($binnString, $pos, $varSize['data']));
285
                $this->_addVal($varType, $val);
0 ignored issues
show
Bug introduced by
It seems like $varType can also be of type boolean; however, parameter $type of Knik\Binn\BinnList::_addVal() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

285
                $this->_addVal(/** @scrutinizer ignore-type */ $varType, $val);
Loading history...
286
                $pos += $varSize['data'];
287
288
            } else if ($varStorageType === self::BINN_STRING ) {
289
                $stringSize = $this->unpack(self::BINN_UINT8, $binnString[$pos]);
290
291
                // Size
292
                if ($stringSize & 1 << 7) {
293
                    $stringSize = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4));
294
                    $stringSize = ($stringSize &~ (1 << 31)); // Cut bit
295
                    $pos += 4;
296
                } else {
297
                    $pos += 1;
298
                }
299
300
                $this->_addVal(self::BINN_STRING, $this->unpack(
301
                    self::BINN_STRING,
302
                    substr($binnString, $pos, $stringSize)
303
                ));
304
305
                $pos += $stringSize;
306
                $pos += 1; // Null byte
307
            } else if ($varStorageType === self::BINN_STORAGE_CONTAINER) {
308
                $list_size = $this->unpack(self::BINN_UINT8, $binnString[$pos]);;
309
310
                // Size
311
                if ($list_size & 1 << 7) {
312
                    $list_size = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4));
313
                    $list_size = ($list_size &~ (1 << 31)); // Cut bit
314
                }
315
316
                $substring = substr($binnString, $pos-1, $list_size);
317
318
                foreach ($this->containersClasses as $containerType => $containersClass) {
319
                    if ($containerType === $varType) {
320
                        $container = new $containersClass($substring);
321
                        $this->_addVal($varType, $container);
322
                        break;
323
                    }
324
                }
325
326
                $pos += ($list_size-1);
327
            } else {
328
                $stop_while = true;
329
            }
330
        }
331
    }
332
}