Completed
Push — master ( f97578...954bd6 )
by Shcherbak
91:32 queued 76:39
created

BaseCollection   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 441
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 0

Importance

Changes 0
Metric Value
wmc 52
lcom 2
cbo 0
dl 0
loc 441
rs 7.9487
c 0
b 0
f 0

32 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A __clone() 0 7 2
A count() 0 3 1
A prepend() 0 4 1
A append() 0 4 1
B addAfter() 0 21 5
A setItems() 0 15 4
A slice() 0 4 1
A extractItems() 0 8 1
A rewind() 0 4 1
A getLast() 0 3 1
A getFirst() 0 3 1
A getNext() 0 4 2
A getPrevious() 0 4 2
A current() 0 3 1
A key() 0 3 1
A next() 0 3 1
A valid() 0 3 1
A offsetSet() 0 17 4
A offsetExists() 0 3 1
A offsetUnset() 0 3 1
A offsetGet() 0 3 2
A getTokens() 0 3 1
A getItems() 0 4 1
A each() 0 14 3
A map() 0 4 1
A remove() 0 6 2
A validateIndex() 0 3 1
A storeContentHash() 0 4 1
A isChanged() 0 3 1
A getContentHash() 0 3 1
A assemble() 0 12 3

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
  namespace Funivan\PhpTokenizer\Collection;
4
5
  use Funivan\PhpTokenizer\Token;
6
7
  /**
8
   *
9
   * @package Fiv\Spl
10
   */
