Completed
Push — develop ( 69bf64...c61561 )
by
unknown
17:17 queued 09:01
created

IdentityWrapper   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 379
Duplicated Lines 5.8 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 2
Bugs 1 Features 1
Metric Value
wmc 51
c 2
b 1
f 1
lcom 1
cbo 1
dl 22
loc 379
rs 8.3206

35 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A count() 0 4 1
A add() 0 4 1
A clear() 0 4 1
A contains() 0 4 1
A isEmpty() 0 4 1
A remove() 0 8 3
A removeElement() 0 4 1
A containsKey() 0 4 1
A get() 0 6 2
A getKeys() 0 6 1
A getValues() 0 4 1
A set() 0 11 2
A toArray() 0 11 2
A first() 0 4 1
A last() 0 4 1
A key() 0 9 2
A current() 0 4 1
A next() 0 4 1
A exists() 11 11 3
A filter() 0 4 1
A forAll() 11 11 3
A map() 0 9 1
A partition() 0 14 2
A indexOf() 0 4 2
A slice() 0 11 2
A getIterator() 0 4 1
A offsetExists() 0 4 1
A offsetGet() 0 4 1
A offsetSet() 0 8 2
A offsetUnset() 0 4 1
A setIdentityExtractor() 0 6 1
A getIdentityExtractor() 0 15 3
A getKey() 0 4 1
A getElement() 0 6 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 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. 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 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
    public function __construct(Collection $collection)
32
    {
33
        $this->collection = $collection;
34
    }
35
    
36
    /**
37
     * {@inheritDoc}
38
     */
39
    public function count()
40
    {
41
        return $this->collection->count();
42
    }
43
    
44
    /**
45
     * {@inheritDoc}
46
     */
47
    public function add($element)
48
    {
49
        return $this->collection->add($element);
50
    }
51
    
52
    /**
53
     * {@inheritDoc}
54
     */
55
    public function clear()
56
    {
57
        $this->collection->clear();
58
    }
59
    
60
    /**
61
     * {@inheritDoc}
62
     */
63
    public function contains($element)
64
    {
65
        return $this->collection->contains($element);
66
    }
67
    
68
    /**
69
     * {@inheritDoc}
70
     */
71
    public function isEmpty()
72
    {
73
        return $this->collection->isEmpty();
74
    }
75
    
76
    /**
77
     * {@inheritDoc}
78
     */
79
    public function remove($key)
80
    {
81
        $element = $this->getElement($key);
82
        
83
        if ($element !== false && $this->collection->removeElement($element)) {
84
    		return $element;
85
        }
86
    }
87
    
88
    /**
89
     * {@inheritDoc}
90
     */
91
    public function removeElement($element)
92
    {
93
        return $this->collection->removeElement($element);
94
    }
95
    
96
    /**
97
     * {@inheritDoc}
98
     */
99
    public function containsKey($key)
100
    {
101
        return $this->getElement($key) !== false;
102
    }
103
    
104
    /**
105
     * {@inheritDoc}
106
     */
107
    public function get($key)
108
    {
109
        $element = $this->getElement($key);
110
        
111
		return $element !== false ? $element : null;
112
    }
113
    
114
    /**
115
     * {@inheritDoc}
116
     */
117
    public function getKeys()
118
    {
119
        return $this->collection->map(function ($element) {
120
            return $this->getKey($element);
121
        })->toArray();
122
    }
123
    
124
    /**
125
     * {@inheritDoc}
126
     */
127
    public function getValues()
128
    {
129
        return $this->collection->getValues();
130
    }
131
    
132
    /**
133
     * {@inheritDoc}
134
     */
135
    public function set($key, $value)
136
    {
137
        if ($this->getElement($key) !== false)
138
        {
139
            $this->collection->set($this->collection->indexOf($value), $value);
140
        }
141
        else
142
        {
143
            $this->collection->add($value);
144
        }
145
    }
146
    
147
    /**
148
     * {@inheritDoc}
149
     */
150
    public function toArray()
151
    {
152
        $array = [];
153
        
154
        foreach ($this->collection as $element)
155
        {
156
            $array[$this->getKey($element)] = $element;
157
        }
158
        
159
        return $array;
160
    }
161
    
162
    /**
163
     * {@inheritDoc}
164
     */
165
    public function first()
166
    {
167
        return $this->collection->first();
168
    }
169
    
170
    /**
171
     * {@inheritDoc}
172
     */
173
    public function last()
174
    {
175
        return $this->collection->last();
176
    }
177
    
178
    /**
179
     * {@inheritDoc}
180
     */
181
    public function key()
182
    {
183
        $element = $this->collection->current();
184
        
185
        if ($element !== false)
186
        {
187
    		return $this->getKey($element);
188
        }
189
    }
190
    
191
    /**
192
     * {@inheritDoc}
193
     */
194
    public function current()
195
    {
196
        return $this->collection->current();
197
    }
