PluginProbe ʕ •ᴥ•ʔ
ShareThis Dashboard for Google Analytics / trunk
ShareThis Dashboard for Google Analytics vtrunk
3.3.2 trunk 1.0.7 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2.5 2.3.5 2.3.6 2.3.7 2.3.8 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.0.0 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.3.0 3.3.1
googleanalytics / lib / analytics-admin / vendor / ramsey / collection / src / AbstractCollection.php
googleanalytics / lib / analytics-admin / vendor / ramsey / collection / src Last commit date
Exception 3 years ago Map 3 years ago Tool 3 years ago AbstractArray.php 3 years ago AbstractCollection.php 3 years ago AbstractSet.php 3 years ago ArrayInterface.php 3 years ago Collection.php 3 years ago CollectionInterface.php 3 years ago DoubleEndedQueue.php 3 years ago DoubleEndedQueueInterface.php 3 years ago GenericArray.php 3 years ago Queue.php 3 years ago QueueInterface.php 3 years ago Set.php 3 years ago
AbstractCollection.php
319 lines
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 <ben@benramsey.com>
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 reset;
37 use function sprintf;
38 use function unserialize;
39 use function usort;
40
41 /**
42 * This class provides a basic implementation of `CollectionInterface`, to
43 * minimize the effort required to implement this interface
44 *
45 * @template T
46 * @extends AbstractArray<T>
47 * @implements CollectionInterface<T>
48 */
49 abstract class AbstractCollection extends AbstractArray implements CollectionInterface
50 {
51 use TypeTrait;
52 use ValueToStringTrait;
53 use ValueExtractorTrait;
54
55 /**
56 * @inheritDoc
57 */
58 public function add($element): bool
59 {
60 $this[] = $element;
61
62 return true;
63 }
64
65 /**
66 * @inheritDoc
67 */
68 public function contains($element, bool $strict = true): bool
69 {
70 return in_array($element, $this->data, $strict);
71 }
72
73 /**
74 * @inheritDoc
75 */
76 public function offsetSet($offset, $value): void
77 {
78 if ($this->checkType($this->getType(), $value) === false) {
79 throw new InvalidArgumentException(
80 'Value must be of type ' . $this->getType() . '; value is '
81 . $this->toolValueToString($value)
82 );
83 }
84
85 if ($offset === null) {
86 $this->data[] = $value;
87 } else {
88 $this->data[$offset] = $value;
89 }
90 }
91
92 /**
93 * @inheritDoc
94 */
95 public function remove($element): bool
96 {
97 if (($position = array_search($element, $this->data, true)) !== false) {
98 unset($this->data[$position]);
99
100 return true;
101 }
102
103 return false;
104 }
105
106 /**
107 * @inheritDoc
108 */
109 public function column(string $propertyOrMethod): array
110 {
111 $temp = [];
112
113 foreach ($this->data as $item) {
114 /** @var mixed $value */
115 $value = $this->extractValue($item, $propertyOrMethod);
116
117 /** @psalm-suppress MixedAssignment */
118 $temp[] = $value;
119 }
120
121 return $temp;
122 }
123
124 /**
125 * @inheritDoc
126 */
127 public function first()
128 {
129 if ($this->isEmpty()) {
130 throw new OutOfBoundsException('Can\'t determine first item. Collection is empty');
131 }
132
133 reset($this->data);
134
135 /** @var T $first */
136 $first = current($this->data);
137
138 return $first;
139 }
140
141 /**
142 * @inheritDoc
143 */
144 public function last()
145 {
146 if ($this->isEmpty()) {
147 throw new OutOfBoundsException('Can\'t determine last item. Collection is empty');
148 }
149
150 /** @var T $item */
151 $item = end($this->data);
152 reset($this->data);
153
154 return $item;
155 }
156
157 public function sort(string $propertyOrMethod, string $order = self::SORT_ASC): CollectionInterface
158 {
159 if (!in_array($order, [self::SORT_ASC, self::SORT_DESC], true)) {
160 throw new InvalidSortOrderException('Invalid sort order given: ' . $order);
161 }
162
163 $collection = clone $this;
164
165 usort(
166 $collection->data,
167 /**
168 * @param T $a
169 * @param T $b
170 */
171 function ($a, $b) use ($propertyOrMethod, $order): int {
172 /** @var mixed $aValue */
173 $aValue = $this->extractValue($a, $propertyOrMethod);
174
175 /** @var mixed $bValue */
176 $bValue = $this->extractValue($b, $propertyOrMethod);
177
178 return ($aValue <=> $bValue) * ($order === self::SORT_DESC ? -1 : 1);
179 }
180 );
181
182 return $collection;
183 }
184
185 public function filter(callable $callback): CollectionInterface
186 {
187 $collection = clone $this;
188 $collection->data = array_merge([], array_filter($collection->data, $callback));
189
190 return $collection;
191 }
192
193 /**
194 * {@inheritdoc}
195 */
196 public function where(string $propertyOrMethod, $value): CollectionInterface
197 {
198 return $this->filter(function ($item) use ($propertyOrMethod, $value) {
199 /** @var mixed $accessorValue */
200 $accessorValue = $this->extractValue($item, $propertyOrMethod);
201
202 return $accessorValue === $value;
203 });
204 }
205
206 public function map(callable $callback): CollectionInterface
207 {
208 return new Collection('mixed', array_map($callback, $this->data));
209 }
210
211 public function diff(CollectionInterface $other): CollectionInterface
212 {
213 $this->compareCollectionTypes($other);
214
215 $diffAtoB = array_udiff($this->data, $other->toArray(), $this->getComparator());
216 $diffBtoA = array_udiff($other->toArray(), $this->data, $this->getComparator());
217
218 /** @var array<array-key, T> $diff */
219 $diff = array_merge($diffAtoB, $diffBtoA);
220
221 $collection = clone $this;
222 $collection->data = $diff;
223
224 return $collection;
225 }
226
227 public function intersect(CollectionInterface $other): CollectionInterface
228 {
229 $this->compareCollectionTypes($other);
230
231 /** @var array<array-key, T> $intersect */
232 $intersect = array_uintersect($this->data, $other->toArray(), $this->getComparator());
233
234 $collection = clone $this;
235 $collection->data = $intersect;
236
237 return $collection;
238 }
239
240 public function merge(CollectionInterface ...$collections): CollectionInterface
241 {
242 $mergedCollection = clone $this;
243
244 foreach ($collections as $index => $collection) {
245 if (!$collection instanceof static) {
246 throw new CollectionMismatchException(
247 sprintf('Collection with index %d must be of type %s', $index, static::class)
248 );
249 }
250
251 // When using generics (Collection.php, Set.php, etc),
252 // we also need to make sure that the internal types match each other
253 if ($collection->getType() !== $this->getType()) {
254 throw new CollectionMismatchException(
255 sprintf('Collection items in collection with index %d must be of type %s', $index, $this->getType())
256 );
257 }
258
259 foreach ($collection as $key => $value) {
260 if (is_int($key)) {
261 $mergedCollection[] = $value;
262 } else {
263 $mergedCollection[$key] = $value;
264 }
265 }
266 }
267
268 return $mergedCollection;
269 }
270
271 /**
272 * @inheritDoc
273 */
274 public function unserialize($serialized): void
275 {
276 /** @var array<array-key, T> $data */
277 $data = unserialize($serialized, ['allowed_classes' => [$this->getType()]]);
278
279 $this->data = $data;
280 }
281
282 /**
283 * @param CollectionInterface<T> $other
284 */
285 private function compareCollectionTypes(CollectionInterface $other): void
286 {
287 if (!$other instanceof static) {
288 throw new CollectionMismatchException('Collection must be of type ' . static::class);
289 }
290
291 // When using generics (Collection.php, Set.php, etc),
292 // we also need to make sure that the internal types match each other
293 if ($other->getType() !== $this->getType()) {
294 throw new CollectionMismatchException('Collection items must be of type ' . $this->getType());
295 }
296 }
297
298 private function getComparator(): Closure
299 {
300 return /**
301 * @param T $a
302 * @param T $b
303 */
304 function ($a, $b): int {
305 // If the two values are object, we convert them to unique scalars.
306 // If the collection contains mixed values (unlikely) where some are objects
307 // and some are not, we leave them as they are.
308 // The comparator should still work and the result of $a < $b should
309 // be consistent but unpredictable since not documented.
310 if (is_object($a) && is_object($b)) {
311 $a = spl_object_id($a);
312 $b = spl_object_id($b);
313 }
314
315 return $a === $b ? 0 : ($a < $b ? 1 : -1);
316 };
317 }
318 }
319