Completed
Push — master ( a10c95...1963b4 )
by Shcherbak
105:51 queued 90:49
created

Collection::offsetGet()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
crap 6
1
<?php
2
3
  declare(strict_types = 1);
4
5
  namespace Funivan\PhpTokenizer;
6
7
  use Funivan\PhpTokenizer\Exception\Exception;
8
  use Funivan\PhpTokenizer\Query\Query;
9
10
  /**
11
   *
12
   */
13
  class Collection implements \Iterator, \ArrayAccess, \Countable {
14
15
    /**
16
     * @var int
17
     */
18
    protected $position = 0;
19
20
    /**
21
     * Array of objects
22
     *
23
     * @var array
24
     */
25
    protected $items = [];
26
27
28
    /**
29
     * @var string
30
     */
31
    protected $initialContentHash;
32
33
34
    /**
35
     * @param array $items
36
     */
37 450
    public function __construct(array $items = []) {
38 450
      if (!empty($items)) {
39 450
        $this->setItems($items);
40 450
      }
41
      $this->storeContentHash();
42
    }
43
44
45
    /**
46
     *
47
     */
48 144
    public function __clone() {
49 144
      $items = [];
50
      foreach ($this->items as $item) {
51
        $items[] = $item;
52
      }
53
      $this->setItems($items);
54
    }
55
56
57
    /**
58
     * Extract each value from token
59 435
     *
60 435
     * @return string
61 435
     */
62
    public function __toString() {
63
      return $this->assemble();
64
    }
65
66
67
    /**
68 9
     * @codeCoverageIgnore
69 9
     * @deprecated
70
     * @see createFromString
71
     * @param string $string
72
     * @return Collection
73
     */
74
    public static function initFromString($string) {
75
      trigger_error(__CLASS__ . '::' . __METHOD__ . ' deprecated and will be removed in 0.1.3 Use ' . __CLASS__ . '::createFromString', E_USER_DEPRECATED);
76 450
      return Collection::createFromString($string);
77 450
    }
78
79
80
    /**
81
     *
82
     * @param string $string
83
     * @return Collection
84
     * @throws Exception
85
     */
86 441
    public static function createFromString($string) : Collection {
87 441
      $tokens = Helper::getTokensFromString($string);
88
      return new Collection($tokens);
89
    }
90
91
92
    /**
93
     * Return number of items in this collection
94 450
     *
95 450
     * @return int
96
     */
97 450
    public function count() {
98 435
      return count($this->items);
99 66
    }
100
101 435
102 300
    /**
103
     * Add one item to begin of collection
104 450
     * This item is accessible via `$collection->getFirst();`
105
     *
106
     * @param $item
107
     * @return $this
108
     */
109
    public function prepend(Token $item) : self {
110
      array_unshift($this->items, $item);
111
      return $this;
112
    }
113
114 9
115 9
    /**
116 9
     * Add one item to the end of collection
117
     * This item is accessible via `$collection->getLast();`
118 9
     *
119 9
     * @param $item
120
     * @return $this
121 9
     */
122 9
    public function append(Token $item) : self {
123
      $this->items[] = $item;
124
      return $this;
125
    }
126
127
128
    /**
129
     * @param int $index
130 3
     * @param array $items
131 3
     * @return $this
132 3
     * @throws \InvalidArgumentException
133 3
     */
134 2
    public function addAfter($index, $items) : self {
135 3
      if (!is_array($items)) {
136
        throw new \InvalidArgumentException('You can add after only array of items');
137
      }
138
139
      foreach ($items as $item) {
140
        if (!($item instanceof Token)) {
141
          throw new \InvalidArgumentException('Expect array of tokens. Token[]');
142
        }
143 6
      }
144 6
145 6
      if (!is_int($index)) {
146 6
        throw new \InvalidArgumentException('Invalid type of index. Must be integer');
147 4
      }
148 6
149
      $offset = $index + 1;
150
      $firstPart = array_slice($this->items, 0, $offset);
151
      $secondPart = array_slice($this->items, $offset);
152
      $this->items = array_merge($firstPart, $items, $secondPart);
153
      return $this;
154
    }
155
156
157
    /**
158
     * Truncate current list of items and add new
159
     *
160
     * @param array $items
161
     * @return $this
162
     */
163
    public function setItems(array $items) : self {
164
      foreach ($items as $item) {
165
        if (!($item instanceof Token)) {
166
          throw new \InvalidArgumentException('Expect array of tokens. Token[]');
167
        }
168
      }
169
170 9
      $this->items = $items;
171 9
      $this->rewind();
172 9
      return $this;
173 9
    }
174
175 3
176 6
    /**
177
     * Remove part of items from collection
178 9
     * Works as array_slice
179
     *
180
     *
181
     * @param int $offset
182
     * @param int|null $length
183
     * @return $this
184
     */
185
    public function slice(int $offset, int $length = null) : self {
186
      $this->items = array_slice($this->items, $offset, $length);
187
      return $this;
188 30
    }
189 30
190 30
191 20
    /**
192 30
     * Take part of items and return new collection
193
     * Works as array_slice
194
     * At this point items in 2 collection is same
195
     *
196
     * @param int $offset
197
     * @param null $length
198
     * @return Collection
199
     */
200
    public function extractItems(int $offset, $length = null) : Collection {
201 177
      $items = array_slice($this->items, $offset, $length);
202
      return new Collection($items);
203 177
    }
204 177
205 177
206
    /**
207 177
     * Rewind current collection
208 177
     */
209 177
    public function rewind() {
210 118
      $this->position = 0;
211 118
      $this->items = array_values($this->items);
212
    }
213
214 177
215
    /**
216
     * Return last item from collection
217
     *
218
     * @return Token|false
219
     */
220
    public function getLast() {
221
      return end($this->items);
222 15
    }
223 15
224 15
225
    /**
226
     * Return first item from collection
227
     * @return Token|false
228
     */
229
    public function getFirst() {
230
      return reset($this->items);
231 450
    }
232 450
233 450
234
    /**
235
     * Return next item from current
236
     * Also can return item with position from current + $step
237
     *
238
     * @param int $step
239
     * @return Token
240
     */
241
    public function getNext(int $step = 1) : Token {
242
      $position = ($this->position + $step);
243
      return $this->items[$position] ?? new Token();
244
    }
245
246
247
    /**
248
     * Return previous item
249
     * Also can return previous from current position + $step
250
     *
251
     * @param int $step
252
     * @return Token
253
     */
254
    public function getPrevious(int $step = 1) : Token {
255
      $position = ($this->position - $step);
256
      return ($this->items[$position]) ?? new Token();
257
    }
258
259
260
    /**
261
     * Return current item in collection
262
     *
263
     * @return Token|null
264
     */
265
    public function current() {
266
      return $this->items[$this->position];
267
    }
268
269
270
    /**
271
     * Return current position
272
     *
273
     * @return int
274
     */
275
    public function key() {
276
      return $this->position;
277
    }
278
279
280
    /**
281
     * Switch to next position
282
     */
283
    public function next() {
284
      ++$this->position;
285
    }
286
287
288
    /**
289
     * Check if item exist in current position
290
     *
291
     * @return bool
292
     */
293
    public function valid() : bool {
294
      return isset($this->items[$this->position]);
295
    }
296
297
298
    /**
299
     * Add item to the end or modify item with given key
300
     *
301
     * @param int|null $offset
302
     * @param Token $item
303
     * @return $this
304
     */
305
    public function offsetSet($offset, $item) {
306
      if (!($item instanceof Token)) {
307
        throw new \InvalidArgumentException('Expect Token object');
308
      }
309
310
      if (is_null($offset)) {
311
        $this->append($item);
312
        return $this;
313
      }
314
315
      if (!is_int($offset)) {
316
        throw new \InvalidArgumentException('Invalid type of index. Must be integer');
317
      }
318
      $this->items[$offset] = $item;
319
320
      return $this;
321
    }
322
323
324
    /**
325
     * Check if item with given offset exists
326
     *
327
     * @param int $offset
328
     * @return bool
329
     */
330
    public function offsetExists($offset) {
331
      return isset($this->items[$offset]);
332
    }
333
334
335
    /**
336
     * Remove item from collection
337
     *
338
     * @param int $offset
339
     */
340
    public function offsetUnset($offset) {
341
      unset($this->items[$offset]);
342
    }
343
344
345
    /**
346
     * Get item from collection
347
     *
348
     * @param int $offset
349
     * @return Token|null
350
     */
351
    public function offsetGet($offset) {
352
      return isset($this->items[$offset]) ? $this->items[$offset] : null;
353
    }
354
355
356
    /**
357
     * Return array of items connected to this collection
358
     *
359
     * Rewrite this method in you class
360
     *
361
     * <code>
362
     * foreach($collection->getTokens() as $item){
363
     *  echo get_class($item)."\n;
364
     * }
365
     * </code>
366
     * @return Token[]
367
     */
368
    public function getTokens() : array {
369
      return $this->items;
370
    }
371
372
373
    public function getItems() {
374
      trigger_error('Deprecated. See getTokens', E_USER_DEPRECATED);
375
      return $this->items;
376
    }
377
378
379
    /**
380
     * Iterate over objects in collection
381
     *
382
     * <code>
383
     * $collection->each(function($item, $index, $collection){
384
     *    if ( $index > 0 ) {
385
     *      $item->remove();
386
     *    }
387
     * })
388
     * </code>
389
     *
390
     * @param callable $callback
391
     * @return $this
392
     * @throws \InvalidArgumentException
393
     */
394
    public function each(callable $callback) : self {
395
396
      if (!is_callable($callback)) {
397
        throw new \InvalidArgumentException('Invalid callback function');
398
      }
399
400
      foreach ($this->getTokens() as $index => $item) {
401
        call_user_func_array($callback, [$item, $index, $this]);
402
      }
403
404
      $this->rewind();
405
406
      return $this;
407
    }
408
409
410
    public function map(callable $callback) {
411
      trigger_error('Deprecated. See each', E_USER_DEPRECATED);
412
      return $this->each($callback);
413
    }
414
415
416
    /**
417
     * Remove all tokens in collection
418
     *
419
     * @return $this
420
     */
421
    public function remove() : self {
422
      foreach ($this as $token) {
423
        $token->remove();
424
      }
425
      return $this;
426
    }
427
428
429
    /**
430
     * @param Query $query
431
     * @return Collection
432
     */
433
    public function find(Query $query) {
434
      $finder = new TokenFinder($this);
435
      return $finder->find($query);
436
    }
437
438
439
    /**
440
     * Remove all invalid tokens in collection
441
     * Refresh index.
442
     *
443
     * @return Collection
444
     */
445
    public function refresh() : self {
446
      $string = $this->assemble();
447
      $this->cleanCollection();
448
449
      $tokens = Helper::getTokensFromString($string);
450
      $this->setItems($tokens);
451
452
      $this->rewind();
453
      return $this;
454
    }
455
456
457
    /**
458
     * @param Token $tokenStart
459
     * @param Token $tokenEnd
460
     * @return Collection
461
     */
462
    public function extractByTokens(Token $tokenStart, Token $tokenEnd) : Collection {
463
464
      $collection = new Collection();
465
      $startIndex = $tokenStart->getIndex();
466
      $endIndex = $tokenEnd->getIndex();
467
468
      foreach ($this->getTokens() as $token) {
469
        if ($token->getIndex() >= $startIndex and $token->getIndex() <= $endIndex) {
470
          $collection->append($token);
471
        }
472
      }
473
474
475
      return $collection;
476
    }
477
478
479
    /**
480
     * @return $this
481
     */
482
    public function storeContentHash() : self {
483
      $this->initialContentHash = $this->getContentHash();
484
      return $this;
485
    }
486
487
488
    /**
489
     * @return bool
490
     */
491
    public function isChanged() : bool {
492
      return ($this->getContentHash() !== $this->initialContentHash);
493
    }
494
495
496
    /**
497
     * @return string
498
     */
499
    private function getContentHash() : string {
500
      return md5($this->assemble());
501
    }
502
503
504
    /**
505
     * @return string
506
     */
507
    public function assemble() : string {
508
      $string = '';
509
      /** @var Token $token */
510
      foreach ($this as $token) {
511
        if (!$token->isValid()) {
512
          continue;
513
        }
514
        $string .= $token->getValue();
515
      }
516
517
      return $string;
518
    }
519
520
521
    /**
522
     * Remove invalid tokens from collection
523
     *
524
     * @return $this
525
     */
526
    protected function cleanCollection() : self {
527
      foreach ($this as $index => $token) {
528
        if ($token->isValid()) {
529
          continue;
530
        }
531
        unset($this->items[$index]);
532
      }
533
534
      return $this;
535
    }
536
537
  }