• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

elephox-dev / framework / 4877852653

pending completion
4877852653

push

github

Ricardo Boss
WIP

38 of 38 new or added lines in 6 files covered. (100.0%)

3863 of 5835 relevant lines covered (66.2%)

8.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.85
/modules/Collection/src/ArrayList.php
1
<?php
2
declare(strict_types=1);
3

4
namespace Elephox\Collection;
5

6
use ArrayIterator;
7
use Elephox\Collection\Contract\GenericArrayList;
8
use InvalidArgumentException;
9
use Iterator;
10
use Traversable;
11

12
/**
13
 * @template T
14
 *
15
 * @implements GenericArrayList<T>
16
 */
17
class ArrayList implements GenericArrayList
18
{
19
        // TODO: replace generic enumerable function with array-specific functions where possible
20
        /**
21
         * @use IsKeyedEnumerable<int, T>
22
         */
23
        use IsKeyedEnumerable {
24
                IsKeyedEnumerable::count as genericCount;
25
        }
26

27
        /**
28
         * @use IsArrayEnumerable<int, T>
29
         */
30
        use IsArrayEnumerable {
31
                IsArrayEnumerable::contains as arrayContains;
32
                IsArrayEnumerable::containsKey as arrayContainsKey;
33
                IsArrayEnumerable::count as arrayCount;
34
        }
35

36
        /**
37
         * @template UValue
38
         *
39
         * @param ArrayList<UValue>|iterable<UValue>|UValue $value
40
         *
41
         * @return ArrayList<UValue>
42
         */
43
        public static function from(mixed $value): self
44
        {
45
                if ($value instanceof self) {
15✔
46
                        return $value;
1✔
47
                }
48

49
                if (is_array($value)) {
15✔
50
                        if (!array_is_list($value)) {
13✔
51
                                throw new InvalidArgumentException('ArrayList::from() expects a list of values');
1✔
52
                        }
53

54
                        return new self($value);
12✔
55
                }
56

57
                if ($value instanceof Iterator) {
2✔
58
                        return new self(array_values(iterator_to_array($value)));
1✔
59
                }
60

61
                /** @var list<UValue> $value */
62
                $value = [$value];
1✔
63

64
                return new self($value);
1✔
65
        }
66

67
        /**
68
         * @param array<int, T> $items
69
         */
70
        public function __construct(
71
                protected array $items = [],
72
        ) {
73
        }
153✔
74

75
        public function getIterator(): Traversable
76
        {
77
                return new ArrayIterator($this->items);
69✔
78
        }
79

80
        public function offsetExists(mixed $offset): bool
81
        {
82
                /** @psalm-suppress DocblockTypeContradiction */
83
                if (!is_int($offset)) {
3✔
84
                        throw new OffsetNotAllowedException($offset);
1✔
85
                }
86

87
                return $offset < $this->count();
2✔
88
        }
89

90
        public function offsetGet(mixed $offset): mixed
91
        {
92
                /** @psalm-suppress DocblockTypeContradiction */
93
                if (!is_int($offset)) {
4✔
94
                        throw new OffsetNotAllowedException($offset);
1✔
95
                }
96

97
                return $this->elementAt($offset);
4✔
98
        }
99

100
        public function offsetSet(mixed $offset, mixed $value): void
101
        {
102
                if ($offset === null) {
4✔
103
                        $this->add($value);
2✔
104

105
                        return;
2✔
106
                }
107

108
                /** @psalm-suppress DocblockTypeContradiction */
109
                if (!is_int($offset)) {
2✔
110
                        throw new OffsetNotAllowedException($offset);
1✔
111
                }
112

113
                $this->put($offset, $value);
1✔
114
        }
115

116
        public function offsetUnset(mixed $offset): void
117
        {
118
                /** @psalm-suppress DocblockTypeContradiction */
119
                if (!is_int($offset)) {
2✔
120
                        throw new OffsetNotAllowedException($offset);
1✔
121
                }
122

123
                $this->removeAt($offset);
1✔
124
        }
125

126
        public function add(mixed $value): bool
127
        {
128
                $this->items[] = $value;
16✔
129

130
                return true;
16✔
131
        }
132

133
        public function addAll(iterable $values): bool
134
        {
135
                $added = false;
1✔
136

137
                foreach ($values as $value) {
1✔
138
                        $added = $this->add($value) || $added;
1✔
139
                }
140

141
                return $added;
1✔
142
        }
143

144
        public function put(int $index, mixed $value): bool
145
        {
146
                if ($index < 0 || $index > $this->count()) {
2✔
147
                        throw new OffsetNotAllowedException($index);
1✔
148
                }
149

150
                $this->items[$index] = $value;
1✔
151

152
                return true;
1✔
153
        }
154

155
        public function removeValue(mixed $value, ?callable $comparer = null): bool
156
        {
157
                $index = $this->indexOf($value, $comparer);
2✔
158

159
                if ($index === -1) {
2✔
160
                        return false;
1✔
161
                }
162

163
                $this->removeAt($index);
1✔
164

165
                return true;
1✔
166
        }
167

168
        public function elementAt(int $index): mixed
169
        {
170
                if ($index < 0 || $index >= $this->count()) {
12✔
171
                        throw new OffsetNotFoundException($index);
1✔
172
                }
173

174
                /** @var T */
175
                return $this->items[$index];
11✔
176
        }
177

178
        /**
179
         * @return T
180
         *
181
         * @param int $index
182
         */
183
        public function removeAt(int $index): mixed
184
        {
185
                if ($index < 0 || $index >= $this->count()) {
6✔
186
                        throw new OffsetNotFoundException($index);
1✔
187
                }
188

189
                $removed = $this->items[$index];
5✔
190

191
                array_splice($this->items, $index, 1);
5✔
192

193
                return $removed;
5✔
194
        }
195

196
        public function clear(): void
197
        {
198
                $this->items = [];
7✔
199
        }
200

201
        public function indexOf(mixed $value, ?callable $comparer = null): int
202
        {
203
                $comparer ??= DefaultEqualityComparer::same(...);
3✔
204

205
                foreach ($this->items as $index => $item) {
3✔
206
                        if ($comparer($item, $value)) {
3✔
207
                                /** @var int */
208
                                return $index;
2✔
209
                        }
210
                }
211

212
                return -1;
2✔
213
        }
214

215
        public function lastIndexOf(mixed $value, ?callable $comparer = null): int
216
        {
217
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
218

219
                $lastMatchingIndex = -1;
1✔
220
                foreach ($this->items as $index => $item) {
1✔
221
                        if ($comparer($item, $value)) {
1✔
222
                                $lastMatchingIndex = $index;
1✔
223
                        }
224
                }
225

226
                return $lastMatchingIndex;
1✔
227
        }
228

229
        /**
230
         * @param null|callable(T, int): bool $predicate
231
         *
232
         * @return T
233
         *
234
         * @throws EmptySequenceException
235
         */
236
        public function pop(?callable $predicate = null): mixed
237
        {
238
                if ($this->count() === 0) {
4✔
239
                        throw new EmptySequenceException();
1✔
240
                }
241

242
                if ($predicate === null) {
3✔
243
                        /** @var T */
244
                        return array_pop($this->items);
1✔
245
                }
246

247
                /** @var null|int $key */
248
                $key = $this->reverse()->firstKeyOrDefault(null, $predicate);
2✔
249
                if ($key === null) {
2✔
250
                        throw new EmptySequenceException();
1✔
251
                }
252

253
                return $this->removeAt($key);
1✔
254
        }
255

256
        /**
257
         * @param null|callable(T, int): bool $predicate
258
         *
259
         * @return T
260
         *
261
         * @throws EmptySequenceException
262
         */
263
        public function shift(?callable $predicate = null): mixed
264
        {
265
                if ($this->count() === 0) {
4✔
266
                        throw new EmptySequenceException();
1✔
267
                }
268

269
                if ($predicate === null) {
3✔
270
                        /** @var T */
271
                        return array_shift($this->items);
1✔
272
                }
273

274
                $key = $this->firstKeyOrDefault(null, $predicate);
2✔
275
                if ($key === null) {
2✔
276
                        throw new EmptySequenceException();
1✔
277
                }
278

279
                return $this->removeAt($key);
1✔
280
        }
281

282
        /**
283
         * @param T $value
284
         */
285
        public function unshift(mixed $value): void
286
        {
287
                array_unshift($this->items, $value);
1✔
288
        }
289

290
        public function contains(mixed $value, ?callable $comparer = null): bool
291
        {
292
                return $this->arrayContains($value, $comparer);
8✔
293
        }
294

295
        public function containsKey(mixed $key, ?callable $comparer = null): bool
296
        {
297
                return $this->arrayContainsKey($key, $comparer);
1✔
298
        }
299

300
        /**
301
         * @param T $value
302
         */
303
        public function insertAt(int $index, mixed $value): void
304
        {
305
                array_splice($this->items, $index, 0, [$value]);
1✔
306
        }
307

308
        /**
309
         * @return ArrayList<T>&static
310
         */
311
        public function slice(int $offset, ?int $length = null): static
312
        {
313
                /** @var ArrayList<T>&static */
314
                return new self(array_slice($this->items, $offset, $length));
1✔
315
        }
316

317
        public function key(): ?int
318
        {
319
                return key($this->items);
2✔
320
        }
321

322
        public function count(?callable $predicate = null): int {
323
                if ($predicate === null) {
27✔
324
                        return $this->arrayCount();
27✔
325
                }
326

327
                return $this->genericCount($predicate);
×
328
        }
329
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc