Completed
Push — master ( 52f875...78f1d5 )
by Dawid
10:30 queued 47s
created

Collection::insert()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 2
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Igni\Storage\Mapping\Collection;
4
5
use Igni\Storage\Exception\CollectionException;
6
use Iterator;
7
use Igni\Exception\OutOfBoundsException;
8
9
/**
10
 * Immutable collection representation.
11
 */
12
class Collection implements \Igni\Storage\Mapping\Collection
13
{
14
    protected $length = 0;
15
    protected $cursor = 0;
16
    protected $items = [];
17
18 21
    public function __construct(Iterator $cursor = null)
19
    {
20 21
        if ($cursor === null) {
21 4
            $this->items = [];
22 4
            $this->length = 0;
23 4
            return;
24
        }
25
26 21
        $this->items = iterator_to_array($cursor);
27 21
        $this->length = count($this->items);
28 21
    }
29
30
    /**
31
     * Reduces a collection to a single value by iteratively combining elements of the collection
32
     * using the provided callable.
33
     *
34
     * @param callable $f($previousValue, $current)
35
     * @param mixed $initialValue
36
     * @return mixed
37
     */
38 1
    public function reduce(callable $f, $initialValue = null)
39
    {
40 1
        foreach ($this as $element) {
41 1
            $initialValue = $f($initialValue, $element);
42
        }
43
44 1
        return $initialValue;
45
    }
46
47
    /**
48
     * Returns new collection with items sorted by the order specified by the compare function.
49
     *
50
     * @param callable $compare
51
     * @return Collection
52
     */
53 1
    public function sort(callable $compare): self
54
    {
55 1
        $collection = clone $this;
56 1
        usort($collection->items, $compare);
57
58 1
        return $collection;
59
    }
60
61
    /**
62
     * Returns new collection with elements that are created by calling f callable on each element.
63
     *
64
     * @param callable $f($element)
65
     * @return Collection
66
     */
67 1
    public function map(callable $f): self
68
    {
69 1
        $collection = new self();
70 1
        foreach ($this as $item) {
71 1
            $collection->items[] = $f($item);
72
        }
73 1
        $collection->length = $this->length;
74
75 1
        return $collection;
76
    }
77
78
    /**
79
     * Returns new collection with inserted element(s) at position index.
80
     *
81
     * @param int $index
82
     * @param mixed ...$elements
83
     * @return Collection
84
     */
85 3
    public function insert(int $index, ...$elements): self
86
    {
87 3
        if ($index < 0 || $index - 1 > $this->length) {
88 1
            throw CollectionException::forInvalidIndex($index);
89
        }
90
91 2
        $collection = clone $this;
92 2
        array_splice($collection->items, $index, 0, $elements);
93
94 2
        return $collection;
95
    }
96
97
    /**
98
     * Returns a new collection with elements extracted as slice.
99
     *
100
     * @param int $start
101
     * @param int $length
102
     * @return Collection
103
     */
104 1
    public function slice(int $start, int $length): self
105
    {
106 1
        $items = array_slice($this->items, $start, $length);
107 1
        $collection = new self();
108 1
        $collection->items = $items;
109 1
        $collection->length = $length;
110
111 1
        return $collection;
112
    }
113
114
    /**
115
     * Returns a new Collection with all elements that satisfy the predicate test.
116
     * @param callable $test($element)
117
     * @return Collection
118
     */
119 1
    public function where(callable $test): self
120
    {
121 1
        $collection = new self();
122 1
        $length = 0;
123 1
        foreach ($this as $item) {
124 1
            if ($test($item)) {
125 1
                $length++;
126 1
                $collection->items[] = $item;
127
            }
128
        }
129 1
        $collection->length = $length;
130
131 1
        return $collection;
132
    }
133
134
    /**
135
     * Checks whether every element of this iterable satisfies test
136
     * @param callable $test($element)
137
     * @return bool
138
     */
139 1
    public function every(callable $test): bool
140
    {
141 1
        foreach($this as $item) {
142 1
            if (!$test($item)) {
143 1
                return false;
144
            }
145
        }
146
147 1
        return true;
148
    }
149
150
    /**
151
     * Checks whether any element of this iterable satisfies test
152
     * @param callable $test($element)
153
     * @return bool
154
     */
155 1
    public function any(callable $test): bool
156
    {
157 1
        foreach($this as $item) {
158 1
            if ($test($item)) {
159 1
                return true;
160
            }
161
        }
162
163 1
        return false;
164
    }
165
166
    /**
167
     * Returns instance of the collection with reversed items.
168
     * @return Collection
169
     */
170 1
    public function reverse(): self
171
    {
172 1
        $collection = clone $this;
173 1
        $collection->items = array_reverse($collection->items);
174
175 1
        return $collection;
176
    }
177
178
    /**
179
     * Removes all objects from this collection; the length of the collection becomes zero.
180
     */
181 1
    public function clear(): void
182
    {
183 1
        $this->items = [];
184 1
        $this->length = 0;
185 1
        $this->cursor = 0;
186 1
    }
187
188
    /**
189
     * Returns new collection with value added to the end of this list, extending the length by one.
190
     *
191
     * @param mixed $element
192
     * @return Collection
193
     */
194 1
    public function add($element): self
195
    {
196 1
        $collection = clone $this;
197 1
        $collection->cursor = 0;
198 1
        $collection->items[] = $element;
199 1
        $collection->length++;
200
201 1
        return $collection;
202
    }
203
204 1
    public function addMany(...$elements): self
205
    {
206 1
        $collection = clone $this;
207 1
        $collection->cursor = 0;
208 1
        $collection->items = array_merge($collection->items, $elements);
209 1
        $collection->length += count($elements);
210
211 1
        return $collection;
212
    }
213
214
    /**
215
     * Returns new collection with removed value, decreasing the length by one.
216
     *
217
     * @param $element
218
     * @return Collection
219
     */
220 1
    public function remove($element): self
221
    {
222 1
        $index = array_search($element, $this->items);
223 1
        if ($index === false) {
224
            return $this;
225
        }
226
227 1
        $collection = clone $this;
228 1
        array_splice($collection->items, $index, 1);
0 ignored issues
show
Bug introduced by
It seems like $index can also be of type string; however, parameter $offset of array_splice() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

228
        array_splice($collection->items, /** @scrutinizer ignore-type */ $index, 1);
Loading history...
229 1
        $collection->length--;
230 1
        $collection->cursor = 0;
231
232 1
        return $collection;
233
    }
234
235 1
    public function removeMany(...$elements): self
236
    {
237 1
        $collection = clone $this;
238 1
        $collection->items = array_values(array_diff($collection->items, $elements));
239 1
        $collection->length -= count($elements);
240 1
        $collection->cursor = 0;
241
242 1
        return $collection;
243
    }
244
245
    /**
246
     * Checks if collection contains given element.
247
     * @param $element
248
     * @return bool
249
     */
250 1
    public function contains($element): bool
251
    {
252 1
        return in_array($element, $this->items);
253
    }
254
255
    /**
256
     * Returns first element of the collection
257
     * @return mixed
258
     */
259 1
    public function first()
260
    {
261 1
        $this->cursor = 0;
262
263 1
        return $this->at($this->cursor);
264
    }
265
266
    /**
267
     * Returns last element of the collection
268
     * @return mixed
269
     */
270 1
    public function last()
271
    {
272 1
        $this->cursor = $this->length - 1;
273
274 1
        return $this->current();
275
    }
276
277
    /**
278
     * Returns element at the index
279
     * @param int $offset
280
     * @return mixed
281
     */
282 9
    public function at(int $offset)
283
    {
284 9
        if ($offset < $this->length) {
285 9
            return $this->items[$offset];
286
        }
287
288 1
        throw new OutOfBoundsException("Invalid offset: ${offset}");
289
    }
290
291
    /**
292
     * Returns current element
293
     * @return mixed
294
     */
295
    public function current()
296
    {
297 9
        return $this->at($this->cursor);
298
    }
299
300
    /**
301
     * Moves cursor of the collection forward by one.
302
     */
303
    public function next(): void
304
    {
305 7
        $this->cursor++;
306 7
    }
307
308
    /**
309
     * Moves cursor of the collection backward by one.
310
     */
311
    public function previous(): void
312
    {
313 1
        if ($this->cursor > 0) {
314 1
            $this->cursor--;
315
        }
316 1
    }
317
318
    public function key(): int
319
    {
320 1
        return $this->cursor;
321
    }
322
323
    public function valid(): bool
324
    {
325 6
        if ($this->cursor < $this->length) {
326 6
            return true;
327
        }
328
329 6
        return false;
330
    }
331
332
    public function rewind(): void
333
    {
334 6
        $this->cursor = 0;
335 6
    }
336
337
    public function count(): int
338
    {
339 3
        return $this->length;
340
    }
341
342
    public function toArray(): array
343
    {
344 11
        return $this->items;
345
    }
346
}
347