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

BinnObject::_binnLoad()   C

Complexity

Conditions 16
Paths 44

Size

Total Lines 97
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 64
nc 44
nop 1
dl 0
loc 97
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
class BinnObject extends BinnAbstract
8
{
9
    protected $binnType = self::BINN_OBJECT;
10
11
    public function __construct($binnString = '')
12
    {
13
        $this->binnClass = self::class;
14
15
        if ($binnString != '') {
16
            $this->_binnLoad($binnString);
17
        }
18
19
        return $this;
20
    }
21
22
    /**
23
     * @param string
24
     */
25
    private function _binnLoad($binnString)
26
    {
27
        $pos = 1; // Position
28
        $sizeBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]);
29
30
        // Size
31
        if ($sizeBytes & 1 << 7) {
32
            $sizeBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4));
33
            $this->size = ($sizeBytes &~ (1 << 31)); // Cut bit
34
            $pos += 4;
35
        } else {
36
            $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...
37
            $pos += 1;
38
        }
39
40
        unset($sizeBytes);
41
42
        $countBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]);
43
44
        // Size
45
        if ($countBytes & 1 << 7) {
46
            $countBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4));
47
            $this->count = ($countBytes &~ (1 << 31)); // Cut bit
48
            $pos += 4;
49
        } else {
50
            $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...
51
            $pos += 1;
52
        }
53
54
        unset($countBytes);
55
56
        // Data
57
        $stopWhile = false;
58
        while ($pos < $this->size && !$stopWhile) {
59
            // Key size
60
            $varKeySize = $this->unpack(self::BINN_UINT8, substr($binnString, $pos, 1));
61
            $pos += 1;
62
63
            $varKey = $this->unpack(self::BINN_STRING, substr($binnString, $pos, $varKeySize));
0 ignored issues
show
Bug introduced by
It seems like $varKeySize can also be of type boolean; however, parameter $length of substr() 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

63
            $varKey = $this->unpack(self::BINN_STRING, substr($binnString, $pos, /** @scrutinizer ignore-type */ $varKeySize));
Loading history...
64
            $pos += $varKeySize;
65
66
            $varType = $this->unpack(self::BINN_UINT8, $binnString[$pos]);
67
            $varStorageType = $this->storageType($varType);
68
            $pos += 1;
69
70
            if ($varStorageType === self::BINN_STORAGE_QWORD
71
                || $varStorageType === self::BINN_STORAGE_DWORD
72
                || $varStorageType === self::BINN_STORAGE_WORD
73
                || $varStorageType === self::BINN_STORAGE_BYTE
74
                || $varStorageType === self::BINN_STORAGE_NOBYTES
75
            ) {
76
                $varSize = $this->getTypeSize($varType);
77
                $val = $this->unpack($varType, substr($binnString, $pos, $varSize['data']));
78
                $this->_addVal($varKey, $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\BinnObject::_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

78
                $this->_addVal($varKey, /** @scrutinizer ignore-type */ $varType, $val);
Loading history...
Bug introduced by
It seems like $varKey can also be of type boolean; however, parameter $key of Knik\Binn\BinnObject::_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

78
                $this->_addVal(/** @scrutinizer ignore-type */ $varKey, $varType, $val);
Loading history...
79
                $pos += $varSize['data'];
80
81
            } else if ($varStorageType === self::BINN_STRING ) {
82
                $stringSize = $this->unpack(self::BINN_UINT8, $binnString[$pos]);
83
84
                // Size
85
                if ($stringSize & 1 << 7) {
86
                    $stringSize = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4));
87
                    $stringSize = ($stringSize &~ (1 << 31)); // Cut bit
88
                    $pos += 4;
89
                } else {
90
                    $pos += 1;
91
                }
92
93
                $this->_addVal($varKey,self::BINN_STRING, $this->unpack(
94
                    self::BINN_STRING,
95
                    substr($binnString, $pos, $stringSize)
96
                ));
97
98
                $pos += $stringSize;
99
                $pos += 1; // Null byte
100
            } else if ($varStorageType === self::BINN_STORAGE_CONTAINER) {
101
                $list_size = $this->unpack(self::BINN_UINT8, $binnString[$pos]);;
102
103
                // Size
104
                if ($list_size & 1 << 7) {
105
                    $list_size = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4));
106
                    $list_size = ($list_size &~ (1 << 31)); // Cut bit
107
                }
108
109
                $substring = substr($binnString, $pos-1, $list_size);
110
111
                foreach ($this->containersClasses as $containerType => $containersClass) {
112
                    if ($containerType === $varType) {
113
                        $container = new $containersClass($substring);
114
                        $this->_addVal($varKey, $varType, $container);
115
                        break;
116
                    }
117
                }
118
119
                $pos += ($list_size-1);
120
            } else {
121
                $stopWhile = true;
122
            }
123
        }
124
    }
125
126
    /**
127
     * Get binary string
128
     *
129
     * @return string
130
     */
131
    public function getBinnVal()
