Passed
Push — master ( 81cd5a...f8b808 )
by Chris
09:03
created

CollectionKernel::spawnWith()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace WebTheory\Collection\Kernel;
4
5
use ArrayIterator;
6
use Closure;
7
use IteratorAggregate;
8
use Traversable;
9
use WebTheory\Collection\Contracts\ArrayDriverInterface;
10
use WebTheory\Collection\Contracts\ArrayFusionInterface;
11
use WebTheory\Collection\Contracts\CollectionAggregateInterface;
12
use WebTheory\Collection\Contracts\CollectionComparatorInterface;
13
use WebTheory\Collection\Contracts\CollectionKernelInterface;
14
use WebTheory\Collection\Contracts\CollectionQueryInterface;
15
use WebTheory\Collection\Contracts\CollectionSorterInterface;
16
use WebTheory\Collection\Contracts\JsonSerializerInterface;
17
use WebTheory\Collection\Contracts\LoopInterface;
18
use WebTheory\Collection\Contracts\OperationProviderInterface;
19
use WebTheory\Collection\Contracts\PropertyResolverInterface;
20
use WebTheory\Collection\Enum\Order;
21
use WebTheory\Collection\Fusion\Collection\FusionSelection;
22
use WebTheory\Collection\Fusion\Contrast;
23
use WebTheory\Collection\Fusion\Diff;
24
use WebTheory\Collection\Fusion\Intersection;
25
use WebTheory\Collection\Fusion\Merger;
26
use WebTheory\Collection\Iteration\ForeachLoop;
27
use WebTheory\Collection\Json\BasicJsonSerializer;
28
use WebTheory\Collection\Kernel\Factory\CollectionKernelSubsystemFactory;
29
use WebTheory\Collection\Query\BasicQuery;
30
use WebTheory\Collection\Query\Operation\Operations;
31
use WebTheory\Collection\Sorting\MapBasedSorter;
32
use WebTheory\Collection\Sorting\PropertyBasedSorter;
33
34
class CollectionKernel implements CollectionKernelInterface, IteratorAggregate
35
{
36
    /**
37
     * Array of objects to be operated on.
38
     *
39
     * @var array<int|string,object>
40
     */
41
    protected array $items = [];
42
43
    /**
44
     * Function to create a new instance of the client collection class when
45
     * performing an operation that spawns a new collection. The callback will
46
     * be passed a clone of the kernel with the mutated array.
47
     */
48
    protected Closure $generator;
49
50
    protected ArrayDriverInterface $driver;
51
52
    protected PropertyResolverInterface $propertyResolver;
53
54
    protected CollectionComparatorInterface $collectionComparator;
55
56
    protected OperationProviderInterface $operationProvider;
57
58
    protected JsonSerializerInterface $jsonSerializer;
59
60
    protected FusionSelection $fusions;
61
62 309
    public function __construct(
63
        array $items,
64
        Closure $generator,
65
        ?string $identifier = null,
66
        array $accessors = [],
67
        bool $mapToIdentifier = false,
68
        ?JsonSerializerInterface $jsonSerializer = null,
69
        ?OperationProviderInterface $operationProvider = null
70
    ) {
71 309
        $this->generator = $generator;
72
73 309
        $this->jsonSerializer = $jsonSerializer ?? new BasicJsonSerializer();
74 309
        $this->operationProvider = $operationProvider ?? new Operations();
75
76 309
        $subsystems = new CollectionKernelSubsystemFactory(
77
            $identifier,
78
            $accessors,
79
            $mapToIdentifier
80
        );
81
82 309
        $this->driver = $subsystems->getArrayDriver();
83 309
        $this->propertyResolver = $subsystems->getPropertyResolver();
84 309
        $this->collectionComparator = $subsystems->getCollectionComparator();
85
86 309
        $objectComparator = $subsystems->getObjectComparator();
87
88 309
        $this->fusions = new FusionSelection([
89 309
            'contrast' => new Contrast($objectComparator),
90 309
            'diff' => new Diff($objectComparator),
91 309
            'intersect' => new Intersection($objectComparator),
92 309
            'merge' => new Merger($objectComparator),
0 ignored issues
show
Unused Code introduced by
The call to WebTheory\Collection\Fusion\Merger::__construct() has too many arguments starting with $objectComparator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

92
            'merge' => /** @scrutinizer ignore-call */ new Merger($objectComparator),

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
93
        ]);
94
95 309
        $this->collect($items);
96
    }
97
98 3
    public function __serialize(): array
99
    {
100 3
        return $this->toArray();
101
    }
102
103 309
    public function collect(array $items): void
104
    {
105 309
        array_walk($items, [$this, 'insert']);
106
    }
107
108 309
    public function insert(object $item, $offset = null): bool
109
    {
110 309
        return $this->driver->insert($this->items, $item, $offset);
111
    }
112
113
    public function fetch($item): object
114
    {
115
        return $this->driver->fetch($this->items, $item);
116
    }
117
118 9
    public function remove($item): bool
119
    {
120 9
        return $this->driver->remove($this->items, $item);
121
    }
122
123 24
    public function contains($item): bool
124
    {
125 24
        return $this->driver->contains($this->items, $item);
126
    }
127
128 3
    public function first(): object
129
    {
130 3
        return reset($this->items);
131
    }
132
133 3
    public function last(): object
134
    {
135 3
        $last = end($this->items);
136
137 3
        reset($this->items);
138
139 3
        return $last;
140
    }
141
142 6
    public function hasItems(): bool
143
    {
144 6
        return !empty($this->items);
145
    }
146
147
    public function hasWhere(string $property, string $operator, $value): bool
148
    {
149
        return !empty($this->getItemsWhere($property, $operator, $value));
150
    }
151
152 6
    public function firstWhere(string $property, string $operator, $value): ?object
153
    {
154 6
        $items = $this->getItemsWhere($property, $operator, $value);
155
156 6
        return reset($items) ?: null;
157
    }
158
159 30
    public function query(CollectionQueryInterface $query): object
160
    {
161 30
        return $this->spawnFrom($this->performQuery($query));
162
    }
163
164 18
    public function where(string $property, string $operator, $value): object
165
    {
166 18
        return $this->query($this->getBasicQuery($property, $operator, $value));
167
    }
168
169 12
    public function filter(callable $callback): object
170
    {
171 12
        return $this->spawnFrom(array_filter($this->items, $callback));
172
    }
173
174 6
    public function column(string $property): array
175
    {
176 6
        return $this->map(
177 6
            fn ($item) => $this->getPropertyValue($item, $property)
178
        );
179
    }
180
181 6
    public function matches(array $collection): bool
182
    {
183 6
        return $this->collectionComparator->matches($this->items, $collection);
184
    }
185
186 69
    public function remix(ArrayFusionInterface $fusion, array ...$collections): object
187
    {
188 69
        return $this->spawnFrom($fusion->remix($this->items, ...$collections));
189
    }
190
191 18
    public function diff(array ...$collections): object
192
    {
193 18
        return $this->remix($this->getFusion('diff'), ...$collections);
194
    }
195
196 21
    public function contrast(array ...$collections): object
197
    {
198 21
        return $this->remix($this->getFusion('contrast'), ...$collections);
199
    }
200
201 21
    public function intersect(array ...$collections): object
202
    {
203 21
        return $this->remix($this->getFusion('intersect'), ...$collections);
204
    }
205
206 9
    public function merge(array ...$collections): object
207
    {
208 9
        return $this->remix($this->getFusion('merge'), ...$collections);
209
    }
210
211 102
    public function sortWith(CollectionSorterInterface $sorter, string $order = Order::Asc): object
212
    {
213 102
        return $this->spawnFrom($sorter->sort($this->items, $order));
214
    }
215
216 51
    public function sortBy(string $property, string $order = Order::Asc): object
217
    {
218 51
        return $this->sortWith(
219 51
            new PropertyBasedSorter($this->propertyResolver, $property),
220
            $order
221
        );
222
    }
223
224 51
    public function sortMapped(array $map, string $property, string $order = Order::Asc): object
225
    {
226 51
        return $this->sortWith(
227 51
            new MapBasedSorter($this->propertyResolver, $property, $map),
228
            $order
229
        );
230
    }
231
232
    public function sortCustom(callable $callback): object
233
    {
234
        $clone = clone $this;
235
236
        usort($clone->items, $callback);
237
238
        return $this->spawnWith($clone);
239
    }
240
241 9
    public function map(callable $callback): array
242
    {
243 9
        return array_map($callback, $this->items);
244
    }
245
246 3
    public function walk(callable $callback): void
247
    {
248 3
        array_walk($this->items, $callback);
249
    }
250
251 3
    public function loop(LoopInterface $loop, callable $callback): void
252
    {
253 3
        $loop->iterate($this->items, $callback);
254
    }
255
256 3
    public function foreach(callable $callback): void
257
    {
258 3
        $this->loop(new ForeachLoop(), $callback);
259
    }
260
261
    public function operate(CollectionAggregateInterface $aggregate, array ...$collections)
262
    {
263
        return $aggregate->operate($this->items, ...$collections);
264
    }
265
266
    public function values(): array
267
    {
268
        return array_values($this->items);
269
    }
270
271 234
    public function toArray(): array
272
    {
273 234
        return $this->items;
274
    }
275
276 3
    public function toJson(): string
277
    {
278 3
        return $this->jsonSerializer->serialize($this->items);
279
    }
280
281 3
    public function count(): int
282
    {
283 3
        return count($this->items);
284
    }
285
286 3
    public function jsonSerialize(): array
287
    {
288 3
        return $this->items;
289
    }
290
291 3
    public function getIterator(): Traversable
292
    {
293 3
        return new ArrayIterator($this->items);
294
    }
295
296 24
    protected function getBasicQuery(string $property, string $operator, $value): CollectionQueryInterface
297
    {
298 24
        return new BasicQuery(
299 24
            $this->propertyResolver,
300
            $property,
301
            $operator,
302
            $value,
303 24
            $this->operationProvider
304
        );
305
    }
306
307 69
    protected function getFusion(string $fusion): ArrayFusionInterface
308
    {
309 69
        return $this->fusions->fetch($fusion);
310
    }
311
312 6
    protected function getPropertyValue(object $item, string $property)
313
    {
314 6
        return $this->propertyResolver->resolveProperty($item, $property);
315
    }
316
317 36
    protected function performQuery(CollectionQueryInterface $query): array
318
    {
319 36
        return $query->query($this->items);
320
    }
321
322 6
    protected function getItemsWhere(string $property, string $operator, $value): array
323
    {
324 6
        return $this->performQuery(
325 6
            $this->getBasicQuery($property, $operator, $value)
326
        );
327
    }
328
329 207
    protected function spawnWith(self $clone): object
330
    {
331 207
        return ($this->generator)($clone);
332
    }
333
334 207
    protected function spawnFrom(array $items): object
335
    {
336 207
        $clone = clone $this;
337
338 207
        $clone->items = [];
339 207
        $clone->collect($items);
340
341 207
        return $this->spawnWith($clone);
342
    }
343
}
344