11
  class BaseCollection implements \Iterator, \ArrayAccess, \Countable {
12
13
    /**
14
     * @var int
15
     */
16
    protected $position = 0;
17
18
    /**
19
     * Array of objects
20
     *
21
     * @var array
22
     */
23
    protected $items = [];
24
25
26
    /**
27
     * @var string
28
     */
29
    protected $initialContentHash;
30
31
32
    /**
33
     * @param array $items
34
     */
35
    public function __construct(array $items = []) {
36
37
      if (!empty($items)) {
38
        $this->setItems($items);
39
      }
40
      $this->storeContentHash();
41
42
    }
43
44
45
    /**
46
     *
47
     */
48
    public function __clone() {
49
      $items = [];
50
      foreach ($this->items as $item) {
51
        $items[] = $item;
52
      }
53
      $this->setItems($items);
54
    }
55
56
57
    /**
58
     * Return number of items in this collection
59
     *
60
     * @return int
61
     */
62
    public function count() {
63
      return count($this->items);
64
    }
65
66
67
    /**
68
     * Add one item to begin of collection
69
     * This item is accessible via `$collection->getFirst();`
70
     *
71
     * @param $item
72
     * @return $this
73
     */
74
    public function prepend(Token $item) {
75
      array_unshift($this->items, $item);
76
      return $this;
77
    }
78
79
80
    /**
81
     * Add one item to the end of collection
82
     * This item is accessible via `$collection->getLast();`
83
     *
84
     * @param $item
85
     * @return $this
86
     */
87
    public function append(Token $item) {
88
      $this->items[] = $item;
89
      return $this;
90
    }
91
92
93
    /**
94
     * @param int $index
95
     * @param array $items
96
     * @return $this
97
     * @throws \InvalidArgumentException
98
     */
99
    public function addAfter($index, $items) {
100
      if (!is_array($items)) {
101
        throw new \InvalidArgumentException('You can add after only array of items');
102
      }
103
104
      foreach ($items as $item) {
105
        if (!($item instanceof Token)) {
106
          throw new \InvalidArgumentException('Expect array of tokens. Token[]');
107
        }
108
      }
109
110
      if (!is_int($index)) {
111
        throw new \InvalidArgumentException('Invalid type of index. Must be integer');
112
      }
113
114
      $offset = $index + 1;
115
      $firstPart = array_slice($this->items, 0, $offset);
116
      $secondPart = array_slice($this->items, $offset);
117
      $this->items = array_merge($firstPart, $items, $secondPart);
118
      return $this;
119
    }
120
121
122
    /**
123
     * Truncate current list of items and add new
124
     *
125
     * @param array $items
126
     * @return $this
127
     */
128
    public function setItems($items) {
129
      if (!is_array($items)) {
130
        throw new \InvalidArgumentException("You can set only array of items");
131
      }
132
133
      foreach ($items as $item) {
134
        if (!($item instanceof Token)) {
135
          throw new \InvalidArgumentException('Expect array of tokens. Token[]');
136
        }
137
      }
138
139
      $this->items = $items;
140
      $this->rewind();
141
      return $this;
142
    }
143
144
145
    /**
146
     * Remove part of items from collection
147
     * Works as array_slice
148
     *
149
     *
150
     * @param $offset
151
     * @param null $length
152
     * @return $this
153
     */
154
    public function slice($offset, $length = null) {
155
      $this->items = array_slice($this->items, $offset, $length);
156
      return $this;
157
    }
158
159
160
    /**
161
     * Take part of items and return new collection
162
     * Works as array_slice
163
     * At this point items in 2 collection is same
164
     *
165
     * @param int $offset
166
     * @param null $length
167
     * @return self
168
     */
169
    public function extractItems($offset, $length = null) {
170
      $items = array_slice($this->items, $offset, $length);
171
      $className = get_called_class();
172
      $collection = new $className();
173
      /** @var BaseCollection $collection */
174
      $collection->setItems($items);
175
      return $collection;
176
    }
177
178
179
    /**
180
     * Rewind current collection
181
     */
182
    public function rewind() {
183
      $this->position = 0;
184
      $this->items = array_values($this->items);
185
    }
186
187
188
    /**
189
     * Return last item from collection
190
     *
191
     * @return mixed
192
     */
193
    public function getLast() {
194
      return end($this->items);
195
    }
196
197
198
    /**
199
     * Return first item from collection
200
     * @return mixed
201
     */
202
    public function getFirst() {
203
      return reset($this->items);
204
    }
205
206
207
    /**
208
     * Return next item from current
209
     * Also can return item with position from current + $step
210
     *
211
     * @param int $step
212
     * @return mixed
213
     */
214
    public function getNext($step = 1) {
215
      $position = ($this->position + $step);
216
      return isset($this->items[$position]) ? $this->items[$position] : null;
217
    }
218
219
220
    /**
221
     * Return previous item
222
     * Also can return previous from current position + $step
223
     *
224
     * @param int $step
225
     * @return mixed
226
     */
227
    public function getPrevious($step = 1) {
228
      $position = ($this->position - $step);
229
      return isset($this->items[$position]) ? $this->items[$position] : null;
230
    }
231
232
233
    /**
234
     * Return current item in collection
235
     *
236
     * @return object
237
     */
238
    public function current() {
239
      return $this->items[$this->position];
240
    }
241
242
243
    /**
244
     * Return current position
245
     *
246
     * @return int
247
     */
248
    public function key() {
249
      return $this->position;
250
    }
251
252
253
    /**
254
     * Switch to next position
255
     */
256
    public function next() {
257
      ++$this->position;
258
    }
259
260
261
    /**
262
     * Check if item exist in current position
263
     *
264
     * @return bool
265
     */
266
    public function valid() {
267
      return isset($this->items[$this->position]);
268
    }
269
270
271
    /**
272
     * Add item to the end or modify item with given key
273
     *
274
     * @param int|null $offset
275
     * @param object $item
276
     * @return $this
277
     */
278
    public function offsetSet($offset, $item) {
279
      if (!($item instanceof Token)) {
280
        throw new \InvalidArgumentException('Expect Token object');
281
      }
282
283
      if (is_null($offset)) {
284
        $this->append($item);
285
        return $this;
286
      }
287
288
      if (!is_int($offset)) {
289
        throw new \InvalidArgumentException('Invalid type of index. Must be integer');
290
      }
291
      $this->items[$offset] = $item;
292
293
      return $this;
294
    }
295
296
297
    /**
298
     * Check if item with given offset exists
299
     *
300
     * @param mixed $offset
301
     * @return bool
302
     */
303
    public function offsetExists($offset) {
304
      return isset($this->items[$offset]);
305
    }
306
307
308
    /**
309
     * Remove item from collection
310
     *
311
     * @param int $offset
312
     */
313
    public function offsetUnset($offset) {
314
      unset($this->items[$offset]);
315
    }
316
317
318
    /**
319
     * Get item from collection
320
     *
321
     * @param int $offset
322
     * @return object
323
     */
324
    public function offsetGet($offset) {
325
      return isset($this->items[$offset]) ? $this->items[$offset] : null;
326
    }
327
328
329
    /**
330
     * Return array of items connected to this collection
331
     *
332
     * Rewrite this method in you class
333
     *
334
     * <code>
335
     * foreach($collection->getTokens() as $item){
336
     *  echo get_class($item)."\n;
337
     * }
338
     * </code>
339
     * @return Token[]
340
     */
341
    public function getTokens() : array {
342
      return $this->items;
343
    }
344
345
346
    public function getItems() {
347
      trigger_error('Deprecated. See getTokens', E_USER_DEPRECATED);
348
      return $this->items;
349
    }
350
351
352
    /**
353
     * Iterate over objects in collection
354
     *
355
     * <code>
356
     * $collection->each(function($item, $index, $collection){
357
     *    if ( $index > 0 ) {
358
     *      $item->remove();
359
     *    }
360
     * })
361
     * </code>
362
     *
363
     * @param callable $callback
364
     * @return $this
365
     * @throws \InvalidArgumentException
366
     */
367
    public function each(callable $callback) : self {
368
369
      if (!is_callable($callback)) {
370
        throw new \InvalidArgumentException('Invalid callback function');
371
      }
372
373
      foreach ($this->getTokens() as $index => $item) {
374
        call_user_func_array($callback, [$item, $index, $this]);
375
      }
376
377
      $this->rewind();
378
379
      return $this;
380
    }
381
382
383
    public function map(callable $callback) {
384
      trigger_error('Deprecated. See each', E_USER_DEPRECATED);
385
      return $this->each($callback);
386
    }
387
388
389
    /**
390
     * Remove all tokens in collection
391
     *
392
     * @return Collection
393
     */
394
    public function remove() {
395
      foreach ($this as $token) {
396
        $token->remove();
397
      }
398
      return $this;
399
    }
400
401
402
    /**
403
     * @param int $index
404
     */
405
    protected function validateIndex($index) {
0 ignored issues
show
Unused Code introduced by
The parameter $index is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
406
407
    }
408
409
410
    /**
411
     * @return $this
412
     */
413
    public function storeContentHash() {
414
      $this->initialContentHash = $this->getContentHash();
415
      return $this;
416
    }
417
418
419
    /**
420
     * @return bool
421
     */
422
    public function isChanged() {
423
      return ($this->getContentHash() !== $this->initialContentHash);
424
    }
425
426
427
    /**
428
     * @return string
429
     */
430
    private function getContentHash() {
431
      return md5($this->assemble());
432
    }
433
434
435
    /**
436
     * @return string
437
     */
438
    public function assemble() {
439
      $string = '';
440
      /** @var Token $token */
441
      foreach ($this as $token) {
442
        if (!$token->isValid()) {
443
          continue;
444
        }
445
        $string .= $token->getValue();
446
      }
447
448
      return $string;
449
    }
450
451
  }