PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.3.1
Defender Security – Malware Scanner, Login Security & Firewall v5.3.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / vendor / ramsey / collection / src / AbstractCollection.php

AbstractCollection.php in Defender Security – Malware Scanner, Login Security & Firewall 5.3.1, at vendor/ramsey/collection/src/AbstractCollection.php

342 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the ramsey/collection library
5 *
6 * For the full copyright and license information, please view the LICENSE
7 * file that was distributed with this source code.
8 *
9 * @copyright Copyright (c) Ben Ramsey <[email protected]>
10 * @license http://opensource.org/licenses/MIT MIT
11 */
12
13 declare(strict_types=1);
14
15 namespace Ramsey\Collection;
16
17 use Closure;
18 use Ramsey\Collection\Exception\CollectionMismatchException;
19 use Ramsey\Collection\Exception\InvalidArgumentException;
20 use Ramsey\Collection\Exception\InvalidSortOrderException;
21 use Ramsey\Collection\Exception\OutOfBoundsException;
22 use Ramsey\Collection\Tool\TypeTrait;
23 use Ramsey\Collection\Tool\ValueExtractorTrait;
24 use Ramsey\Collection\Tool\ValueToStringTrait;
25
26 use function array_filter;
27 use function array_map;
28 use function array_merge;
29 use function array_search;
30 use function array_udiff;
31 use function array_uintersect;
32 use function current;
33 use function end;
34 use function in_array;
35 use function is_int;
36 use function is_object;
37 use function reset;
38 use function spl_object_id;
39 use function sprintf;
40 use function unserialize;
41 use function usort;
42
43 /**
44 * This class provides a basic implementation of `CollectionInterface`, to
45 * minimize the effort required to implement this interface
46 *
47 * @template T
48 * @extends AbstractArray<T>
49 * @implements CollectionInterface<T>
50 */
51 abstract class AbstractCollection extends AbstractArray implements CollectionInterface
52 {
53 use TypeTrait;
54 use ValueToStringTrait;
55 use ValueExtractorTrait;
56
57 /**
58 * @inheritDoc
59 */
60 public function add($element): bool
61 {
62 $this[] = $element;
63
64 return true;
65 }
66
67 /**
68 * @inheritDoc
69 */
70 public function contains($element, bool $strict = true): bool
71 {
72 return in_array($element, $this->data, $strict);
73 }
74
75 /**
76 * @inheritDoc
77 */
78 public function offsetSet($offset, $value): void
79 {
80 if ($this->checkType($this->getType(), $value) === false) {
81 throw new InvalidArgumentException(
82 'Value must be of type ' . $this->getType() . '; value is '
83 . $this->toolValueToString($value),
84 );
85 }
86
87 if ($offset === null) {
88 $this->data[] = $value;
89 } else {
90 $this->data[$offset] = $value;
91 }
92 }
93
94 /**
95 * @inheritDoc
96 */
97 public function remove($element): bool
98 {
99 if (($position = array_search($element, $this->data, true)) !== false) {
100 unset($this[$position]);
101
102 return true;
103 }
104
105 return false;
106 }
107
108 /**
109 * @inheritDoc
110 */
111 public function column(string $propertyOrMethod): array
112 {
113 $temp = [];
114
115 foreach ($this->data as $item) {
116 /** @var mixed $value */
117 $value = $this->extractValue($item, $propertyOrMethod);
118
119 /** @psalm-suppress MixedAssignment */
120 $temp[] = $value;
121 }
122
123 return $temp;
124 }
125
126 /**
127 * @inheritDoc
128 */
129 public function first()
130 {
131 if ($this->isEmpty()) {
132 throw new OutOfBoundsException('Can\'t determine first item. Collection is empty');
133 }
134
135 reset($this->data);
136
137 /** @var T $first */
138 $first = current($this->data);
139
140 return $first;
141 }
142
143 /**
144 * @inheritDoc
145 */
146 public function last()
147 {
148 if ($this->isEmpty()) {
149 throw new OutOfBoundsException('Can\'t determine last item. Collection is empty');
150 }
151
152 /** @var T $item */
153 $item = end($this->data);
154 reset($this->data);
155
156 return $item;
157 }
158
159 public function sort(string $propertyOrMethod, string $order = self::SORT_ASC): CollectionInterface
160 {
161 if (!in_array($order, [self::SORT_ASC, self::SORT_DESC], true)) {
162 throw new InvalidSortOrderException('Invalid sort order given: ' . $order);
163 }
164
165 $collection = clone $this;
166
167 usort(
168 $collection->data,
169 /**
170 * @param T $a
171 * @param T $b
172 */
173 function ($a, $b) use ($propertyOrMethod, $order): int {
174 /** @var mixed $aValue */
175 $aValue = $this->extractValue($a, $propertyOrMethod);
176
177 /** @var mixed $bValue */
178 $bValue = $this->extractValue($b, $propertyOrMethod);
179
180 return ($aValue <=> $bValue) * ($order === self::SORT_DESC ? -1 : 1);
181 },
182 );
183
184 return $collection;
185 }
186
187 public function filter(callable $callback): CollectionInterface
188 {
189 $collection = clone $this;
190 $collection->data = array_merge([], array_filter($collection->data, $callback));
191
192 return $collection;
193 }
194
195 /**
196 * {@inheritdoc}
197 */
198 public function where(string $propertyOrMethod, $value): CollectionInterface
199 {
200 return $this->filter(function ($item) use ($propertyOrMethod, $value) {
201 /** @var mixed $accessorValue */
202 $accessorValue = $this->extractValue($item, $propertyOrMethod);
203
204 return $accessorValue === $value;
205 });
206 }
207
208 public function map(callable $callback): CollectionInterface
209 {
210 return new Collection('mixed', array_map($callback, $this->data));
211 }
212
213 public function diff(CollectionInterface $other): CollectionInterface
214 {
215 $this->compareCollectionTypes($other);
216
217 $diffAtoB = array_udiff($this->data, $other->toArray(), $this->getComparator());
218 $diffBtoA = array_udiff($other->toArray(), $this->data, $this->getComparator());
219
220 /** @var array<array-key, T> $diff */
221 $diff = array_merge($diffAtoB, $diffBtoA);
222
223 $collection = clone $this;
224 $collection->data = $diff;
225
226 return $collection;
227 }
228
229 public function intersect(CollectionInterface $other): CollectionInterface
230 {
231 $this->compareCollectionTypes($other);
232
233 /** @var array<array-key, T> $intersect */
234 $intersect = array_uintersect($this->data, $other->toArray(), $this->getComparator());
235
236 $collection = clone $this;
237 $collection->data = $intersect;
238
239 return $collection;
240 }
241
242 public function merge(CollectionInterface ...$collections): CollectionInterface
243 {
244 $mergedCollection = clone $this;
245
246 foreach ($collections as $index => $collection) {
247 if (!$collection instanceof static) {
248 throw new CollectionMismatchException(
249 sprintf('Collection with index %d must be of type %s', $index, static::class),
250 );
251 }
252
253 // When using generics (Collection.php, Set.php, etc),
254 // we also need to make sure that the internal types match each other
255 if ($this->getUniformType($collection) !== $this->getUniformType($this)) {
256 throw new CollectionMismatchException(
257 sprintf(
258 'Collection items in collection with index %d must be of type %s',
259 $index,
260 $this->getType(),
261 ),
262 );
263 }
264
265 foreach ($collection as $key => $value) {
266 if (is_int($key)) {
267 $mergedCollection[] = $value;
268 } else {
269 $mergedCollection[$key] = $value;
270 }
271 }
272 }
273
274 return $mergedCollection;
275 }
276
277 /**
278 * @inheritDoc
279 */
280 public function unserialize($serialized): void
281 {
282 /** @var array<array-key, T> $data */
283 $data = unserialize($serialized, ['allowed_classes' => [$this->getType()]]);
284
285 $this->data = $data;
286 }
287
288 /**
289 * @param CollectionInterface<T> $other
290 */
291 private function compareCollectionTypes(CollectionInterface $other): void
292 {
293 if (!$other instanceof static) {
294 throw new CollectionMismatchException('Collection must be of type ' . static::class);
295 }
296
297 // When using generics (Collection.php, Set.php, etc),
298 // we also need to make sure that the internal types match each other
299 if ($this->getUniformType($other) !== $this->getUniformType($this)) {
300 throw new CollectionMismatchException('Collection items must be of type ' . $this->getType());
301 }
302 }
303
304 private function getComparator(): Closure
305 {
306 return /**
307 * @param T $a
308 * @param T $b
309 */
310 function ($a, $b): int {
311 // If the two values are object, we convert them to unique scalars.
312 // If the collection contains mixed values (unlikely) where some are objects
313 // and some are not, we leave them as they are.
314 // The comparator should still work and the result of $a < $b should
315 // be consistent but unpredictable since not documented.
316 if (is_object($a) && is_object($b)) {
317 $a = spl_object_id($a);
318 $b = spl_object_id($b);
319 }
320
321 return $a === $b ? 0 : ($a < $b ? 1 : -1);
322 };
323 }
324
325 /**
326 * @param CollectionInterface<mixed> $collection
327 */
328 private function getUniformType(CollectionInterface $collection): string
329 {
330 switch ($collection->getType()) {
331 case 'integer':
332 return 'int';
333 case 'boolean':
334 return 'bool';
335 case 'double':
336 return 'float';
337 default:
338 return $collection->getType();
339 }
340 }
341 }
342