Passed
Push — 1.x ( b7bde1...3cc805 )
by Ulises Jeremias
02:11
created

ConcatIterator::jsonSerialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 1
b 0
f 1
1
<?php namespace Mbh\Iterator;
2
3
/**
4
 * MBHFramework
5
 *
6
 * @link      https://github.com/MBHFramework/mbh-framework
7
 * @copyright Copyright (c) 2017 Ulises Jeremias Cornejo Fandos
8
 * @license   https://github.com/MBHFramework/mbh-framework/blob/master/LICENSE (MIT License)
9
 */
10
11
use Mbh\Collection\Interfaces\Sequenceable as SequenceableInterface;
12
use ArrayAccess;
13
use AppendIterator;
14
use JsonSerializable;
15
use Countable;
16
use Iterator;
17
use RuntimeException;
18
use InvalidArgumentException;
19
20
/**
21
 * Iterator to allow multiple iterators to be concatenated
22
 */
23
24
class ConcatIterator extends AppendIterator implements SequenceableInterface
25
{
26
    use \Mbh\Collection\Traits\Collection;
27
28
    const INVALID_INDEX = 'Index invalid or out of range';
29
30
    /** @var int $count Fast-lookup count for full set of iterators */
31
    public $count = 0;
32
33
    /**
34
     * Build an iterator over multiple iterators
35
     * Unlike a LimitIterator, the $end defines the last index, not the count
36
     *
37
     * @param Iterator $iterator,... Concat iterators in order
38
     */
39
    public function __construct()
40
    {
41
        parent::__construct();
42
        foreach (func_get_args() as $i => $iterator) {
43
            if (
44
                $iterator instanceof ArrayAccess &&
45
                $iterator instanceof Countable
46
            ) {
47
                // Unroll other ConcatIterators, so we avoid deep iterator stacks
48
                if ($iterator instanceof self) {
49
                    foreach ($iterator->getArrayIterator() as $innerIt) {
0 ignored issues
show
Bug introduced by
The method getArrayIterator() does not exist on Countable. It seems like you code against a sub-type of Countable such as Mbh\Iterator\ConcatIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

49
                    foreach ($iterator->/** @scrutinizer ignore-call */ getArrayIterator() as $innerIt) {
Loading history...
Bug introduced by
The method getArrayIterator() does not exist on ArrayAccess. It seems like you code against a sub-type of ArrayAccess such as Mbh\Iterator\ConcatIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

49
                    foreach ($iterator->/** @scrutinizer ignore-call */ getArrayIterator() as $innerIt) {
Loading history...
50
                        $this->append($innerIt);
51
                    }
52
                } else {
53
                    $this->append($iterator);
0 ignored issues
show
Bug introduced by
$iterator of type ArrayAccess&Countable is incompatible with the type Iterator expected by parameter $iterator of AppendIterator::append(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

53
                    $this->append(/** @scrutinizer ignore-type */ $iterator);
Loading history...
54
                }
55
56
                $this->count += count($iterator);
57
            } else {
58
                throw new InvalidArgumentException(
59
                    'Argument ' . $i .
60
                    ' passed to ' . __METHOD__ .
61
                    ' must be of type ArrayAccess, Countable, and Traversable. ' .
62
                    gettype($iterator) . ' given.'
63
                );
64
            }
65
        }
66
    }
67
68
    /**
69
     * Countable
70
     */
71
    public function count(): int
72
    {
73
        return $this->count;
74
    }
75
76
    /**
77
     * ArrayAccess
78
     */
79
    public function offsetExists($offset)
80
    {
81
        return $offset >= 0 && $offset < $this->count;
82
    }
83
84
    public function offsetGet($offset)
85
    {
86
        if ($this->offsetExists($offset)) {
87
            list($it, $idx) = $this->getIteratorByIndex($offset);
88
            return $it->offsetGet($idx);
89
        } else {
90
            throw new RuntimeException(self::INVALID_INDEX);
91
        }
92
    }
93
94
    public function offsetSet($offset, $value)
95
    {
96
        list($it, $idx) = $this->getIteratorByIndex($offset);
97
        $it->offsetSet($idx, $value);
98
    }
99
100
    public function offsetUnset($offset)
101
    {
102
        list($it, $idx) = $this->getIteratorByIndex($offset);
103
        $it->offsetUnset($idx);
104
    }
105
106
    public function toArray(): array
107
    {
108
        return iterator_to_array($this, false);
109
    }
110
111
    /**
112
     * Find which of the inner iterators an index corresponds to
113
     *
114
     * @param int $index
115
     * @return [ArrayAccess, int] The iterator and interior index
116
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment [ArrayAccess, int] at position 0 could not be parsed: Unknown type name '[' at position 0 in [ArrayAccess, int].
Loading history...
117
    protected function getIteratorByIndex($index = 0)
118
    {
119
        $runningCount = 0;
120
        foreach ($this->getArrayIterator() as $innerIt) {
121
            $count = count($innerIt);
122
            if ($index < $runningCount + $count) {
123
                return [$innerIt, $index - $runningCount];
124
            }
125
            $runningCount += $count;
126
        }
127
128
        return null;
129
    }
130
}
131