GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — develop ( 3d4c79...7ab88f )
by Baptiste
05:30
created

Primitive   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 443
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 137
dl 0
loc 443
ccs 153
cts 153
cp 1
rs 6.96
c 1
b 0
f 0
wmc 53

26 Methods

Rating   Name   Duplication   Size   Complexity  
A get() 0 7 2
A keyType() 0 3 1
A foreach() 0 4 2
A size() 0 4 1
A keys() 0 12 1
A valueType() 0 3 1
A put() 0 10 1
A map() 0 25 3
A filter() 0 11 3
A equals() 0 17 5
A groupBy() 0 40 5
A count() 0 3 1
A contains() 0 4 1
A clear() 0 7 1
A values() 0 6 1
A __construct() 0 13 2
A remove() 0 12 2
A reduce() 0 7 2
A toSetOf() 0 12 3
A empty() 0 7 1
A clearMap() 0 3 1
A partition() 0 22 3
A toMapOf() 0 12 3
A merge() 0 11 1
A toSequenceOf() 0 12 3
A normalizeKey() 0 9 3

How to fix   Complexity   

Complex Class

Complex classes like Primitive 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 Primitive, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types = 1);
3
4
namespace Innmind\Immutable\Map;
5
6
use Innmind\Immutable\{
7
    Map,
8
    Type,
9
    Str,
10
    Sequence,
11
    Set,
12
    Pair,
13
    ValidateArgument,
14
    Exception\LogicException,
15
    Exception\ElementNotFound,
16
    Exception\CannotGroupEmptyStructure,
17
};
18
19
/**
20
 * @template T
21
 * @template S
22
 */
