ConcatIterator::toArray()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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 ArrayAccess;
12
use AppendIterator;
13
use JsonSerializable;
14
use Countable;
15
use Iterator;
16
use RuntimeException;
17
use InvalidArgumentException;
18
19
/**
20
 * Iterator to allow multiple iterators to be concatenated
21
 */
22
23
class ConcatIterator extends AppendIterator implements ArrayAccess, Countable, JsonSerializable
24
{
25
    const INVALID_INDEX = 'Index invalid or out of range';
26
27
    /** @var int $count Fast-lookup count for full set of iterators */
28
    public $count = 0;
29
30
    /**
31
     * Build an iterator over multiple iterators
32
     * Unlike a LimitIterator, the $end defines the last index, not the count
33
     *
34
     * @param Iterator $iterator,... Concat iterators in order
35
     */
36
    public function __construct(...$args)
37
    {
38
        parent::__construct();
39
40
        foreach ($args as $i => $iterator) {
41
            if (!(
42
                $iterator instanceof ArrayAccess
43
                && $iterator instanceof Countable
44
            )) {
45
                throw new InvalidArgumentException(
46
                    'Argument ' . $i .
47
                    ' passed to ' . __METHOD__ .
48
                    ' must be of type ArrayAccess, Countable, and Traversable. ' .
49
                    gettype($iterator) . ' given.'
50
                );
51
            }
52
53
            // Unroll other ConcatIterators, so we avoid deep iterator stacks
54
            if ($iterator instanceof self) {
55
                foreach ($iterator as $innerIt) {
56
                    $this->append($innerIt);
57
                }
58
            } else {
59
                $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

59
                $this->append(/** @scrutinizer ignore-type */ $iterator);
Loading history...
60
            }
61
62
            $this->count += count($iterator);
63
        }
64
    }
65
66
    /**
67
     * Countable
68
     */
69
    public function count(): int
70
    {
71
        return $this->count;
72
    }
73
74
    /**
75
     * ArrayAccess
76
     */
77
    public function offsetExists($offset): bool
78
    {
79
        return $offset >= 0 && $offset < $this->count;
80
    }
81
82
    public function offsetGet($offset)
83
    {
84
        if (!$this->offsetExists($offset)) {
85
            throw new RuntimeException(self::INVALID_INDEX);
86
        }
87
88
        list($it, $idx) = $this->getIteratorByIndex($offset);
89
        return $it->offsetGet($idx);
90
    }
91
92
    public function offsetSet($offset, $value)
93
    {
94
        list($it, $idx) = $this->getIteratorByIndex($offset);
95
        $it->offsetSet($idx, $value);
96
    }
97
98
    public function offsetUnset($offset)
99
    {
100
        list($it, $idx) = $this->getIteratorByIndex($offset);
101
        $it->offsetUnset($idx);
102
    }
103
104
    /**
105
     * JsonSerializable
106
     */
107
    public function jsonSerialize(): array
108
    {
109
        return $this->toArray();
110
    }
111
112
    public function toArray(): array
113
    {
114
        return iterator_to_array($this, false);
115
    }
116
117
    /**
118
     * Find which of the inner iterators an index corresponds to
119
     *
120
     * @param int $index
121
     * @return array [ArrayAccess, int] The iterator and interior index
122
     */
123
    protected function getIteratorByIndex($index = 0)
124
    {
125
        $runningCount = 0;
126
        
127
        foreach ($this as $innerIt) {
128
            $count = count($innerIt);
129
            if ($index < $runningCount + $count) {
130
                return [$innerIt, $index - $runningCount];
131
            }
132
133
            $runningCount += $count;
134
        }
135
136
        return null;
137
    }
138
}
139