Collection::sort()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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

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