summaryrefslogtreecommitdiff
path: root/vendor/doctrine/collections/src/ArrayCollection.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/collections/src/ArrayCollection.php')
-rw-r--r--vendor/doctrine/collections/src/ArrayCollection.php490
1 files changed, 490 insertions, 0 deletions
diff --git a/vendor/doctrine/collections/src/ArrayCollection.php b/vendor/doctrine/collections/src/ArrayCollection.php
new file mode 100644
index 0000000..2e7d0e3
--- /dev/null
+++ b/vendor/doctrine/collections/src/ArrayCollection.php
@@ -0,0 +1,490 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\Common\Collections;
6
7use ArrayIterator;
8use Closure;
9use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
10use ReturnTypeWillChange;
11use Stringable;
12use Traversable;
13
14use function array_filter;
15use function array_key_exists;
16use function array_keys;
17use function array_map;
18use function array_reduce;
19use function array_reverse;
20use function array_search;
21use function array_slice;
22use function array_values;
23use function count;
24use function current;
25use function end;
26use function in_array;
27use function key;
28use function next;
29use function reset;
30use function spl_object_hash;
31use function uasort;
32
33use const ARRAY_FILTER_USE_BOTH;
34
35/**
36 * An ArrayCollection is a Collection implementation that wraps a regular PHP array.
37 *
38 * Warning: Using (un-)serialize() on a collection is not a supported use-case
39 * and may break when we change the internals in the future. If you need to
40 * serialize a collection use {@link toArray()} and reconstruct the collection
41 * manually.
42 *
43 * @psalm-template TKey of array-key
44 * @psalm-template T
45 * @template-implements Collection<TKey,T>
46 * @template-implements Selectable<TKey,T>
47 * @psalm-consistent-constructor
48 */
49class ArrayCollection implements Collection, Selectable, Stringable
50{
51 /**
52 * An array containing the entries of this collection.
53 *
54 * @psalm-var array<TKey,T>
55 * @var mixed[]
56 */
57 private array $elements = [];
58
59 /**
60 * Initializes a new ArrayCollection.
61 *
62 * @psalm-param array<TKey,T> $elements
63 */
64 public function __construct(array $elements = [])
65 {
66 $this->elements = $elements;
67 }
68
69 /**
70 * {@inheritDoc}
71 */
72 public function toArray()
73 {
74 return $this->elements;
75 }
76
77 /**
78 * {@inheritDoc}
79 */
80 public function first()
81 {
82 return reset($this->elements);
83 }
84
85 /**
86 * Creates a new instance from the specified elements.
87 *
88 * This method is provided for derived classes to specify how a new
89 * instance should be created when constructor semantics have changed.
90 *
91 * @param array $elements Elements.
92 * @psalm-param array<K,V> $elements
93 *
94 * @return static
95 * @psalm-return static<K,V>
96 *
97 * @psalm-template K of array-key
98 * @psalm-template V
99 */
100 protected function createFrom(array $elements)
101 {
102 return new static($elements);
103 }
104
105 /**
106 * {@inheritDoc}
107 */
108 public function last()
109 {
110 return end($this->elements);
111 }
112
113 /**
114 * {@inheritDoc}
115 */
116 public function key()
117 {
118 return key($this->elements);
119 }
120
121 /**
122 * {@inheritDoc}
123 */
124 public function next()
125 {
126 return next($this->elements);
127 }
128
129 /**
130 * {@inheritDoc}
131 */
132 public function current()
133 {
134 return current($this->elements);
135 }
136
137 /**
138 * {@inheritDoc}
139 */
140 public function remove(string|int $key)
141 {
142 if (! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) {
143 return null;
144 }
145
146 $removed = $this->elements[$key];
147 unset($this->elements[$key]);
148
149 return $removed;
150 }
151
152 /**
153 * {@inheritDoc}
154 */
155 public function removeElement(mixed $element)
156 {
157 $key = array_search($element, $this->elements, true);
158
159 if ($key === false) {
160 return false;
161 }
162
163 unset($this->elements[$key]);
164
165 return true;
166 }
167
168 /**
169 * Required by interface ArrayAccess.
170 *
171 * @param TKey $offset
172 *
173 * @return bool
174 */
175 #[ReturnTypeWillChange]
176 public function offsetExists(mixed $offset)
177 {
178 return $this->containsKey($offset);
179 }
180
181 /**
182 * Required by interface ArrayAccess.
183 *
184 * @param TKey $offset
185 *
186 * @return T|null
187 */
188 #[ReturnTypeWillChange]
189 public function offsetGet(mixed $offset)
190 {
191 return $this->get($offset);
192 }
193
194 /**
195 * Required by interface ArrayAccess.
196 *
197 * @param TKey|null $offset
198 * @param T $value
199 *
200 * @return void
201 */
202 #[ReturnTypeWillChange]
203 public function offsetSet(mixed $offset, mixed $value)
204 {
205 if ($offset === null) {
206 $this->add($value);
207
208 return;
209 }
210
211 $this->set($offset, $value);
212 }
213
214 /**
215 * Required by interface ArrayAccess.
216 *
217 * @param TKey $offset
218 *
219 * @return void
220 */
221 #[ReturnTypeWillChange]
222 public function offsetUnset(mixed $offset)
223 {
224 $this->remove($offset);
225 }
226
227 /**
228 * {@inheritDoc}
229 */
230 public function containsKey(string|int $key)
231 {
232 return isset($this->elements[$key]) || array_key_exists($key, $this->elements);
233 }
234
235 /**
236 * {@inheritDoc}
237 */
238 public function contains(mixed $element)
239 {
240 return in_array($element, $this->elements, true);
241 }
242
243 /**
244 * {@inheritDoc}
245 */
246 public function exists(Closure $p)
247 {
248 foreach ($this->elements as $key => $element) {
249 if ($p($key, $element)) {
250 return true;
251 }
252 }
253
254 return false;
255 }
256
257 /**
258 * {@inheritDoc}
259 *
260 * @psalm-param TMaybeContained $element
261 *
262 * @return int|string|false
263 * @psalm-return (TMaybeContained is T ? TKey|false : false)
264 *
265 * @template TMaybeContained
266 */
267 public function indexOf($element)
268 {
269 return array_search($element, $this->elements, true);
270 }
271
272 /**
273 * {@inheritDoc}
274 */
275 public function get(string|int $key)
276 {
277 return $this->elements[$key] ?? null;
278 }
279
280 /**
281 * {@inheritDoc}
282 */
283 public function getKeys()
284 {
285 return array_keys($this->elements);
286 }
287
288 /**
289 * {@inheritDoc}
290 */
291 public function getValues()
292 {
293 return array_values($this->elements);
294 }
295
296 /**
297 * {@inheritDoc}
298 *
299 * @return int<0, max>
300 */
301 #[ReturnTypeWillChange]
302 public function count()
303 {
304 return count($this->elements);
305 }
306
307 /**
308 * {@inheritDoc}
309 */
310 public function set(string|int $key, mixed $value)
311 {
312 $this->elements[$key] = $value;
313 }
314
315 /**
316 * {@inheritDoc}
317 *
318 * @psalm-suppress InvalidPropertyAssignmentValue
319 *
320 * This breaks assumptions about the template type, but it would
321 * be a backwards-incompatible change to remove this method
322 */
323 public function add(mixed $element)
324 {
325 $this->elements[] = $element;
326 }
327
328 /**
329 * {@inheritDoc}
330 */
331 public function isEmpty()
332 {
333 return empty($this->elements);
334 }
335
336 /**
337 * {@inheritDoc}
338 *
339 * @return Traversable<int|string, mixed>
340 * @psalm-return Traversable<TKey, T>
341 */
342 #[ReturnTypeWillChange]
343 public function getIterator()
344 {
345 return new ArrayIterator($this->elements);
346 }
347
348 /**
349 * {@inheritDoc}
350 *
351 * @psalm-param Closure(T):U $func
352 *
353 * @return static
354 * @psalm-return static<TKey, U>
355 *
356 * @psalm-template U
357 */
358 public function map(Closure $func)
359 {
360 return $this->createFrom(array_map($func, $this->elements));
361 }
362
363 /**
364 * {@inheritDoc}
365 */
366 public function reduce(Closure $func, $initial = null)
367 {
368 return array_reduce($this->elements, $func, $initial);
369 }
370
371 /**
372 * {@inheritDoc}
373 *
374 * @psalm-param Closure(T, TKey):bool $p
375 *
376 * @return static
377 * @psalm-return static<TKey,T>
378 */
379 public function filter(Closure $p)
380 {
381 return $this->createFrom(array_filter($this->elements, $p, ARRAY_FILTER_USE_BOTH));
382 }
383
384 /**
385 * {@inheritDoc}
386 */
387 public function findFirst(Closure $p)
388 {
389 foreach ($this->elements as $key => $element) {
390 if ($p($key, $element)) {
391 return $element;
392 }
393 }
394
395 return null;
396 }
397
398 /**
399 * {@inheritDoc}
400 */
401 public function forAll(Closure $p)
402 {
403 foreach ($this->elements as $key => $element) {
404 if (! $p($key, $element)) {
405 return false;
406 }
407 }
408
409 return true;
410 }
411
412 /**
413 * {@inheritDoc}
414 */
415 public function partition(Closure $p)
416 {
417 $matches = $noMatches = [];
418
419 foreach ($this->elements as $key => $element) {
420 if ($p($key, $element)) {
421 $matches[$key] = $element;
422 } else {
423 $noMatches[$key] = $element;
424 }
425 }
426
427 return [$this->createFrom($matches), $this->createFrom($noMatches)];
428 }
429
430 /**
431 * Returns a string representation of this object.
432 * {@inheritDoc}
433 *
434 * @return string
435 */
436 #[ReturnTypeWillChange]
437 public function __toString()
438 {
439 return self::class . '@' . spl_object_hash($this);
440 }
441
442 /**
443 * {@inheritDoc}
444 */
445 public function clear()
446 {
447 $this->elements = [];
448 }
449
450 /**
451 * {@inheritDoc}
452 */
453 public function slice(int $offset, int|null $length = null)
454 {
455 return array_slice($this->elements, $offset, $length, true);
456 }
457
458 /** @psalm-return Collection<TKey, T>&Selectable<TKey,T> */
459 public function matching(Criteria $criteria)
460 {
461 $expr = $criteria->getWhereExpression();
462 $filtered = $this->elements;
463
464 if ($expr) {
465 $visitor = new ClosureExpressionVisitor();
466 $filter = $visitor->dispatch($expr);
467 $filtered = array_filter($filtered, $filter);
468 }
469
470 $orderings = $criteria->orderings();
471
472 if ($orderings) {
473 $next = null;
474 foreach (array_reverse($orderings) as $field => $ordering) {
475 $next = ClosureExpressionVisitor::sortByField($field, $ordering === Order::Descending ? -1 : 1, $next);
476 }
477
478 uasort($filtered, $next);
479 }
480
481 $offset = $criteria->getFirstResult();
482 $length = $criteria->getMaxResults();
483
484 if ($offset !== null && $offset > 0 || $length !== null && $length > 0) {
485 $filtered = array_slice($filtered, (int) $offset, $length, true);
486 }
487
488 return $this->createFrom($filtered);
489 }
490}