Collection   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 385
Duplicated Lines 0 %

Test Coverage

Coverage 98.37%

Importance

Changes 0
Metric Value
wmc 46
eloc 110
dl 0
loc 385
ccs 121
cts 123
cp 0.9837
rs 8.72
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A getRaw() 0 3 1
A addToIndex() 0 4 2
A getAt() 0 3 1
A __construct() 0 4 1
A getType() 0 3 1
A getById() 0 3 1
A setRawData() 0 6 1
A indexData() 0 5 2
A setAt() 0 27 6
A indexRow() 0 9 4
A toArray() 0 12 3
A initialize() 0 15 3
A setType() 0 5 1
A getBy() 0 8 2
A add() 0 5 1
A key() 0 13 2
A offsetUnset() 0 18 1
A rewind() 0 10 1
A toJson() 0 9 2
A next() 0 10 1
A offsetExists() 0 16 2
A count() 0 17 1
A valid() 0 14 2
A offsetGet() 0 13 1
A offsetSet() 0 16 1
A current() 0 13 2

How to fix   Complexity   

Complex Class

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

1
<?php
2
/**
3
 * @author @jenschude <[email protected]>
4
 */
5
6
namespace Commercetools\Core\Model\Common;
7
8
use Commercetools\Core\Error\Message;
9
10
/**
11
 * @package Commercetools\Core\Model\Common
12
 */
