IdentityWrapper   B
last analyzed

Complexity

Total Complexity 51

Size/Duplication

Total Lines 370
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 51
eloc 79
dl 0
loc 370
ccs 107
cts 107
cp 1
rs 7.92
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A map() 0 8 1
A forAll() 0 10 3
A getKeys() 0 5 1
A getKey() 0 3 1
A isEmpty() 0 3 1
A current() 0 3 1
A toArray() 0 9 2
A offsetGet() 0 3 1
A setIdentityExtractor() 0 5 1
A containsKey() 0 3 1
A removeElement() 0 3 1
A first() 0 3 1
A clear() 0 3 1
A partition() 0 12 2
A offsetSet() 0 7 2
A key() 0 6 2
A set() 0 6 2
A contains() 0 3 1
A get() 0 5 2
A last() 0 3 1
A getValues() 0 3 1
A remove() 0 6 3
A next() 0 3 1
A getIterator() 0 3 1
A getElement() 0 5 1
A offsetExists() 0 3 1
A add() 0 3 1
A filter() 0 3 1
A indexOf() 0 3 2
A __construct() 0 3 1
A count() 0 3 1
A exists() 0 10 3
A offsetUnset() 0 3 1
A slice() 0 9 2
A getIdentityExtractor() 0 14 3

How to fix   Complexity   

Complex Class

Complex classes like IdentityWrapper 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 IdentityWrapper, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * YAWIK
4
 *
5
 * @filesource
6
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
7
 * @license   MIT
8
 */