198
    
199
    /**
200
     * {@inheritDoc}
201
     */
202
    public function next()
203
    {
204
        return $this->collection->next();
205
    }
206
    
207
    /**
208
     * {@inheritDoc}
209
     */
210 View Code Duplication
    public function exists(Closure $p)
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...
211
    {
212
        foreach ($this->collection as $element) {
213
            $key = $this->getKey($element);
214
            if ($p($key, $element)) {
215
                return true;
216
            }
217
        }
218
        
219
        return false;
220
    }
221
    
222
    /**
223
     * {@inheritDoc}
224
     */
225
    public function filter(Closure $p)
226
    {
227
        return new static($this->collection->filter($p));
228
    }
229
    
230
    /**
231
     * {@inheritDoc}
232
     */
233 View Code Duplication
    public function forAll(Closure $p)
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...
234
    {
235
        foreach ($this->collection as $element) {
236
            $key = $this->getKey($element);
237
            if (!$p($key, $element)) {
238
                return false;
239
            }
240
        }
241
        
242
        return true;
243
    }
244
    
245
    /**
246
     * {@inheritDoc}
247
     */
248
    public function map(Closure $func)
249
    {
250
        $mapped = $this->collection->map($func);
251
        
252
        // validate mapped elements
253
        array_map($this->getIdentityExtractor(), $mapped->toArray());
254
        
255
		return new static($mapped);
256
    }
257
    
258
    /**
259
     * {@inheritDoc}
260
     */
261
    public function partition(Closure $p)
262
    {
263
        $array = $this->toArray();
264
        $this->collection->clear();
265
        
266
        foreach ($array as $key => $element)
267
        {
268
            $this->collection->set($key, $element);
269
        }
270
        
271
        $partitions = $this->collection->partition($p);
272
        
273
        return [new static($partitions[0]), new static($partitions[1])];
274
    }
275
    
276
    /**
277
     * {@inheritDoc}
278
     */
279
    public function indexOf($element)
280
    {
281
        return $this->collection->indexOf($element) === false ? false : $this->getKey($element);
282
    }
283
    
284
    /**
285
     * {@inheritDoc}
286
     */
287
    public function slice($offset, $length = null)
288
    {
289
        $array = [];
290
        
291
        foreach ($this->collection->slice($offset, $length) as $element)
292
        {
293
            $array[$this->getKey($element)] = $element;
294
        }
295
        
296
        return $array;
297
    }
298
    
299
    /**
300
     * {@inheritDoc}
301
     */
302
    public function getIterator()
303
    {
304
        return new \ArrayIterator($this->toArray());
305
    }
306
    
307
    /**
308
     * {@inheritDoc}
309
     */
310
    public function offsetExists($offset)
311
    {
312
        return (bool)$this->getElement($offset);
313
    }
314
    
315
    /**
316
     * {@inheritDoc}
317
     */
318
    public function offsetGet($offset)
319
    {
320
        return $this->get($offset);
321
    }
322
    
323
    /**
324
     * {@inheritDoc}
325
     */
326
    public function offsetSet($offset, $value)
327
    {
328
        if (!isset($offset)) {
329
            return $this->add($value);
330
        }
331
        
332
        $this->set($offset, $value);
333
    }
334
    
335
    /**
336
     * {@inheritDoc}
337
     */
338
    public function offsetUnset($offset)
339
    {
340
        return $this->remove($offset);
341
    }
342
    
343
    /**
344
	 * @param callable $identityExtractor
345
	 * @return IdentityWrapper
346
	 */
347
	public function setIdentityExtractor(callable $identityExtractor)
348
	{
349
		$this->identityExtractor = $identityExtractor;
350
		
351
		return $this;
352
	}
353
354
    /**
355
     *
356
     * @return \Core\Collection\callable
0 ignored issues
show
Documentation introduced by
Should the return type not be callable?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
357
     */
358
    protected function getIdentityExtractor()
359
    {
360
        if (!isset($this->identityExtractor)) {
361
            // default identity extractor
362
            $this->identityExtractor = function ($element) {
363
                if (!is_callable([$element, 'getId'])) {
364
                    throw new \LogicException('$element must have getId() method');
365
                }
366
                
367
                return $element->getId();
368
            };
369
        }
370
        
371
        return $this->identityExtractor;
372
    }
373
374
	/**
375
	 * @param mixed $element
376
	 * @return mixed
377
	 */
378
	protected function getKey($element)
379
	{
380
	    return call_user_func($this->getIdentityExtractor(), $element);
381
	}
382
383
	/**
384
	 * @param mixed $element
0 ignored issues
show
Bug introduced by
There is no parameter named $element. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
385
	 * @return mixed
386
	 */
387
	protected function getElement($key)
388
	{
389
	    return $this->collection->filter(function ($element) use ($key) {
390
	        return $this->getKey($element) == $key;
391
	    })->first();
392
	}
393
}
394