132
    {
133
        $this->calculateSize();
134
135
        $this->binnString = '';
136
        $this->binnString .= $this->pack(self::BINN_UINT8, $this->binnType);
137
138
        $this->binnString .= $this->packSize($this->size);
139
140
        $count = count($this->binnArr);
141
        $this->binnString .= $this->packSize($count);
142
143
        foreach ($this->binnArr as &$arr) {
144
            $key = $arr[self::KEY_KEY];
145
            $type = $arr[self::KEY_TYPE];
146
            $storageType = $this->storageType($type);
147
148
            $this->binnString .= $this->pack(self::BINN_UINT8, mb_strlen($key));
149
            $this->binnString .= $this->pack(self::BINN_STRING, $key);
150
151
            if ($type === self::BINN_BOOL) {
152
                $this->binnString .= $arr[self::KEY_VAL]
153
                    ? $this->packType(self::BINN_TRUE)
154
                    : $this->packType(self::BINN_FALSE);
155
156
                continue;
157
            }
158
159
            if ($storageType === self::BINN_STORAGE_QWORD
160
                || $storageType === self::BINN_STORAGE_DWORD
161
                || $storageType === self::BINN_STORAGE_WORD
162
                || $storageType === self::BINN_STORAGE_BYTE
163
            ) {
164
                $this->binnString .= $this->packType($arr[self::KEY_TYPE]);
165
                $this->binnString .= $this->pack($arr[self::KEY_TYPE], $arr[self::KEY_VAL]);
166
            } else if ($storageType === self::BINN_STORAGE_NOBYTES) {
167
                $this->binnString .= $this->packType($arr[self::KEY_TYPE]);
168
            } else if ($storageType === self::BINN_STORAGE_STRING) {
169
                $this->binnString .= $this->packType(self::BINN_STRING);
170
                $this->binnString .= $this->packSize($arr[self::KEY_SIZE]);
171
                $this->binnString .= $this->pack(self::BINN_STRING, $arr[self::KEY_VAL]);
172
                $this->binnString .= $this->pack(self::BINN_NULL);
173
            } else if ($storageType === self::BINN_STORAGE_CONTAINER) {
174
                $this->binnString .= $arr[self::KEY_VAL]->getBinnVal();
175
            }
176
        }
177
178
        return $this->binnString;
179
    }
180
181
    /**
182
     * @param string $binnString
183
     */
184
    public function binnOpen($binnString = '')
185
    {
186
        if ($binnString != '') {
187
            $this->_binnLoad($binnString);
188
        }
189
    }
190
191
    /**
192
     * @param integer $key
193
     * @param int   $type
194
     * @param mixed $value
195
     */
196
    private function _addVal($key, $type, $value)
197
    {
198
        if (in_array($type,
199
            [self::BINN_INT64, self::BINN_INT32, self::BINN_INT16,
200
                self::BINN_UINT64,self::BINN_UINT32, self::BINN_UINT16])
201
        ) {
202
            $type = $this->compressInt($type, $value);
203
        }
204
205
        $size = $this->getTypeSize($type, $value);
206
207
        $this->dataSize += $size['data'];
208
        $this->metaSize += $size['meta'];
209
210
        // Key size. Size size + strlen
211
        $this->metaSize += 1 + strlen($key);
212
213
        $this->count++;
214
215
        $this->binnArr[] = [
216
            self::KEY_TYPE      => $type,
217
            self::KEY_VAL       => $value,
218
            self::KEY_SIZE      => $size['data'],
219
            self::KEY_KEY       => $key,
220
        ];
221
    }
222
223
    /**
224
     * Check is valid array to serialize
225
     *
226
     * @param $array
227
     * @return bool
228
     */
229
    public static function validArray($array)
230
    {
231
        $array = (array)$array;
232
233
        /*
234
        if (count(array_filter(array_keys($array), 'is_string')) > 0) {
235
            return true;
236
        }
237
        */
238
239
        if (self::isArrayObject($array)) {
240
            return true;
241
        }
242
243
        return false;
244
    }
245
246
    /**
247
     * @param array $array
248
     * @return string
249
     */
250
    public function serialize($array = [])
251
    {
252
        if (empty($array)) {
253
            return $this->getBinnVal();
254
        }
255
256
        $this->binnFree();
257
258
        if (! $this->isArrayAssoc($array)) {
259
            throw new InvalidArrayException('Array should be associative');
260
        }
261
262
        foreach ($array as $key => $item) {
263
            $type = $this->detectType($item);
264
            $storageType = $this->storageType($type);
265
266
            if ($storageType === self::BINN_STORAGE_CONTAINER) {
267
                foreach ($this->containersClasses as $contanerType => $containersClass)
268
                {
269
                    if ($containersClass::validArray($item)) {
270
                        $container = new $containersClass();
271
                        $container->serialize($item);
272
                        $item = $container;
273
                        break;
274
                    }
275
                }
276
            }
277
278
            $this->_addVal($key, $type, $item);
279
        }
280
281
        return $this->getBinnVal();
282
    }
283
284
    /**
285
     * @param string $binnString
286
     * @return array
287
     */
288
    public function unserialize($binnString = '')
289
    {
290
        if (empty($binnString)) {
291
            return $this->getBinnArr();
292
        }
293
294
        $this->binnFree();
295
296
        $this->binnOpen($binnString);
297
        return $this->getBinnArr();
298
    }
299
}