TransferCollection   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 276
Duplicated Lines 0 %

Test Coverage

Coverage 88.79%

Importance

Changes 0
Metric Value
wmc 52
dl 0
loc 276
ccs 103
cts 116
cp 0.8879
rs 7.9487
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A exists() 0 9 3
A key() 0 3 1
A first() 0 5 1
A containsKey() 0 3 1
A contains() 0 3 1
B createTransfer() 0 15 6
A next() 0 3 1
A setObjectStorage() 0 5 1
A getKeys() 0 3 1
A setTransferManager() 0 5 1
A remove() 0 3 1
A last() 0 5 1
A current() 0 3 1
A getValues() 0 3 1
A add() 0 5 1
A get() 0 3 1
A clear() 0 3 1
A set() 0 3 1
A setProxyFactory() 0 5 1
A toArray() 0 3 1
A removeElement() 0 8 2
A isEmpty() 0 3 1
A __construct() 0 12 3
A count() 0 3 1
A offsetGet() 0 3 1
A forAll() 0 9 3
A indexOf() 0 3 1
A offsetUnset() 0 3 1
A filter() 0 11 3
A map() 0 9 2
A getIterator() 0 3 1
A offsetExists() 0 3 1
A partition() 0 13 3
A offsetSet() 0 3 1
A slice() 0 3 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace Vox\Webservice;
4
5
use Closure;
6
use Doctrine\Common\Collections\ArrayCollection;
7
use Doctrine\Common\Collections\Collection;
8
use Psr\Http\Message\ResponseInterface;
9
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
10
use Vox\Webservice\Proxy\ProxyFactoryInterface;
11
12
/**
13
 * Transfer collection is used as a way to keep the objects inside the unity of work and proxyed
14
 * 
15
 * @author Jhonatan Teixeira <[email protected]>
16
 */