13
class Collection extends AbstractJsonDeserializeObject implements \Iterator, \JsonSerializable, \Countable, \ArrayAccess
14
{
15
    const ID = 'id';
16
    use ContextTrait;
17
    const COLLECTION_TYPE = Collection::TYPE_LIST;
18
    const TYPE_LIST = 'List';
19
    const TYPE_MAP = 'Map';
20
21
    /**
22
     * @var string
23
     */
24
    protected $type;
25
26
    protected $pos = 0;
27
28
    protected $keys = [];
29
30
    protected $index = [];
31
32
    /**
33
     * @param array $data
34
     * @param Context|callable $context
35
     */
36 729
    final public function __construct(array $data = [], $context = null)
37
    {
38 729
        parent::__construct($data, $context);
39 729
        $this->indexData();
40 729
    }
41
42
    /**
43
     * @return string
44
     */
45 629
    public function getType()
46
    {
47 629
        return $this->type;
48
    }
49
50
    /**
51
     * @param string $type
52
     * @internal
53
     * @return $this
54
     */
55 42
    public function setType($type)
56
    {
57 42
        $this->type = $type;
58
59 42
        return $this;
60
    }
61
62 729
    protected function indexData()
63
    {
64 729
        $this->keys = array_keys($this->rawData);
65 729
        foreach ($this->rawData as $offset => $row) {
66 492
            $this->indexRow($offset, $row);
67
        }
68 729
    }
69
70
    /**
71
     * @param array $rawData
72
     * @internal
73
     * @return $this
74
     */
75 30
    public function setRawData(array $rawData)
76
    {
77 30
        parent::setRawData($rawData);
78 30
        $this->indexData();
79
80 30
        return $this;
81
    }
82
83
    /**
84
     * @param string $indexName
85
     * @param int $offset
86
     * @param $key
87
     */
88 676
    protected function addToIndex($indexName, $offset, $key)
89
    {
90 676
        if (!is_null($key)) {
91 446
            $this->index[$indexName][$key] = $offset;
92
        }
93 676
    }
94
95 488
    protected function indexRow($offset, $row)
96
    {
97 488
        $id = null;
98 488
        if ($row instanceof Resource) {
99 1
            $id = $row->getId();
100 487
        } elseif (is_array($row)) {
101 296
            $id = isset($row[static::ID]) ? $row[static::ID] : null;
102
        }
103 488
        $this->addToIndex(static::ID, $offset, $id);
104 488
    }
105
106
    /**
107
     * @param $id
108
     * @return static
109
     */
110 134
    public function getById($id)
111
    {
112 134
        return $this->getBy(static::ID, $id);
113
    }
114
115
    /**
116
     * @param $indexName
117
     * @param $key
118
     * @return mixed|null
119
     */
120 220
    public function getBy($indexName, $key)
121
    {
122 220
        if (isset($this->index[$indexName][$key])) {
123 218
            $key = $this->index[$indexName][$key];
124
125 218
            return $this->getAt($key);
126
        }
127 7
        return null;
128
    }
129
130 474
    public function add($object)
131
    {
132 474
        $this->setAt(null, $object);
133
134 473
        return $this;
135
    }
136
137
    /**
138
     * @param $offset
139
     * @internal
140
     */
141 434
    protected function initialize($offset)
142
    {
143 434
        $type = $this->getType();
144 434
        if ($this->isDeserializableType($type)) {
145
            /**
146
             * @var JsonDeserializeInterface $type
147
             */
148 421
            $value = $type::fromArray($this->getRaw($offset), $this->getContextCallback());
149 421
            if ($value instanceof ObjectTreeInterface) {
150 419
                $value->parentSet($this);
151 419
                $value->rootSet($this->rootGet());
152
            }
153 421
            $this->typeData[$offset] = $value;
154
        }
155 434
        $this->initialized[$offset] = true;
156 434
    }
157
158
    /**
159
     * @return array
160
     */
161 18
    public function toArray()
162
    {
163 18
        $values = [];
164 18
        foreach ($this->typeData as $key => $value) {
165 18
            if ($value instanceof JsonDeserializeInterface) {
166 1
                $values[$key] = $value->toArray();
167
            } else {
168 17
                $values[$key] = $value;
169
            }
170
        }
171
172 18
        return $values;
173
    }
174
175
    /**
176
     * @param $offset
177
     * @return mixed
178
     */
179 459
    public function getAt($offset)
180
    {
181 459
        return $this->getTyped($offset);
182
    }
183
184
    /**
185
     * @param $offset
186
     * @param mixed $default
187
     * @return array
188
     */
189 421
    protected function getRaw($offset, $default = [])
190
    {
191 421
        return parent::getRaw($offset, $default);
192
    }
193
194
    /**
195
     * @param $offset
196
     * @param $object
197
     * @return $this
198
     */
199 479
    public function setAt($offset, $object)
200
    {
201 479
        $type = $this->getType();
202 479
        if (!$this->isValidType($type, $object)) {
203 1
            throw new \InvalidArgumentException(sprintf(Message::WRONG_TYPE, $offset, $type));
204
        }
205
206 478
        if ($object instanceof ContextAwareInterface) {
207 468
            $object->setContext($this->getContextCallback());
208
        }
209 478
        if ($object instanceof ObjectTreeInterface) {
210 468
            $object->parentSet($this);
211 468
            $object->rootSet($this->rootGet());
212
        }
213 478
        if (is_null($offset)) {
214 476
            $this->typeData[] = $object;
215 476
            $offset = count($this->typeData) - 1;
216
        } else {
217 3
            $this->typeData[$offset] = $object;
218
        }
219 478
        $this->initialized[$offset] = true;
220 478
        if (!in_array($offset, $this->keys)) {
221 478
            $this->keys[] = $offset;
222
        }
223 478
        $this->indexRow($offset, $object);
224
225 478
        return $this;
226
    }
227
228
    #[\ReturnTypeWillChange]
229
    /**
230
     * (PHP 5 &gt;= 5.1.0)<br/>
231
     * Count elements of an object
232
     * @link http://php.net/manual/en/countable.count.php
233
     * @return int The custom count as an integer.
234
     * </p>
235
     * <p>
236
     * The return value is cast to an integer.
237 141
     */
238
    public function count()
239 141
    {
240 141
        $rawKeys = array_keys($this->rawData);
241 141
        $typeKeys = array_keys($this->typeData);
242 141
        $keys = array_merge($rawKeys, $typeKeys);
243 141
        $uniqueKeys = array_unique($keys);
244
        return count($uniqueKeys);
245
    }
246
247
248
    #[\ReturnTypeWillChange]
249
    /**
250
     * (PHP 5 &gt;= 5.0.0)<br/>
251
     * Return the current element
252
     * @link http://php.net/manual/en/iterator.current.php
253 379
     * @return mixed Can return any type.
254
     */
255 379
    public function current()
256 379
    {
257
        if (isset($this->keys[$this->pos])) {
258
            return $this->getAt($this->keys[$this->pos]);
259
        }
260
        return null;
261
    }
262
263
    #[\ReturnTypeWillChange]
264
    /**
265
     * (PHP 5 &gt;= 5.0.0)<br/>
266
     * Move forward to next element
267 62
     * @link http://php.net/manual/en/iterator.next.php
268
     * @return void Any returned value is ignored.
269 62
     */
270 62
    public function next()
271
    {
272
        $this->pos++;
273
    }
274
275
    #[\ReturnTypeWillChange]
276
    /**
277
     * (PHP 5 &gt;= 5.0.0)<br/>
278 32
     * Return the key of the current element
279
     * @link http://php.net/manual/en/iterator.key.php
280 32
     * @return mixed scalar on success, or null on failure.
281 32
     */
282
    public function key()
283
    {
284
        if (isset($this->keys[$this->pos])) {
285
            return $this->keys[$this->pos];
286
        }
287
        return null;
288
    }
289
290
    #[\ReturnTypeWillChange]
291
    /**
292
     * (PHP 5 &gt;= 5.0.0)<br/>
293 65
     * Checks if current position is valid
294
     * @link http://php.net/manual/en/iterator.valid.php
295 65
     * @return boolean The return value will be casted to boolean and then evaluated.
296 63
     * Returns true on success or false on failure.
297
     */
298 39
    public function valid()
299
    {
300
        if (isset($this->keys[$this->pos])) {
301
            return $this->offsetExists($this->keys[$this->pos]);
302
        }
303
        return false;
304
    }
305
306
    #[\ReturnTypeWillChange]
307 67
    /**
308
     * (PHP 5 &gt;= 5.0.0)<br/>
309 67
     * Rewind the Iterator to the first element
310 67
     * @link http://php.net/manual/en/iterator.rewind.php
311
     * @return void Any returned value is ignored.
312
     */
313
    public function rewind()
314
    {
315
        $this->pos = 0;
316
    }
317
318
    #[\ReturnTypeWillChange]
319
    /**
320
     * (PHP 5 &gt;= 5.0.0)<br/>
321
     * Whether a offset exists
322
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
323
     * @param mixed $offset <p>
324 69
     * An offset to check for.
325
     * </p>
326 69
     * @return boolean true on success or false on failure.
327
     * </p>
328
     * <p>
329
     * The return value will be casted to boolean if non-boolean was returned.
330
     */
331
    public function offsetExists($offset)
332
    {
333
        return isset($this->rawData[$offset]) || isset($this->typeData[$offset]);
334
    }
335
336
    #[\ReturnTypeWillChange]
337
    /**
338 5
     * (PHP 5 &gt;= 5.0.0)<br/>
339
     * Offset to retrieve
340 5
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
341
     * @param mixed $offset <p>
342
     * The offset to retrieve.
343
     * </p>
344
     * @return mixed Can return all value types.
345
     */
346
    public function offsetGet($offset)
347
    {
348
        return $this->getAt($offset);
349
    }
350
351
    #[\ReturnTypeWillChange]
352
    /**
353
     * (PHP 5 &gt;= 5.0.0)<br/>
354
     * Offset to set
355 4
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
356
     * @param mixed $offset <p>
357 4
     * The offset to assign the value to.
358 4
     * </p>
359
     * @param mixed $value <p>
360
     * The value to set.
361
     * </p>
362
     * @return void
363
     */
364
    public function offsetSet($offset, $value)
365
    {
366
        $this->setAt($offset, $value);
367
    }
368
369 3
    #[\ReturnTypeWillChange]
370
    /**
371 3
     * (PHP 5 &gt;= 5.0.0)<br/>
372 3
     * Offset to unset
373 3
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
374 3
     * @param mixed $offset <p>
375 3
     * The offset to unset.
376 3
     * </p>
377 3
     * @return void
378
     */
379 455
    public function offsetUnset($offset)
380
    {
381 455
        unset($this->rawData[$offset]);
382
        unset($this->typeData[$offset]);
383 455
        $rawKeys = array_keys($this->rawData);
384 454
        $typeKeys = array_keys($this->typeData);
385
        $keys = array_merge($rawKeys, $typeKeys);
386
        $this->keys = array_unique($keys);
387 2
    }
388
389
    protected function toJson()
390
    {
391
        $data = parent::toJson();
392
393
        if (static::COLLECTION_TYPE == self::TYPE_LIST) {
394
            return array_values($data);
395
        }
396
397
        return $data;
398
    }
399
}
400