Completed
Push — master ( 642bad...fb66f9 )
by Shcherbak
15:09
created

BaseCollection::extractItems()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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