Passed
Push — master ( d6d802...81cd5a )
by Chris
08:49
created

CollectionKernel::operate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 1
c 1
b 0
f 1
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
cc 1
nc 1
nop 2
crap 2
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(
172 12
            array_values(array_filter($this->items, $callback))
173
        );
174
    }
175
176 6
    public function column(string $property): array
177
    {
178 6
        return $this->map(
179 6
            fn ($item) => $this->getPropertyValue($item, $property)
180
        );
181
    }
182
183 6
    public function matches(array $collection): bool
184
    {
185 6
        return $this->collectionComparator->matches($this->items, $collection);
186
    }
187
188 69
    public function remix(ArrayFusionInterface $fusion, array ...$collections): object
189
    {
190 69
        return $this->spawnFrom($fusion->remix($this->items, ...$collections));
191
    }
192
193 18
    public function diff(array ...$collections): object
194
    {
195 18
        return $this->remix($this->getFusion('diff'), ...$collections);
196
    }
197
198 21
    public function contrast(array ...$collections): object
199
    {
200 21
        return $this->remix($this->getFusion('contrast'), ...$collections);
201
    }
202
203 21
    public function intersect(array ...$collections): object
204
    {
205 21
        return $this->remix($this->getFusion('intersect'), ...$collections);
206
    }
207
208 9
    public function merge(array ...$collections): object
209
    {
210 9
        return $this->remix($this->getFusion('merge'), ...$collections);
211
    }
212
213 102
    public function sortWith(CollectionSorterInterface $sorter, string $order = Order::Asc): object
214
    {
215 102
        return $this->spawnFrom($sorter->sort($this->items, $order));
216
    }
217
218 51
    public function sortBy(string $property, string $order = Order::Asc): object
219
    {
220 51
        return $this->sortWith(
221 51
            new PropertyBasedSorter($this->propertyResolver, $property),
222
            $order
223
        );
224
    }
225
226 51
    public function sortMapped(array $map, string $property, string $order = Order::Asc): object
227
    {
228 51
        return $this->sortWith(
229 51
            new MapBasedSorter($this->propertyResolver, $property, $map),
230
            $order
231
        );
232
    }
233
234
    public function sortCustom(callable $callback): object
235
    {
236
        $clone = clone $this;
237
238
        usort($clone->items, $callback);
239
240
        return $this->spawnWith($clone);
241
    }
242
243 9
    public function map(callable $callback): array
244
    {
245 9
        return array_map($callback, $this->items);
246
    }
247
248 3
    public function walk(callable $callback): void
249
    {
250 3
        array_walk($this->items, $callback);
251
    }
252
253 3
    public function loop(LoopInterface $loop, callable $callback): void
254
    {
255 3
        $loop->iterate($this->items, $callback);
256
    }
257
258 3
    public function foreach(callable $callback): void
259
    {
260 3
        $this->loop(new ForeachLoop(), $callback);
261
    }
262
263
    public function operate(CollectionAggregateInterface $aggregate, array ...$collections)
264
    {
265
        return $aggregate->operate($this->items, ...$collections);
266
    }
267
268
    public function values(): array
269
    {
270
        return array_values($this->items);
271
    }
272
273 234
    public function toArray(): array
274
    {
275 234
        return $this->items;
276
    }
277
278 3
    public function toJson(): string
279
    {
280 3
        return $this->jsonSerializer->serialize($this->items);
281
    }
282
283 3
    public function count(): int
284
    {
285 3
        return count($this->items);
286
    }
287
288 3
    public function jsonSerialize(): array
289
    {
290 3
        return $this->items;
291
    }
292
293 3
    public function getIterator(): Traversable
294
    {
295 3
        return new ArrayIterator($this->items);
296
    }
297
298 24
    protected function getBasicQuery(string $property, string $operator, $value): CollectionQueryInterface
299
    {
300 24
        return new BasicQuery(
301 24
            $this->propertyResolver,
302
            $property,
303
            $operator,
304
            $value,
305 24
            $this->operationProvider
306
        );
307
    }
308
309 69
    protected function getFusion(string $fusion): ArrayFusionInterface
310
    {
311 69
        return $this->fusions->fetch($fusion);
312
    }
313
314 6
    protected function getPropertyValue(object $item, string $property)
315
    {
316 6
        return $this->propertyResolver->resolveProperty($item, $property);
317
    }
318
319 36
    protected function performQuery(CollectionQueryInterface $query): array
320
    {
321 36
        return $query->query($this->items);
322
    }
323
324 6
    protected function getItemsWhere(string $property, string $operator, $value): array
325
    {
326 6
        return $this->performQuery(
327 6
            $this->getBasicQuery($property, $operator, $value)
328
        );
329
    }
330
331 207
    protected function spawnWith(self $clone): object
332
    {
333 207
        return ($this->generator)($clone);
334
    }
335
336 207
    protected function spawnFrom(array $items): object
337
    {
338 207
        $clone = clone $this;
339
340 207
        $clone->items = [];
341 207
        $clone->collect($items);
342
343 207
        return $this->spawnWith($clone);
344
    }
345
}
346