MixedStore   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Test Coverage

Coverage 94.57%

Importance

Changes 0
Metric Value
wmc 43
eloc 99
dl 0
loc 221
ccs 87
cts 92
cp 0.9457
rs 8.96
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
B offsetUnset() 0 24 10
B offsetExists() 0 32 11
B offsetSet() 0 22 9
C offsetGet() 0 33 12

How to fix   Complexity   

Complex Class

Complex classes like MixedStore 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.

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 MixedStore, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Utils;
6
7
use ArrayAccess;
8
use GraphQL\Type\Definition\EnumValueDefinition;
9
use InvalidArgumentException;
10
use SplObjectStorage;
11
use function array_key_exists;
12
use function array_search;
13
use function array_splice;
14
use function is_array;
15
use function is_float;
16
use function is_int;
17
use function is_object;
18
use function is_string;
19
20
/**
21
 * Similar to PHP array, but allows any type of data to act as key (including arrays, objects, scalars)
22
 *
23
 * Note: unfortunately when storing array as key - access and modification is O(N)
24
 * (yet this should rarely be the case and should be avoided when possible)
25
 */
26
class MixedStore implements ArrayAccess
27
{
28
    /** @var EnumValueDefinition[] */
29
    private $standardStore;
30
31
    /** @var mixed[] */
32
    private $floatStore;
33
34
    /** @var SplObjectStorage */
35
    private $objectStore;
36
37
    /** @var callable[] */
38
    private $arrayKeys;
39
40
    /** @var EnumValueDefinition[] */
41
    private $arrayValues;
42
43
    /** @var callable[] */
44
    private $lastArrayKey;
45
46
    /** @var mixed */
47
    private $lastArrayValue;
48
49
    /** @var mixed */
50
    private $nullValue;
51
52
    /** @var bool */
53
    private $nullValueIsSet;
54
55
    /** @var mixed */
56
    private $trueValue;
57
58
    /** @var bool */
59
    private $trueValueIsSet;
60
61
    /** @var mixed */
62
    private $falseValue;
63
64
    /** @var bool */
65
    private $falseValueIsSet;
66
67 22
    public function __construct()
68
    {
69 22
        $this->standardStore   = [];
70 22
        $this->floatStore      = [];
71 22
        $this->objectStore     = new SplObjectStorage();
72 22
        $this->arrayKeys       = [];
73 22
        $this->arrayValues     = [];
74 22
        $this->nullValueIsSet  = false;
75 22
        $this->trueValueIsSet  = false;
76 22
        $this->falseValueIsSet = false;
77 22
    }
78
79
    /**
80
     * Whether a offset exists
81
     *
82
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
83
     *
84
     * @param mixed $offset <p>
85
     * An offset to check for.
86
     * </p>
87
     *
88
     * @return bool true on success or false on failure.
89
     * </p>
90
     * <p>
91
     * The return value will be casted to boolean if non-boolean was returned.
92
     */
93 30
    public function offsetExists($offset)
94
    {
95 30
        if ($offset === false) {
96 1
            return $this->falseValueIsSet;
97
        }
98 30
        if ($offset === true) {
99 1
            return $this->trueValueIsSet;
100
        }
101 29
        if (is_int($offset) || is_string($offset)) {
102 23
            return array_key_exists($offset, $this->standardStore);
103
        }
104 8
        if (is_float($offset)) {
105 1
            return array_key_exists((string) $offset, $this->floatStore);
106
        }
107 7
        if (is_object($offset)) {
108 3
            return $this->objectStore->offsetExists($offset);
109
        }
110 5
        if (is_array($offset)) {
111 4
            foreach ($this->arrayKeys as $index => $entry) {
112 4
                if ($entry === $offset) {
113 4
                    $this->lastArrayKey   = $offset;
114 4
                    $this->lastArrayValue = $this->arrayValues[$index];
115
116 4
                    return true;
117
                }
118
            }
119
        }
120 2
        if ($offset === null) {
121 1
            return $this->nullValueIsSet;
122
        }
123
124 1
        return false;
125
    }
126
127
    /**
128
     * Offset to retrieve
129
     *
130
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
131
     *
132
     * @param mixed $offset <p>
133
     * The offset to retrieve.
134
     * </p>
135
     *
136
     * @return mixed Can return all value types.
137
     */
138 29
    public function offsetGet($offset)
139
    {
140 29
        if ($offset === true) {
141 1
            return $this->trueValue;
142
        }
143 29
        if ($offset === false) {
144 1
            return $this->falseValue;
145
        }
146 28
        if (is_int($offset) || is_string($offset)) {
147 22
            return $this->standardStore[$offset];
148
        }
149 8
        if (is_float($offset)) {
150 1
            return $this->floatStore[(string) $offset];
151
        }
152 7
        if (is_object($offset)) {
153 3
            return $this->objectStore->offsetGet($offset);
154
        }
155 5
        if (is_array($offset)) {
156
            // offsetGet is often called directly after offsetExists, so optimize to avoid second loop:
157 4
            if ($this->lastArrayKey === $offset) {
158 4
                return $this->lastArrayValue;
159
            }
160
            foreach ($this->arrayKeys as $index => $entry) {
161
                if ($entry === $offset) {
162
                    return $this->arrayValues[$index];
163
                }
164
            }
165
        }
166 1
        if ($offset === null) {
167 1
            return $this->nullValue;
168
        }
169
170
        return null;
171
    }
172
173
    /**
174
     * Offset to set
175
     *
176
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
177
     *
178
     * @param mixed $offset <p>
179
     * The offset to assign the value to.
180
     * </p>
181
     * @param mixed $value  <p>
182
     *  The value to set.
183
     *  </p>
184
     *
185
     * @return void
186
     */
187 22
    public function offsetSet($offset, $value)
188
    {
189 22
        if ($offset === false) {
190 1
            $this->falseValue      = $value;
191 1
            $this->falseValueIsSet = true;
192 22
        } elseif ($offset === true) {
193 1
            $this->trueValue      = $value;
194 1
            $this->trueValueIsSet = true;
195 21
        } elseif (is_int($offset) || is_string($offset)) {
196 15
            $this->standardStore[$offset] = $value;
197 10
        } elseif (is_float($offset)) {
198 1
            $this->floatStore[(string) $offset] = $value;
199 9
        } elseif (is_object($offset)) {
200 6
            $this->objectStore[$offset] = $value;
201 5
        } elseif (is_array($offset)) {
202 4
            $this->arrayKeys[]   = $offset;
203 4
            $this->arrayValues[] = $value;
204 1
        } elseif ($offset === null) {
205 1
            $this->nullValue      = $value;
206 1
            $this->nullValueIsSet = true;
207
        } else {
208
            throw new InvalidArgumentException('Unexpected offset type: ' . Utils::printSafe($offset));
209
        }
210 22
    }
211
212
    /**
213
     * Offset to unset
214
     *
215
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
216
     *
217
     * @param mixed $offset <p>
218
     * The offset to unset.
219
     * </p>
220
     *
221
     * @return void
222
     */
223 6
    public function offsetUnset($offset)
224
    {
225 6
        if ($offset === true) {
226 1
            $this->trueValue      = null;
227 1
            $this->trueValueIsSet = false;
228 6
        } elseif ($offset === false) {
229 1
            $this->falseValue      = null;
230 1
            $this->falseValueIsSet = false;
231 5
        } elseif (is_int($offset) || is_string($offset)) {
232 1
            unset($this->standardStore[$offset]);
233 4
        } elseif (is_float($offset)) {
234 1
            unset($this->floatStore[(string) $offset]);
235 3
        } elseif (is_object($offset)) {
236 1
            $this->objectStore->offsetUnset($offset);
237 2
        } elseif (is_array($offset)) {
238 1
            $index = array_search($offset, $this->arrayKeys, true);
239
240 1
            if ($index !== false) {
241 1
                array_splice($this->arrayKeys, $index, 1);
0 ignored issues
show
Bug introduced by
It seems like $index can also be of type string; however, parameter $offset of array_splice() 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

241
                array_splice($this->arrayKeys, /** @scrutinizer ignore-type */ $index, 1);
Loading history...
242 1
                array_splice($this->arrayValues, $index, 1);
243
            }
244 1
        } elseif ($offset === null) {
245 1
            $this->nullValue      = null;
246 1
            $this->nullValueIsSet = false;
247
        }
248 6
    }
249
}
250