9
10
namespace Core\Collection;
11
12
use Doctrine\Common\Collections\Collection;
13
use Closure;
14
15
class IdentityWrapper implements Collection
16
{
17
    
18
    /**
19
     * @var Collection
20
     */
21
    protected $collection;
22
    
23
    /**
24
     * @var callable
25
     */
26
    protected $identityExtractor;
27
    
28
    /**
29
     * @param Collection $collection
30
     */
31 39
    public function __construct(Collection $collection)
32
    {
33 39
        $this->collection = $collection;
34
    }
35
    
36
    /**
37
     * {@inheritDoc}
38
     */
39 11
    public function count()
40
    {
41 11
        return $this->collection->count();
42
    }
43
    
44
    /**
45
     * {@inheritDoc}
46
     */
47 2
    public function add($element)
48
    {
49 2
        return $this->collection->add($element);
50
    }
51
    
52
    /**
53
     * {@inheritDoc}
54
     */
55 4
    public function clear()
56
    {
57 4
        $this->collection->clear();
58
    }
59
    
60
    /**
61
     * {@inheritDoc}
62
     */
63 11
    public function contains($element)
64
    {
65 11
        return $this->collection->contains($element);
66
    }
67
    
68
    /**
69
     * {@inheritDoc}
70
     */
71 1
    public function isEmpty()
72
    {
73 1
        return $this->collection->isEmpty();
74
    }
75
    
76
    /**
77
     * {@inheritDoc}
78
     */
79 2
    public function remove($key)
80
    {
81 2
        $element = $this->getElement($key);
82
        
83 2
        if ($element !== false && $this->collection->removeElement($element)) {
84 2
            return $element;
85
        }
86
    }
87
    
88
    /**
89
     * {@inheritDoc}
90
     */
91 1
    public function removeElement($element)
92
    {
93 1
        return $this->collection->removeElement($element);
94
    }
95
    
96
    /**
97
     * {@inheritDoc}
98
     */
99 1
    public function containsKey($key)
100
    {
101 1
        return $this->getElement($key) !== false;
102
    }
103
    
104
    /**
105
     * {@inheritDoc}
106
     */
107 2
    public function get($key)
108
    {
109 2
        $element = $this->getElement($key);
110
        
111 2
        return $element !== false ? $element : null;
112
    }
113
    
114
    /**
115
     * {@inheritDoc}
116
     */
117 1
    public function getKeys()
118
    {
119
        return $this->collection->map(function ($element) {
120 1
            return $this->getKey($element);
121 1
        })->toArray();
122
    }
123
    
124
    /**
125
     * {@inheritDoc}
126
     */
127 1
    public function getValues()
128
    {
129 1
        return $this->collection->getValues();
130
    }
131
    
132
    /**
133
     * {@inheritDoc}
134
     */
135 4
    public function set($key, $value)
136
    {
137 4
        if ($this->getElement($key) !== false) {
138 2
            $this->collection->set($this->collection->indexOf($value), $value);
139
        } else {
140 2
            $this->collection->add($value);
141
        }
142
    }
143
    
144
    /**
145
     * {@inheritDoc}
146
     */
147 6
    public function toArray()
148
    {
149 6
        $array = [];
150
        
151 6
        foreach ($this->collection as $element) {
152 6
            $array[$this->getKey($element)] = $element;
153
        }
154
        
155 6
        return $array;
156
    }
157
    
158
    /**
159
     * {@inheritDoc}
160
     */
161 1
    public function first()
162
    {
163 1
        return $this->collection->first();
164
    }
165
    
166
    /**
167
     * {@inheritDoc}
168
     */
169 2
    public function last()
170
    {
171 2
        return $this->collection->last();
172
    }
173
    
174
    /**
175
     * {@inheritDoc}
176
     */
177 1
    public function key()
178
    {
179 1
        $element = $this->collection->current();
180
        
181 1
        if ($element !== false) {
182 1
            return $this->getKey($element);
183
        }
184
    }
185
    
186
    /**
187
     * {@inheritDoc}
188
     */
189 1
    public function current()
190
    {
191 1
        return $this->collection->current();
192
    }
193
    
194
    /**
195
     * {@inheritDoc}
196
     */
197 1
    public function next()
198
    {
199 1
        return $this->collection->next();
200
    }
201
    
202
    /**
203
     * {@inheritDoc}
204
     */
205 2
    public function exists(Closure $p)
206
    {
207 2
        foreach ($this->collection as $element) {
208 2
            $key = $this->getKey($element);
209 2
            if ($p($key, $element)) {
210 2
                return true;
211
            }
212
        }
213
        
214 2
        return false;
215
    }
216
    
217
    /**
218
     * {@inheritDoc}
219
     */
220 1
    public function filter(Closure $p)
221
    {
222 1
        return new static($this->collection->filter($p));
223
    }
224
    
225
    /**
226
     * {@inheritDoc}
227
     */
228 2
    public function forAll(Closure $p)
229
    {
230 2
        foreach ($this->collection as $element) {
231 2
            $key = $this->getKey($element);
232 2
            if (!$p($key, $element)) {
233 2
                return false;
234
            }
235
        }
236
        
237 2
        return true;
238
    }
239
    
240
    /**
241
     * {@inheritDoc}
242
     */
243 2
    public function map(Closure $func)
244
    {
245 2
        $mapped = $this->collection->map($func);
246
        
247
        // validate mapped elements
248 2
        array_map($this->getIdentityExtractor(), $mapped->toArray());
249
        
250 1
        return new static($mapped);
251
    }
252
    
253
    /**
254
     * {@inheritDoc}
255
     */
256 2
    public function partition(Closure $p)
257
    {
258 2
        $array = $this->toArray();
259 2
        $this->collection->clear();
260
        
261 2
        foreach ($array as $key => $element) {
262 2
            $this->collection->set($key, $element);
263
        }
264
        
265 2
        $partitions = $this->collection->partition($p);
266
        
267 2
        return [new static($partitions[0]), new static($partitions[1])];
268
    }
269
    
270
    /**
271
     * {@inheritDoc}
272
     */
273 2
    public function indexOf($element)
274
    {
275 2
        return $this->collection->indexOf($element) === false ? false : $this->getKey($element);
276
    }
277
    
278
    /**
279
     * {@inheritDoc}
280
     */
281 1
    public function slice($offset, $length = null)
282
    {
283 1
        $array = [];
284
        
285 1
        foreach ($this->collection->slice($offset, $length) as $element) {
286 1
            $array[$this->getKey($element)] = $element;
287
        }
288
        
289 1
        return $array;
290
    }
291
    
292
    /**
293
     * {@inheritDoc}
294
     */
295 1
    public function getIterator()
296
    {
297 1
        return new \ArrayIterator($this->toArray());
298
    }
299
    
300
    /**
301
     * {@inheritDoc}
302
     */
303 1
    public function offsetExists($offset)
304
    {
305 1
        return (bool)$this->getElement($offset);
306
    }
307
    
308
    /**
309
     * {@inheritDoc}
310
     */
311 1
    public function offsetGet($offset)
312
    {
313 1
        return $this->get($offset);
314
    }
315
    
316
    /**
317
     * {@inheritDoc}
318
     */
319 3
    public function offsetSet($offset, $value)
320
    {
321 3
        if (!isset($offset)) {
322 1
            return $this->add($value);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->add($value) returns the type true which is incompatible with the return type mandated by ArrayAccess::offsetSet() of void.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
323
        }
324
        
325 2
        $this->set($offset, $value);
326
    }
327
    
328
    /**
329
     * {@inheritDoc}
330
     */
331 1
    public function offsetUnset($offset)
332
    {
333 1
        return $this->remove($offset);
334
    }
335
    
336
    /**
337
     * @param callable $identityExtractor
338
     * @return IdentityWrapper
339
     */
340 1
    public function setIdentityExtractor(callable $identityExtractor)
341
    {
342 1
        $this->identityExtractor = $identityExtractor;
343
        
344 1
        return $this;
345
    }
346
347
    /**
348
     *
349
     * @return \Core\Collection\callable
0 ignored issues
show
Bug introduced by
The type Core\Collection\callable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
350
     */
351 26
    protected function getIdentityExtractor()
352
    {
353 26
        if (!isset($this->identityExtractor)) {
354
            // default identity extractor
355
            $this->identityExtractor = function ($element) {
356 25
                if (!is_callable([$element, 'getId'])) {
357 1
                    throw new \LogicException('$element must have getId() method');
358
                }
359
                
360 24
                return $element->getId();
361
            };
362
        }
363
        
364 26
        return $this->identityExtractor;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->identityExtractor returns the type callable which is incompatible with the documented return type Core\Collection\callable.
Loading history...
365
    }
366
367
    /**
368
     * @param mixed $element
369
     * @return mixed
370
     */
371 25
    protected function getKey($element)
372
    {
373 25
        return call_user_func($this->getIdentityExtractor(), $element);
374
    }
375
376
    /**
377
     * @param mixed $element
378
     * @return mixed
379
     */
380 10
    protected function getElement($key)
381
    {
382
        return $this->collection->filter(function ($element) use ($key) {
383 10
            return $this->getKey($element) == $key;
384 10
        })->first();
385
    }
386
}
387