17
class TransferCollection implements Collection
18
{
19
    private $transferName;
20
    
21
    /**
22
     * @var DenormalizerInterface
23
     */
24
    private $denormalizer;
25
    
26
    /**
27
     * @var ObjectStorageInterface
28
     */
29
    private $objectStorage;
30
    
31
    /**
32
     * @var ProxyFactoryInterface
33
     */
34
    private $proxyFactory;
35
    
36
    /**
37
     * @var TransferManagerInterface
38
     */
39
    private $transferManager;
40
41
    private $items = [];
42
43
    private $iterator;
44
    
45 9
    public function __construct(string $transferName, DenormalizerInterface $denormalizer, ResponseInterface $response)
46
    {
47 9
        $this->items = json_decode($response->getBody()->getContents() ?: '[]', true);
48
        
49 2
        $this->iterator = function () {
50 2
            foreach ($this->items as $key => $item) {
51 2
                yield $key => $this->createTransfer($item);
52
            }
53 2
        };
54
55 9
        $this->transferName = $transferName;
56 9
        $this->denormalizer = $denormalizer;
57 9
    }
58
59 7
    private function createTransfer($data)
60
    {
61 7
        if (!is_object($data)) {
62 6
            $data = $this->denormalizer->denormalize($data, $this->transferName);
63
64 6
            if ($this->proxyFactory && $this->transferManager) {
65 5
                $data = $this->proxyFactory->createProxy($data, $this->transferManager);
66
            }
67
        }
68
69 7
        if (isset($this->objectStorage) && !$this->objectStorage->contains($data)) {
70 5
            $this->objectStorage->attach($data);
71
        }
72
73 7
        return $data;
74
    }
75
76 6
    public function setObjectStorage(ObjectStorageInterface $objectStorage)
77
    {
78 6
        $this->objectStorage = $objectStorage;
79
        
80 6
        return $this;
81
    }
82
    
83 7
    public function setProxyFactory(ProxyFactoryInterface $proxyFactory)
84
    {
85 7
        $this->proxyFactory = $proxyFactory;
86
        
87 7
        return $this;
88
    }
89
90 7
    public function setTransferManager(TransferManagerInterface $transferManager)
91
    {
92 7
        $this->transferManager = $transferManager;
93
        
94 7
        return $this;
95
    }
96
97 2
    public function add($element)
98
    {
99 2
        $this->items[] = $element;
100
101 2
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Vox\Webservice\TransferCollection which is incompatible with the return type mandated by Doctrine\Common\Collections\Collection::add() of boolean.

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...
102
    }
103
104
    public function clear()
105
    {
106
        $this->items = [];
107
    }
108
109 1
    public function contains($element): bool
110
    {
111 1
        return in_array($element, $this->toArray(), true);
112
    }
113
114 1
    public function isEmpty(): bool
115
    {
116 1
        return empty($this->items);
117
    }
118
119 1
    public function remove($key)
120
    {
121 1
        unset($this->items[$key]);
122 1
    }
123
124 1
    public function removeElement($element)
125
    {
126 1
        $key = array_search($element, $this->toArray(), true);
127
128 1
        $this->remove($key);
129
        
130 1
        if ($this->transferManager) {
131 1
            $this->transferManager->remove($element);
132
        }
133 1
    }
134
135
    public function containsKey($key)
136
    {
137
        return array_key_exists($key, $this->items);
138
    }
139
140 6
    public function get($key)
141
    {
142 6
        return $this->createTransfer($this->items[$key]);
143
    }
144
145 4
    public function getKeys()
146
    {
147 4
        return array_keys($this->items);
148
    }
149
150
    public function getValues()
151
    {
152
        return array_values($this->toArray());
153
    }
154
155 1
    public function set($key, $value)
156
    {
157 1
        $this->items[$key] = $value;
158 1
    }
159
160 1
    public function toArray()
161
    {
162 1
        return iterator_to_array($this->getIterator());
163
    }
164
165 4
    public function first()
166
    {
167 4
        $keys = $this->getKeys();
168
169 4
        return $this->get(reset($keys));
170
    }
171
172 1
    public function last()
173
    {
174 1
        $keys = $this->getKeys();
175
176 1
        return $this->get(end($keys));
177
    }
178
179 1
    public function key()
180
    {
181 1
        return key($this->items);
182
    }
183
184 1
    public function current()
185
    {
186 1
        return $this->createTransfer(current($this->items));
187
    }
188
189
    public function next()
190
    {
191
        return $this->createTransfer(next($this->items));
192
    }
193
194 1
    public function exists(Closure $p): bool
195
    {
196 1
        foreach ($this->getIterator() as $key => $item) {
197 1
            if ($p($key, $item)) {
198 1
                return true;
199
            }
200
        }
201
202
        return false;
203
    }
204
205 1
    public function filter(Closure $p)
206
    {
207 1
        $filtered = new ArrayCollection();
208
        
209 1
        foreach ($this->getIterator() as $item) {
210 1
            if ($p($item)) {
211 1
                $filtered->add($item);
212
            }
213
        }
214
        
215 1
        return $filtered;
216
    }
217
218 1
    public function forAll(Closure $p)
219
    {
220 1
        foreach ($this->getIterator() as $key => $item) {
221 1
            if (!$p($key, $item)) {
222 1
                return false;
223
            }
224
        }
225
226
        return true;
227
    }
228
229 1
    public function map(Closure $func)
230
    {
231 1
        $data = new ArrayCollection();
232
        
233 1
        foreach ($this->getIterator() as $item) {
234 1
            $data->add($func($item));
235
        }
236
        
237 1
        return $data;
238
    }
239
240 1
    public function partition(Closure $p)
241
    {
242 1
        $matches = $noMatches = array();
243
244 1
        foreach ($this->getIterator() as $key => $element) {
245 1
            if ($p($key, $element)) {
246 1
                $matches[$key] = $element;
247
            } else {
248 1
                $noMatches[$key] = $element;
249
            }
250
        }
251
252 1
        return [new ArrayCollection($matches), new ArrayCollection($noMatches)];
253
    }
254
255
    public function indexOf($element)
256
    {
257
        return array_search($element, $this->toArray(), true);
258
    }
259
260 1
    public function slice($offset, $length = null)
261
    {
262 1
        return new ArrayCollection(array_slice(iterator_to_array($this->getIterator()), $offset, $length, true));
0 ignored issues
show
Bug Best Practice introduced by
The expression return new Doctrine\Comm...offset, $length, true)) returns the type Doctrine\Common\Collections\ArrayCollection which is incompatible with the return type mandated by Doctrine\Common\Collections\Collection::slice() of array.

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...
263
    }
264
265 2
    public function getIterator()
266
    {
267 2
        return call_user_func($this->iterator);
268
    }
269
270 1
    public function offsetExists($offset)
271
    {
272 1
        return isset($this->items[$offset]);
273
    }
274
275 3
    public function offsetGet($offset)
276
    {
277 3
        return $this->get($offset);
278
    }
279
280 1
    public function offsetSet($offset, $value)
281
    {
282 1
        $this->set($offset, $value);
283 1
    }
284
285 1
    public function offsetUnset($offset)
286
    {
287 1
        unset($this->items[$offset]);
288 1
    }
289
290 4
    public function count(): int
291
    {
292 4
        return count($this->items);
293
    }
294
}
295