Passed
Push — master ( ee29df...4a790f )
by Chris
06:37 queued 02:23
created

CollectionKernel   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 290
Duplicated Lines 0 %

Test Coverage

Coverage 90.65%

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 42
eloc 83
c 4
b 0
f 1
dl 0
loc 290
ccs 97
cts 107
cp 0.9065
rs 9.0399

42 Methods

Rating   Name   Duplication   Size   Complexity  
A toArray() 0 3 1
A jsonSerialize() 0 3 1
A getIterator() 0 3 1
A count() 0 3 1
A getBasicQuery() 0 8 1
A toJson() 0 3 1
A where() 0 3 1
A query() 0 3 1
A matches() 0 3 1
A hasWhere() 0 3 1
A spawnFrom() 0 8 1
A sortCustom() 0 3 1
A diff() 0 3 1
A __construct() 0 34 1
A hasItems() 0 3 1
A filter() 0 3 1
A contrast() 0 3 1
A spawnWith() 0 3 1
A sortMapped() 0 5 1
A loop() 0 3 1
A walk() 0 3 1
A contains() 0 3 1
A firstWhere() 0 3 1
A sortBy() 0 5 1
A foreach() 0 3 1
A first() 0 3 1
A column() 0 4 1
A map() 0 3 1
A intersect() 0 3 1
A collect() 0 3 1
A merge() 0 3 1
A __serialize() 0 3 1
A remove() 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 fetch() 0 3 1
A last() 0 7 1
A getRemix() 0 3 1
A insert() 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\CallbackSorter;
32
use WebTheory\Collection\Sorting\MapBasedSorter;
33
use WebTheory\Collection\Sorting\PropertyBasedSorter;
34
35
class CollectionKernel implements CollectionKernelInterface, IteratorAggregate
36
{
37
    /**
38
     * Array of objects to be operated on.
39
     *
40
     * @var array<int|string,object>
41
     */
42
    protected array $items = [];
43
44
    /**
45
     * Function to create a new instance of the client collection class when
46
     * performing an operation that spawns a new collection. The callback will
47
     * be passed a clone of the kernel with the mutated array.
48
     */
49
    protected Closure $generator;
50
51
    protected ArrayDriverInterface $driver;
52
53
    protected PropertyResolverInterface $propertyResolver;
54
55
    protected CollectionComparatorInterface $collectionComparator;
56
57
    protected OperationProviderInterface $operationProvider;
58
59
    protected JsonSerializerInterface $jsonSerializer;
60
61
    protected FusionSelection $fusions;
62
63 333
    public function __construct(
64
        array $items,
65
        Closure $generator,
66
        ?string $identifier = null,
67
        array $accessors = [],
68
        bool $mapToIdentifier = false,
69
        ?JsonSerializerInterface $jsonSerializer = null,
70
        ?OperationProviderInterface $operationProvider = null
71
    ) {
72 333
        $this->generator = $generator;
73
74 333
        $this->jsonSerializer = $jsonSerializer ?? new BasicJsonSerializer();
75 333
        $this->operationProvider = $operationProvider ?? new Operations();
76
77 333
        $subsystems = new CollectionKernelSubsystemFactory(
78
            $identifier,
79
            $accessors,
80
            $mapToIdentifier
81
        );
82
83 333
        $this->driver = $subsystems->getArrayDriver();
84 333
        $this->propertyResolver = $subsystems->getPropertyResolver();
85 333
        $this->collectionComparator = $subsystems->getCollectionComparator();
86
87 333
        $objectComparator = $subsystems->getObjectComparator();
88
89 333
        $this->fusions = new FusionSelection([
90 333
            'contrast' => new Contrast($objectComparator),
91 333
            'diff' => new Diff($objectComparator),
92 333
            'intersect' => new Intersection($objectComparator),
93 333
            'merge' => new Merger(),
94
        ]);
95
96 333
        $this->collect($items);
97
    }
98
99 3
    public function __serialize(): array
100
    {
101 3
        return $this->toArray();
102
    }
103
104 333
    public function collect(array $items): void
105
    {
106 333
        $this->driver->collect($this->items, $items);
107
    }
108
109
    public function insert(object $item, $offset = null): bool
110
    {
111
        return $this->driver->insert($this->items, $item, $offset);
112
    }
113
114
    public function fetch($item): object
115
    {
116
        return $this->driver->fetch($this->items, $item);
117
    }
118
119 9
    public function remove($item): bool
120
    {
121 9
        return $this->driver->remove($this->items, $item);
122
    }
123
124 24
    public function contains($item): bool
125
    {
126 24
        return $this->driver->contains($this->items, $item);
127
    }
128
129 3
    public function first(): object
130
    {
131 3
        return reset($this->items);
132
    }
133
134 3
    public function last(): object
135
    {
136 3
        $last = end($this->items);
137
138 3
        reset($this->items);
139
140 3
        return $last;
141
    }
142
143 6
    public function hasItems(): bool
144
    {
145 6
        return !empty($this->items);
146
    }
147
148
    public function hasWhere(string $property, string $operator, $value): bool
149
    {
150
        return $this->getBasicQuery(...func_get_args())->match($this->items);
151
    }
152
153 6
    public function firstWhere(string $property, string $operator, $value): ?object
154
    {
155 6
        return $this->getBasicQuery(...func_get_args())->first($this->items);
156
    }
157
158 30
    public function query(CollectionQueryInterface $query): object
159
    {
160 30
        return $this->spawnFrom($query->query($this->items));
161
    }
162
163 18
    public function where(string $property, string $operator, $value): object
164
    {
165 18
        return $this->query($this->getBasicQuery(...func_get_args()));
166
    }
167
168 12
    public function filter(callable $callback): object
169
    {
170 12
        return $this->spawnFrom(array_filter($this->items, $callback));
171
    }
172
173 6
    public function matches(array $collection): bool
174
    {
175 6
        return $this->collectionComparator->matches($this->items, $collection);
176
    }
177
178 69
    public function remix(ArrayFusionInterface $fusion, array ...$collections): object
179
    {
180 69
        return $this->spawnFrom($fusion->remix($this->items, ...$collections));
181
    }
182
183 18
    public function diff(array ...$collections): object
184
    {
185 18
        return $this->getRemix('diff', ...$collections);
186
    }
187
188 21
    public function contrast(array ...$collections): object
189
    {
190 21
        return $this->getRemix('contrast', ...$collections);
191
    }
192
193 21
    public function intersect(array ...$collections): object
194
    {
195 21
        return $this->getRemix('intersect', ...$collections);
196
    }
197
198 9
    public function merge(array ...$collections): object
199
    {
200 9
        return $this->getRemix('merge', ...$collections);
201
    }
202
203 126
    public function sort(CollectionSorterInterface $sorter, string $order = Order::Asc): object
204
    {
205 126
        return $this->spawnFrom($sorter->sort($this->items, $order));
206
    }
207
208 51
    public function sortBy(string $property, string $order = Order::Asc): object
209
    {
210 51
        return $this->sort(
211 51
            new PropertyBasedSorter($this->propertyResolver, $property),
212
            $order
213
        );
214
    }
215
216 51
    public function sortMapped(array $map, string $property, string $order = Order::Asc): object
217
    {
218 51
        return $this->sort(
219 51
            new MapBasedSorter($this->propertyResolver, $property, $map),
220
            $order
221
        );
222
    }
223
224 24
    public function sortCustom(callable $callback, string $order = Order::Asc): object
225
    {
226 24
        return $this->sort(new CallbackSorter($callback), $order);
227
    }
228
229 9
    public function map(callable $callback): array
230
    {
231 9
        return array_map($callback, $this->items);
232
    }
233
234 3
    public function walk(callable $callback): void
235
    {
236 3
        array_walk($this->items, $callback);
237
    }
238
239 3
    public function loop(LoopInterface $loop, callable $callback): void
240
    {
241 3
        $loop->iterate($this->items, $callback);
242
    }
243
244 3
    public function foreach(callable $callback): void
245
    {
246 3
        $this->loop(new ForeachLoop(), $callback);
247
    }
248
249
    public function operate(CollectionAggregateInterface $aggregate, array ...$collections)
250
    {
251
        return $aggregate->operate($this->items, ...$collections);
252
    }
253
254 6
    public function column(string $property): array
255
    {
256 6
        return $this->map(
257 6
            fn ($item) => $this->getPropertyValue($item, $property)
258
        );
259
    }
260
261
    public function values(): array
262
    {
263
        return array_values($this->items);
264
    }
265
266 258
    public function toArray(): array
267
    {
268 258
        return $this->items;
269
    }
270
271 3
    public function toJson(): string
272
    {
273 3
        return $this->jsonSerializer->serialize($this->items);
274
    }
275
276 3
    public function count(): int
277
    {
278 3
        return count($this->items);
279
    }
280
281 3
    public function jsonSerialize(): array
282
    {
283 3
        return $this->items;
284
    }
285
286 3
    public function getIterator(): Traversable
287
    {
288 3
        return new ArrayIterator($this->items);
289
    }
290
291 24
    protected function getBasicQuery(string $property, string $operator, $value): CollectionQueryInterface
292
    {
293 24
        return new BasicQuery(
294
            $property,
295
            $operator,
296
            $value,
297 24
            $this->propertyResolver,
298 24
            $this->operationProvider
299
        );
300
    }
301
302 69
    protected function getRemix(string $fusion, array ...$collections): object
303
    {
304 69
        return $this->remix($this->fusions->fetch($fusion), ...$collections);
305
    }
306
307 6
    protected function getPropertyValue(object $item, string $property)
308
    {
309 6
        return $this->propertyResolver->resolveProperty($item, $property);
310
    }
311
312 231
    protected function spawnWith(CollectionKernel $clone): object
313
    {
314 231
        return ($this->generator)($clone);
315
    }
316
317 231
    protected function spawnFrom(array $items): object
318
    {
319 231
        $clone = clone $this;
320
321 231
        $clone->items = [];
322 231
        $clone->collect($items);
323
324 231
        return $this->spawnWith($clone);
325
    }
326
}
327