Passed
Push — master ( 69397d...433084 )
by Chris
07:38
created

CollectionKernel   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 294
Duplicated Lines 0 %

Test Coverage

Coverage 87.16%

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 42
eloc 85
c 4
b 0
f 1
dl 0
loc 294
ccs 95
cts 109
cp 0.8716
rs 9.0399

42 Methods

Rating   Name   Duplication   Size   Complexity  
A hasWhere() 0 3 1
A __construct() 0 34 1
A hasItems() 0 3 1
A contains() 0 3 1
A first() 0 3 1
A collect() 0 3 1
A __serialize() 0 3 1
A remove() 0 3 1
A fetch() 0 3 1
A last() 0 7 1
A where() 0 3 1
A matches() 0 3 1
A sortCustom() 0 7 1
A diff() 0 3 1
A filter() 0 3 1
A contrast() 0 3 1
A sortMapped() 0 5 1
A loop() 0 3 1
A walk() 0 3 1
A toArray() 0 3 1
A firstWhere() 0 3 1
A jsonSerialize() 0 3 1
A getIterator() 0 3 1
A sortBy() 0 5 1
A foreach() 0 3 1
A column() 0 4 1
A map() 0 3 1
A intersect() 0 3 1
A merge() 0 3 1
A values() 0 3 1
A remix() 0 3 1
A getPropertyValue() 0 3 1
A sort() 0 3 1
A operate() 0 3 1
A count() 0 3 1
A getRemix() 0 3 1
A getBasicQuery() 0 8 1
A insert() 0 3 1
A toJson() 0 3 1
A query() 0 3 1
A spawnFrom() 0 8 1
A spawnWith() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like CollectionKernel often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CollectionKernel, and based on these observations, apply Extract Interface, too.

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(),
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
        $this->driver->collect($this->items, $items);
106
    }
107
108
    public function insert(object $item, $offset = null): bool
109
    {
110
        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 $this->getBasicQuery(...func_get_args())->match($this->items);
150
    }
151
152 6
    public function firstWhere(string $property, string $operator, $value): ?object
153
    {
154 6
        return $this->getBasicQuery(...func_get_args())->first($this->items);
155
    }
156
157 30
    public function query(CollectionQueryInterface $query): object
158
    {
159 30
        return $this->spawnFrom($query->query($this->items));
160
    }
161
162 18
    public function where(string $property, string $operator, $value): object
163
    {
164 18
        return $this->query($this->getBasicQuery(...func_get_args()));
165
    }
166
167 12
    public function filter(callable $callback): object
168
    {
169 12
        return $this->spawnFrom(array_filter($this->items, $callback));
170
    }
171
172 6
    public function matches(array $collection): bool
173
    {
174 6
        return $this->collectionComparator->matches($this->items, $collection);
175
    }
176
177 69
    public function remix(ArrayFusionInterface $fusion, array ...$collections): object
178
    {
179 69
        return $this->spawnFrom($fusion->remix($this->items, ...$collections));
180
    }
181
182 18
    public function diff(array ...$collections): object
183
    {
184 18
        return $this->getRemix('diff', ...$collections);
185
    }
186
187 21
    public function contrast(array ...$collections): object
188
    {
189 21
        return $this->getRemix('contrast', ...$collections);
190
    }
191
192 21
    public function intersect(array ...$collections): object
193
    {
194 21
        return $this->getRemix('intersect', ...$collections);
195
    }
196
197 9
    public function merge(array ...$collections): object
198
    {
199 9
        return $this->getRemix('merge', ...$collections);
200
    }
201
202 102
    public function sort(CollectionSorterInterface $sorter, string $order = Order::Asc): object
203
    {
204 102
        return $this->spawnFrom($sorter->sort($this->items, $order));
205
    }
206
207 51
    public function sortBy(string $property, string $order = Order::Asc): object
208
    {
209 51
        return $this->sort(
210 51
            new PropertyBasedSorter($this->propertyResolver, $property),
211
            $order
212
        );
213
    }
214
215 51
    public function sortMapped(array $map, string $property, string $order = Order::Asc): object
216
    {
217 51
        return $this->sort(
218 51
            new MapBasedSorter($this->propertyResolver, $property, $map),
219
            $order
220
        );
221
    }
222
223
    public function sortCustom(callable $callback): object
224
    {
225
        $clone = clone $this;
226
227
        usort($clone->items, $callback);
228
229
        return $this->spawnWith($clone);
230
    }
231
232 9
    public function map(callable $callback): array
233
    {
234 9
        return array_map($callback, $this->items);
235
    }
236
237 3
    public function walk(callable $callback): void
238
    {
239 3
        array_walk($this->items, $callback);
240
    }
241
242 3
    public function loop(LoopInterface $loop, callable $callback): void
243
    {
244 3
        $loop->iterate($this->items, $callback);
245
    }
246
247 3
    public function foreach(callable $callback): void
248
    {
249 3
        $this->loop(new ForeachLoop(), $callback);
250
    }
251
252
    public function operate(CollectionAggregateInterface $aggregate, array ...$collections)
253
    {
254
        return $aggregate->operate($this->items, ...$collections);
255
    }
256
257 6
    public function column(string $property): array
258
    {
259 6
        return $this->map(
260 6
            fn ($item) => $this->getPropertyValue($item, $property)
261
        );
262
    }
263
264
    public function values(): array
265
    {
266
        return array_values($this->items);
267
    }
268
269 234
    public function toArray(): array
270
    {
271 234
        return $this->items;
272
    }
273
274 3
    public function toJson(): string
275
    {
276 3
        return $this->jsonSerializer->serialize($this->items);
277
    }
278
279 3
    public function count(): int
280
    {
281 3
        return count($this->items);
282
    }
283
284 3
    public function jsonSerialize(): array
285
    {
286 3
        return $this->items;
287
    }
288
289 3
    public function getIterator(): Traversable
290
    {
291 3
        return new ArrayIterator($this->items);
292
    }
293
294 24
    protected function getBasicQuery(string $property, string $operator, $value): CollectionQueryInterface
295
    {
296 24
        return new BasicQuery(
297
            $property,
298
            $operator,
299
            $value,
300 24
            $this->propertyResolver,
301 24
            $this->operationProvider
302
        );
303
    }
304
305 69
    protected function getRemix(string $fusion, array ...$collections): object
306
    {
307 69
        return $this->remix($this->fusions->fetch($fusion), ...$collections);
308
    }
309
310 6
    protected function getPropertyValue(object $item, string $property)
311
    {
312 6
        return $this->propertyResolver->resolveProperty($item, $property);
313
    }
314
315 207
    protected function spawnWith(CollectionKernel $clone): object
316
    {
317 207
        return ($this->generator)($clone);
318
    }
319
320 207
    protected function spawnFrom(array $items): object
321
    {
322 207
        $clone = clone $this;
323
324 207
        $clone->items = [];
325 207
        $clone->collect($items);
326
327 207
        return $this->spawnWith($clone);
328
    }
329
}
330