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.
Completed
Push — develop ( fff013...7af216 )
by Baptiste
05:28
created

ObjectKeys   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 472
Duplicated Lines 0 %

Test Coverage

Coverage 98.19%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 150
dl 0
loc 472
ccs 163
cts 166
cp 0.9819
rs 7.44
c 2
b 0
f 0
wmc 52

How to fix   Complexity   

Complex Class

Complex classes like ObjectKeys 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 ObjectKeys, 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
    ValidateArgument\ClassType,
15
    Exception\LogicException,
16
    Exception\ElementNotFound,
17
    Exception\CannotGroupEmptyStructure,
18
};
19
20
/**
21
 * @template T
22
 * @template S
23
 */
24
final class ObjectKeys implements Implementation
25
{
26
    private string $keyType;
27
    private string $valueType;
28
    private ValidateArgument $validateKey;
29
    private ValidateArgument $validateValue;
30
    private \SplObjectStorage $values;
31
32 30
    public function __construct(string $keyType, string $valueType)
33
    {
34 30
        $this->validateKey = Type::of($keyType);
35
36 30
        if (!$this->validateKey instanceof ClassType && $keyType !== 'object') {
37 1
            throw new LogicException;
38
        }
39
40 29
        $this->validateValue = Type::of($valueType);
41 29
        $this->keyType = $keyType;
42 29
        $this->valueType = $valueType;
43 29
        $this->values = new \SplObjectStorage;
44 29
    }
45
46 9
    public function keyType(): string
47
    {
48 9
        return $this->keyType;
49
    }
50
51 8
    public function valueType(): string
52
    {
53 8
        return $this->valueType;
54
    }
55
56 7
    public function size(): int
57
    {
58 7
        return $this->values->count();
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function count(): int
65
    {
66
        return $this->size();
67
    }
68
69
    /**
70
     * @param T $key
71
     * @param S $value
72
     *
73
     * @return self<T, S>
74
     */
75 25
    public function put($key, $value): Implementation
76
    {
77 25
        ($this->validateKey)($key, 1);
78 24
        ($this->validateValue)($value, 2);
79
80 23
        $map = clone $this;
81 23
        $map->values = clone $this->values;
82
        /** @psalm-suppress MixedArgumentTypeCoercion */
83 23
        $map->values[$key] = $value;
84
85 23
        return $map;
86
    }
87
88
    /**
89
     * @param T $key
90
     *
91
     * @throws ElementNotFound
92
     *
93
     * @return S
94
     */
95 7
    public function get($key)
96
    {
97 7
        if (!$this->contains($key)) {
98 1
            throw new ElementNotFound($key);
99
        }
100
101
        /**
102
         * @psalm-suppress MixedArgumentTypeCoercion
103
         * @var S
104
         */
105 6
        return $this->values->offsetGet($key);
106
    }
107
108
    /**
109
     * @param T $key
110
     */
111 10
    public function contains($key): bool
112
    {
113 10
        if (!is_object($key)) {
114 1
            return false;
115
        }
116
117
        /** @psalm-suppress MixedArgumentTypeCoercion */
118 10
        return $this->values->offsetExists($key);
119
    }
120
121
    /**
122
     * @return self<T, S>
123
     */
124 5
    public function clear(): self
125
    {
126 5
        $map = clone $this;
127 5
        $map->values = new \SplObjectStorage;
128
129 5
        return $map;
130
    }
131
132
    /**
133
     * @param Implementation<T, S> $map
134
     */
135 3
    public function equals(Implementation $map): bool
136
    {
137 3
        if ($map->size() !== $this->size()) {
138 1
            return false;
139
        }
140
141 3
        foreach ($this->values as $k) {
142
            /** @var T $key */
143 3
            $key = $k;
144
            /** @var S $v */
145 3
            $v = $this->values[$k];
146
147 3
            if (!$map->contains($key)) {
148
                return false;
149
            }
150
151 3
            if ($map->get($key) !== $v) {
152 3
                return false;
153
            }
154
        }
155
156 1
        return true;
157
    }
158
159
    /**
160
     * @param callable(T, S): bool $predicate
161
     *
162
     * @return self<T, S>
163
     */
164 1
    public function filter(callable $predicate): self
165
    {
166 1
        $map = $this->clear();
167
168 1
        foreach ($this->values as $k) {
169
            /** @var T $key */
170 1
            $key = $k;
171
            /** @var S $v */
172 1
            $v = $this->values[$k];
173
174 1
            if ($predicate($key, $v) === true) {
175 1
                $map->values[$k] = $v;
176
            }
177
        }
178
179 1
        return $map;
180
    }
181
182
    /**
183
     * @param callable(T, S): void $function
184
     */
185 1
    public function foreach(callable $function): void
186
    {
187 1
        foreach ($this->values as $k) {
188
            /** @var T $key */
189 1
            $key = $k;
190
            /** @var S $v */
191 1
            $v = $this->values[$k];
192
193 1
            $function($key, $v);
194
        }
195 1
    }
196
197
    /**
198
     * @template D
199
     * @param callable(T, S): D $discriminator
200
     *
201
     * @throws CannotGroupEmptyStructure
202
     *
203
     * @return Map<D, Map<T, S>>
204
     */
205 2
    public function groupBy(callable $discriminator): Map
206
    {
207 2
        if ($this->empty()) {
208 1
            throw new CannotGroupEmptyStructure;
209
        }
210
211 1
        $groups = null;
212
213 1
        foreach ($this->values as $k) {
214
            /** @var T $key */
215 1
            $key = $k;
216
            /** @var S $v */
217 1
            $v = $this->values[$k];
218
219 1
            $discriminant = $discriminator($key, $v);
220
221 1
            if ($groups === null) {
222
                /** @var Map<D, Map<T, S>> */
223 1
                $groups = Map::of(
224 1
                    Type::determine($discriminant),
225 1
                    Map::class,
226
                );
227
            }
228
229 1
            if ($groups->contains($discriminant)) {
230
                /** @var Map<T, S> */
231 1
                $group = $groups->get($discriminant);
232
                /** @var Map<T, S> */
233 1
                $group = ($group)($key, $v);
234
235 1
                $groups = ($groups)($discriminant, $group);
236
            } else {
237
                /** @var Map<T, S> */
238 1
                $group = $this->clearMap()($key, $v);
239
240 1
                $groups = ($groups)($discriminant, $group);
241
            }
242
        }
243
244
        /** @var Map<D, Map<T, S>> */
245 1
        return $groups;
246
    }
247
248
    /**
249
     * @return Set<T>
250
     */
251 7
    public function keys(): Set
252
    {
253 7
        return $this->reduce(
254 7
            Set::of($this->keyType),
255
            static function(Set $keys, $key): Set {
256 7
                return ($keys)($key);
257 7
            }
258
        );
259
    }
260
261
    /**
262
     * @return Sequence<S>
263
     */
264 7
    public function values(): Sequence
265
    {
266 7
        return $this->reduce(
267 7
            Sequence::of($this->valueType),
268
            static function(Sequence $values, $key, $value): Sequence {
269 7
                return ($values)($value);
270 7
            }
271
        );
272
    }
273
274
    /**
275
     * @param callable(T, S): (S|Pair<T, S>) $function
276
     *
277
     * @return self<T, S>
278
     */
279 3
    public function map(callable $function): self
280
    {
281 3
        $map = $this->clear();
282
283 3
        foreach ($this->values as $k) {
284
            /** @var T */
285 3
            $key = $k;
286
            /** @var S */
287 3
            $v = $this->values[$k];
288
289 3
            $return = $function($key, $v);
290
291 3
            if ($return instanceof Pair) {
292 2
                ($this->validateKey)($return->key(), 1);
293
294
                /** @var object */
295 1
                $key = $return->key();
296
                /** @var S */
297 1
                $value = $return->value();
298
            } else {
299 2
                $key = $k;
300 2
                $value = $return;
301
            }
302
303 2
            ($this->validateValue)($value, 2);
304
305 1
            $map->values[$key] = $value;
306
        }
307
308 1
        return $map;
309
    }
310
311
    /**
312
     * @param T $key
313
     *
314
     * @return self<T, S>
315
     */
316 1
    public function remove($key): self
317
    {
318 1
        if (!$this->contains($key)) {
319 1
            return $this;
320
        }
321
322 1
        $map = clone $this;
323 1
        $map->values = clone $this->values;
324
        /** @psalm-suppress MixedArgumentTypeCoercion */
325 1
        $map->values->detach($key);
326 1
        $map->values->rewind();
327
328 1
        return $map;
329
    }
330
331
    /**
332
     * @param Implementation<T, S> $map
333
     *
334
     * @return self<T, S>
335
     */
336 2
    public function merge(Implementation $map): self
337
    {
338
        /** @var self<T, S> */
339 2
        return $map->reduce(
340 2
            $this,
341
            function(self $carry, $key, $value): self {
342 2
                return $carry->put($key, $value);
343 2
            }
344
        );
345
    }
346
347
    /**
348
     * @param callable(T, S): bool $predicate
349
     *
350
     * @return Map<bool, Map<T, S>>
351
     */
352 1
    public function partition(callable $predicate): Map
353
    {
354 1
        $truthy = $this->clearMap();
355 1
        $falsy = $this->clearMap();
356
357 1
        foreach ($this->values as $k) {
358
            /** @var T $key */
359 1
            $key = $k;
360
            /** @var S $v */
361 1
            $v = $this->values[$k];
362
363 1
            $return = $predicate($key, $v);
364
365 1
            if ($return === true) {
366 1
                $truthy = ($truthy)($key, $v);
367
            } else {
368 1
                $falsy = ($falsy)($key, $v);
369
            }
370
        }
371
372
        /**
373
         * @psalm-suppress InvalidScalarArgument
374
         * @psalm-suppress InvalidArgument
375
         */
376 1
        return Map::of('bool', Map::class)
377 1
            (true, $truthy)
378 1
            (false, $falsy);
379
    }
380
381
    /**
382
     * @template R
383
     * @param R $carry
384
     * @param callable(R, T, S): R $reducer
385
     *
386
     * @return R
387
     */
388 8
    public function reduce($carry, callable $reducer)
389
    {
390 8
        foreach ($this->values as $k) {
391
            /** @var T $key */
392 8
            $key = $k;
393
            /** @var S $v */
394 8
            $v = $this->values[$k];
395
396 8
            $carry = $reducer($carry, $key, $v);
397
        }
398
399 8
        return $carry;
400
    }
401
402 3
    public function empty(): bool
403
    {
404 3
        $this->values->rewind();
405
406 3
        return !$this->values->valid();
407
    }
408
409
    /**
410
     * @template ST
411
     *
412
     * @param callable(T, S): \Generator<ST> $mapper
413
     *
414
     * @return Sequence<ST>
415
     */
416 1
    public function toSequenceOf(string $type, callable $mapper): Sequence
417
    {
418
        /** @var Sequence<ST> */
419 1
        $sequence = Sequence::of($type);
420
421 1
        foreach ($this->values as $k) {
422
            /** @var T $key */
423 1
            $key = $k;
424
            /** @var S $v */
425 1
            $v = $this->values[$k];
426
427 1
            foreach ($mapper($key, $v) as $newValue) {
428 1
                $sequence = ($sequence)($newValue);
429
            }
430
        }
431
432 1
        return $sequence;
433
    }
434
435
    /**
436
     * @template ST
437
     *
438
     * @param callable(T, S): \Generator<ST> $mapper
439
     *
440
     * @return Set<ST>
441
     */
442 1
    public function toSetOf(string $type, callable $mapper): Set
443
    {
444
        /** @var Set<ST> */
445 1
        $set = Set::of($type);
446
447 1
        foreach ($this->values as $k) {
448
            /** @var T $key */
449 1
            $key = $k;
450
            /** @var S $v */
451 1
            $v = $this->values[$k];
452
453 1
            foreach ($mapper($key, $v) as $newValue) {
454 1
                $set = ($set)($newValue);
455
            }
456
        }
457
458 1
        return $set;
459
    }
460
461
462
463
    /**
464
     * @template MT
465
     * @template MS
466
     *
467
     * @param null|callable(T, S): \Generator<MT, MS> $mapper
468
     *
469
     * @return Map<MT, MS>
470
     */
471 2
    public function toMapOf(string $key, string $value, callable $mapper = null): Map
472
    {
473
        /** @psalm-suppress MissingParamType */
474 2
        $mapper ??= static fn($k, $v): \Generator => yield $k => $v;
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_FN, expecting T_PAAMAYIM_NEKUDOTAYIM on line 474 at column 27
Loading history...
475
476
        /** @var Map<MT, MS> */
477 2
        $map = Map::of($key, $value);
478
479 2
        foreach ($this->values as $k) {
480
            /** @var T $key */
481 2
            $key = $k;
482
            /** @var S $v */
483 2
            $v = $this->values[$k];
484
485 2
            foreach ($mapper($key, $v) as $newKey => $newValue) {
486 2
                $map = ($map)($newKey, $newValue);
487
            }
488
        }
489
490 2
        return $map;
491
    }
492
493
    /**
494
     * @return Map<T, S>
495
     */
496 2
    private function clearMap(): Map
497
    {
498 2
        return Map::of($this->keyType, $this->valueType);
499
    }
500
}
501