23
final class Primitive implements Implementation
24
{
25
    private string $keyType;
26
    private string $valueType;
27
    private ValidateArgument $validateKey;
28
    private ValidateArgument $validateValue;
29
    /** @var array<T, S> */
30
    private array $values;
31
    private ?int $size;
32
33 72
    public function __construct(string $keyType, string $valueType)
34
    {
35 72
        $this->validateKey = Type::of($keyType);
36
37 72
        if (!in_array($keyType, ['int', 'integer', 'string'], true)) {
38 1
            throw new LogicException;
39
        }
40
41 71
        $this->validateValue = Type::of($valueType);
42 71
        $this->keyType = $keyType;
43 71
        $this->valueType = $valueType;
44 71
        $this->values = [];
45 71
        $this->size = null;
46 71
    }
47
48 22
    public function keyType(): string
49
    {
50 22
        return $this->keyType;
51
    }
52
53 20
    public function valueType(): string
54
    {
55 20
        return $this->valueType;
56
    }
57
58 27
    public function size(): int
59
    {
60
        /** @psalm-suppress MixedArgumentTypeCoercion */
61 27
        return $this->size ?? $this->size = \count($this->values);
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 11
    public function count(): int
68
    {
69 11
        return $this->size();
70
    }
71
72
    /**
73
     * @param T $key
0 ignored issues
show
Bug introduced by
The type Innmind\Immutable\Map\T was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
74
     * @param S $value
0 ignored issues
show
Bug introduced by
The type Innmind\Immutable\Map\S was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
75
     *
76
     * @return self<T, S>
77
     */
78 58
    public function put($key, $value): self
79
    {
80 58
        ($this->validateKey)($key, 1);
81 57
        ($this->validateValue)($value, 2);
82
83 56
        $map = clone $this;
84 56
        $map->size = null;
85 56
        $map->values[$key] = $value;
86
87 56
        return $map;
88
    }
89
90
    /**
91
     * @param T $key
92
     *
93
     * @throws ElementNotFound
94
     *
95
     * @return S
96
     */
97 30
    public function get($key)
98
    {
99 30
        if (!$this->contains($key)) {
100 2
            throw new ElementNotFound($key);
101
        }
102
103 28
        return $this->values[$key];
104
    }
105
106
    /**
107
     * @param T $key
108
     */
109 35
    public function contains($key): bool
110
    {
111
        /** @psalm-suppress MixedArgumentTypeCoercion */
112 35
        return \array_key_exists($key, $this->values);
113
    }
114
115
    /**
116
     * @return self<T, S>
117
     */
118 10
    public function clear(): self
119
    {
120 10
        $map = clone $this;
121 10
        $map->size = null;
122 10
        $map->values = [];
123
124 10
        return $map;
125
    }
126
127
    /**
128
     * @param Implementation<T, S> $map
129
     */
130 7
    public function equals(Implementation $map): bool
131
    {
132 7
        if ($map->size() !== $this->size()) {
133 2
            return false;
134
        }
135
136 7
        foreach ($this->values as $k => $v) {
137 6
            if (!$map->contains($k)) {
138 2
                return false;
139
            }
140
141 6
            if ($map->get($k) !== $v) {
142 3
                return false;
143
            }
144
        }
145
146 6
        return true;
147
    }
148
149
    /**
150
     * @param callable(T, S): bool $predicate
151
     *
152
     * @return self<T, S>
153
     */
154 2
    public function filter(callable $predicate): self
155
    {
156 2
        $map = $this->clear();
157
158 2
        foreach ($this->values as $k => $v) {
159 2
            if ($predicate($this->normalizeKey($k), $v) === true) {
160 2
                $map->values[$k] = $v;
161
            }
162
        }
163
164 2
        return $map;
165
    }
166
167
    /**
168
     * @param callable(T, S): void $function
169
     */
170 3
    public function foreach(callable $function): void
171
    {
172 3
        foreach ($this->values as $k => $v) {
173 3
            $function($this->normalizeKey($k), $v);
174
        }
175 3
    }
176
177
    /**
178
     * @template D
179
     * @param callable(T, S): D $discriminator
180
     *
181
     * @throws CannotGroupEmptyStructure
182
     *
183
     * @return Map<D, Map<T, S>>
184
     */
185 4
    public function groupBy(callable $discriminator): Map
186
    {
187 4
        if ($this->empty()) {
188 2
            throw new CannotGroupEmptyStructure;
189
        }
190
191 2
        $groups = null;
192
193 2
        foreach ($this->values as $k => $v) {
194
            /** @var T */
195 2
            $key = $this->normalizeKey($k);
196
            /** @var S */
197 2
            $value = $v;
198 2
            $discriminant = $discriminator($key, $value);
199
200 2
            if ($groups === null) {
201
                /** @var Map<D, Map<T, S>> */
202 2
                $groups = Map::of(
203 2
                    Type::determine($discriminant),
204 2
                    Map::class,
205
                );
206
            }
207
208 2
            if ($groups->contains($discriminant)) {
209
                /** @var Map<T, S> */
210 2
                $group = $groups->get($discriminant);
211
                /** @var Map<T, S> */
212 2
                $group = ($group)($key, $value);
213
214 2
                $groups = ($groups)($discriminant, $group);
215
            } else {
216
                /** @var Map<T, S> */
217 2
                $group = $this->clearMap()($key, $value);
218
219 2
                $groups = ($groups)($discriminant, $group);
220
            }
221
        }
222
223
        /** @var Map<D, Map<T, S>> */
224 2
        return $groups;
225
    }
226
227
    /**
228
     * @return Set<T>
229
     */
230 14
    public function keys(): Set
231
    {
232
        /** @psalm-suppress MixedArgumentTypeCoercion */
233 14
        $keys = \array_keys($this->values);
234
235 14
        return Set::of(
236 14
            $this->keyType,
237 14
            ...\array_map(
238
                function($key) {
239 14
                    return $this->normalizeKey($key);
240 14
                },
241 14
                $keys,
242
            ),
243
        );
244
    }
245
246
    /**
247
     * @return Sequence<S>
248
     */
249 12
    public function values(): Sequence
250
    {
251
        /** @psalm-suppress MixedArgumentTypeCoercion */
252 12
        $values = \array_values($this->values);
253
254 12
        return Sequence::of($this->valueType, ...$values);
255
    }
256
257
    /**
258
     * @param callable(T, S): (S|Pair<T, S>) $function
259
     *
260
     * @return self<T, S>
261
     */
262 6
    public function map(callable $function): self
263
    {
264 6
        $map = $this->clear();
265
266 6
        foreach ($this->values as $k => $v) {
267 6
            $return = $function($this->normalizeKey($k), $v);
268
269 6
            if ($return instanceof Pair) {
270 4
                ($this->validateKey)($return->key(), 1);
271
272
                /** @var T */
273 2
                $key = $return->key();
274
                /** @var S */
275 2
                $value = $return->value();
276
            } else {
277 4
                $key = $k;
278 4
                $value = $return;
279
            }
280
281 4
            ($this->validateValue)($value, 2);
282
283 2
            $map->values[$key] = $value;
284
        }
285
286 2
        return $map;
287
    }
288
289
    /**
290
     * @param T $key
291
     *
292
     * @return self<T, S>
293
     */
294 2
    public function remove($key): self
295
    {
296 2
        if (!$this->contains($key)) {
297 2
            return $this;
298
        }
299
300 2
        $map = clone $this;
301 2
        $map->size = null;
302
        /** @psalm-suppress MixedArrayTypeCoercion */
303 2
        unset($map->values[$key]);
304
305 2
        return $map;
306
    }
307
308
    /**
309
     * @param Implementation<T, S> $map
310
     *
311
     * @return self<T, S>
312
     */
313 1
    public function merge(Implementation $map): self
314
    {
315
        /** @var self<T, S> $merged */
316 1
        $merged = $map->reduce(
317 1
            $this,
318
            function(self $carry, $key, $value): self {
319 1
                return $carry->put($key, $value);
320 1
            }
321
        );
322
323 1
        return $merged;
324
    }
325
326
    /**
327
     * @param callable(T, S): bool $predicate
328
     *
329
     * @return Map<bool, Map<T, S>>
330
     */
331 2
    public function partition(callable $predicate): Map
332
    {
333 2
        $truthy = $this->clearMap();
334 2
        $falsy = $this->clearMap();
335
336 2
        foreach ($this->values as $k => $v) {
337 2
            $return = $predicate($this->normalizeKey($k), $v);
338
339 2
            if ($return === true) {
340 2
                $truthy = ($truthy)($k, $v);
341
            } else {
342 2
                $falsy = ($falsy)($k, $v);
343
            }
344
        }
345
346
        /**
347
         * @psalm-suppress InvalidScalarArgument
348
         * @psalm-suppress InvalidArgument
349
         */
350 2
        return Map::of('bool', Map::class)
351 2
            (true, $truthy)
352 2
            (false, $falsy);
353
    }
354
355
    /**
356
     * @template R
357
     * @param R $carry
0 ignored issues
show
Bug introduced by
The type Innmind\Immutable\Map\R was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
358
     * @param callable(R, T, S): R $reducer
359
     *
360
     * @return R
361
     */
362 5
    public function reduce($carry, callable $reducer)
363
    {
364 5
        foreach ($this->values as $k => $v) {
365 5
            $carry = $reducer($carry, $this->normalizeKey($k), $v);
366
        }
367
368 5
        return $carry;
369
    }
370
371 5
    public function empty(): bool
372
    {
373
        /** @psalm-suppress MixedArgumentTypeCoercion */
374 5
        \reset($this->values);
375
376
        /** @psalm-suppress MixedArgumentTypeCoercion */
377 5
        return \is_null(\key($this->values));
378
    }
379
380
    /**
381
     * @param mixed $value
382
     *
383
     * @return T
384
     */
385 25
    private function normalizeKey($value)
386
    {
387 25
        if ($this->keyType === 'string' && !\is_null($value)) {
388
            /** @var T */
389 1
            return (string) $value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return (string)$value returns the type string which is incompatible with the documented return type Innmind\Immutable\Map\T.
Loading history...
390
        }
391
392
        /** @var T */
393 24
        return $value;
394
    }
395
396
    /**
397
     * @template ST
398
     *
399
     * @param callable(T, S): \Generator<ST> $mapper
400
     *
401
     * @return Sequence<ST>
402
     */
403 1
    public function toSequenceOf(string $type, callable $mapper): Sequence
404
    {
405
        /** @var Sequence<ST> */
406 1
        $sequence = Sequence::of($type);
407
408 1
        foreach ($this->values as $key => $value) {
409 1
            foreach ($mapper($key, $value) as $newValue) {
410 1
                $sequence = ($sequence)($newValue);
411
            }
412
        }
413
414 1
        return $sequence;
415
    }
416
417
    /**
418
     * @template ST
419
     *
420
     * @param callable(T, S): \Generator<ST> $mapper
421
     *
422
     * @return Set<ST>
423
     */
424 2
    public function toSetOf(string $type, callable $mapper): Set
425
    {
426
        /** @var Set<ST> */
427 2
        $set = Set::of($type);
428
429 2
        foreach ($this->values as $key => $value) {
430 2
            foreach ($mapper($key, $value) as $newValue) {
431 2
                $set = ($set)($newValue);
432
            }
433
        }
434
435 2
        return $set;
436
    }
437
438
    /**
439
     * @template MT
440
     * @template MS
441
     *
442
     * @param callable(T, S): \Generator<MT, MS> $mapper
443
     *
444
     * @return Map<MT, MS>
445
     */
446 2
    public function toMapOf(string $key, string $value, callable $mapper): Map
447
    {
448
        /** @var Map<MT, MS> */
449 2
        $map = Map::of($key, $value);
450
451 2
        foreach ($this->values as $key => $value) {
452 2
            foreach ($mapper($key, $value) as $newKey => $newValue) {
453 2
                $map = ($map)($newKey, $newValue);
454
            }
455
        }
456
457 2
        return $map;
458
    }
459
460
    /**
461
     * @return Map<T, S>
462
     */
463 4
    private function clearMap(): Map
464
    {
465 4
        return Map::of($this->keyType, $this->valueType);
466
    }
467
}
468