Passed
Push — master ( 60a2b8...a4571b )
by Chris
07:43
created

CollectionKernel   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 285
Duplicated Lines 0 %

Test Coverage

Coverage 92.45%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 41
eloc 83
dl 0
loc 285
ccs 98
cts 106
cp 0.9245
rs 9.1199
c 1
b 0
f 0

40 Methods

Rating   Name   Duplication   Size   Complexity  
A getItemsWhere() 0 4 1
A where() 0 3 1
A performQuery() 0 3 1
A matches() 0 3 1
A hasWhere() 0 3 1
A spawnFrom() 0 8 1
A sortCustom() 0 7 1
A diff() 0 4 1
A add() 0 3 1
A __construct() 0 25 1
A hasItems() 0 3 1
A filter() 0 4 1
A contrast() 0 4 1
A spawnWith() 0 3 1
A sortMapped() 0 5 1
A loop() 0 3 1
A walk() 0 3 1
A toArray() 0 3 1
A contains() 0 3 1
A firstWhere() 0 5 2
A jsonSerialize() 0 3 1
A getIterator() 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 4 1
A collect() 0 3 1
A merge() 0 4 1
A __serialize() 0 3 1
A remove() 0 3 1
A sortWith() 0 3 1
A values() 0 3 1
A getPropertyValue() 0 3 1
A count() 0 3 1
A last() 0 7 1
A getBasicQuery() 0 8 1
A toJson() 0 3 1
A query() 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\CollectionComparatorInterface;
11
use WebTheory\Collection\Contracts\CollectionKernelInterface;
12
use WebTheory\Collection\Contracts\CollectionQueryInterface;
13
use WebTheory\Collection\Contracts\CollectionSorterInterface;
14
use WebTheory\Collection\Contracts\JsonSerializerInterface;
15
use WebTheory\Collection\Contracts\LoopInterface;
16
use WebTheory\Collection\Contracts\OperationProviderInterface;
17
use WebTheory\Collection\Contracts\PropertyResolverInterface;
18
use WebTheory\Collection\Enum\Order;
19
use WebTheory\Collection\Iteration\ForeachLoop;
20
use WebTheory\Collection\Json\BasicJsonSerializer;
21
use WebTheory\Collection\Kernel\Factory\CollectionKernelSubsystemFactory;
22
use WebTheory\Collection\Query\BasicQuery;
23
use WebTheory\Collection\Query\Operation\Operations;
24
use WebTheory\Collection\Sorting\MapBasedSorter;
25
use WebTheory\Collection\Sorting\PropertyBasedSorter;
26
27
class CollectionKernel implements CollectionKernelInterface, IteratorAggregate
28
{
29
    /**
30
     * Array of objects to be operated on.
31
     *
32
     * @var array<int,object>|array<string,object>
33
     */
34
    protected array $items = [];
35
36
    /**
37
     * Function to create a new instance of the interfacing collection.
38
     */
39
    protected Closure $generator;
40
41
    protected ArrayDriverInterface $driver;
42
43
    protected PropertyResolverInterface $propertyResolver;
44
45
    protected CollectionComparatorInterface $aggregateComparator;
46
47
    protected OperationProviderInterface $operationProvider;
48
49
    protected JsonSerializerInterface $jsonSerializer;
50
51 216
    public function __construct(
52
        array $items,
53
        Closure $generator,
54
        ?string $identifier = null,
55
        array $accessors = [],
56
        ?bool $mapToIdentifier = false,
57
        ?JsonSerializerInterface $jsonSerializer = null,
58
        ?OperationProviderInterface $operations = null
59
    ) {
60 216
        $this->generator = $generator;
61
62 216
        $this->jsonSerializer = $jsonSerializer ?? new BasicJsonSerializer();
63 216
        $this->operationProvider = $operations ?? new Operations();
64
65 216
        $subsystems = new CollectionKernelSubsystemFactory(
66
            $identifier,
67
            $accessors,
68
            $mapToIdentifier
0 ignored issues
show
Bug introduced by
It seems like $mapToIdentifier can also be of type null; however, parameter $mapToIdentifier of WebTheory\Collection\Ker...mFactory::__construct() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

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