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.

Defer   F
last analyzed

Complexity

Total Complexity 75

Size/Duplication

Total Lines 568
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 1
Metric Value
eloc 165
c 3
b 0
f 1
dl 0
loc 568
rs 2.4
ccs 185
cts 185
cp 1
wmc 75

38 Methods

Rating   Name   Duplication   Size   Complexity  
A takeEnd() 0 5 1
A load() 0 4 1
A reduce() 0 7 2
A intersect() 0 5 1
A indices() 0 11 2
A sort() 0 15 2
A __invoke() 0 11 2
A toSet() 0 9 2
A first() 0 8 2
A match() 0 7 1
A size() 0 3 1
A empty() 0 7 1
A flatMap() 0 15 3
A pad() 0 15 3
A get() 0 14 3
A filter() 0 11 3
A map() 0 9 2
A indexOf() 0 14 3
A slice() 0 14 4
A drop() 0 17 3
A dropEnd() 0 5 1
A iterator() 0 3 1
A partition() 0 4 1
A distinct() 0 16 3
A contains() 0 9 3
A append() 0 14 3
A toSequence() 0 9 2
A find() 0 10 3
A foreach() 0 7 2
A clear() 0 3 1
A equals() 0 3 1
A take() 0 15 3
A reverse() 0 8 1
A count() 0 3 1
A last() 0 11 3
A groupBy() 0 4 1
A __construct() 0 9 2
A diff() 0 5 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
declare(strict_types = 1);
3
4
namespace Innmind\Immutable\Sequence;
5
6
use Innmind\Immutable\{
7
    Map,
8
    Sequence,
9
    Str,
10
    Set,
11
    Maybe,
12
    Accumulate,
13
    SideEffect,
14
};
15
16
/**
17
 * @template T
18
 * @psalm-immutable
19
 */
20
final class Defer implements Implementation
21
{
22
    /** @var \Iterator<int, T> */
23
    private \Iterator $values;
24
25
    public function __construct(\Generator $generator)
26
    {
27
        /** @var \Iterator<int, T> */
28
        $this->values = new Accumulate((static function(\Generator $generator): \Generator {
0 ignored issues
show
Bug introduced by
Accessing values on the interface Iterator suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
29
            /** @var T $value */
30 70
            foreach ($generator as $value) {
31
                yield $value;
32 70
            }
33 70
        })($generator));
34 70
    }
35 70
36
    /**
37 3
     * @param T $element
0 ignored issues
show
Bug introduced by
The type Innmind\Immutable\Sequence\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...
38
     *
39 3
     * @return Implementation<T>
40
     */
41
    public function __invoke($element): Implementation
42 4
    {
43
        return new self(
44 4
            (static function(\Iterator $values, mixed $element): \Generator {
45
                /** @var T $value */
46
                foreach ($values as $value) {
47 1
                    yield $value;
48
                }
49 1
50
                yield $element;
51
            })($this->values, $element),
52
        );
53
    }
54
55 38
    public function size(): int
56
    {
57 38
        return $this->load()->size();
58
    }
59
60
    public function count(): int
61
    {
62
        return $this->size();
63
    }
64
65 2
    /**
66
     * @return \Iterator<int, T>
67 2
     */
68
    public function iterator(): \Iterator
69
    {
70 2
        return $this->values;
71 1
    }
72
73 1
    /**
74
     * @return Maybe<T>
75
     */
76 1
    public function get(int $index): Maybe
77
    {
78
        $iteration = 0;
79 1
80
        foreach ($this->values as $value) {
81
            if ($index === $iteration) {
82
                return Maybe::just($value);
83
            }
84
85
            ++$iteration;
86
        }
87 2
88
        /** @var Maybe<T> */
89
        return Maybe::nothing();
90
    }
91 2
92 2
    /**
93
     * @param Implementation<T> $sequence
94
     *
95
     * @return Implementation<T>
96
     */
97
    public function diff(Implementation $sequence): Implementation
98 27
    {
99
        return $this->filter(static function(mixed $value) use ($sequence): bool {
100
            /** @var T $value */
101 27
            return !$sequence->contains($value);
102 27
        });
103
    }
104
105 25
    /**
106
     * @return Implementation<T>
107
     */
108 25
    public function distinct(): Implementation
109 25
    {
110 25
        return new self(
111
            (static function(\Iterator $values): \Generator {
112 25
                /** @var list<T> */
113
                $uniques = [];
114
115 27
                /** @var T $value */
116
                foreach ($values as $value) {
117
                    if (!\in_array($value, $uniques, true)) {
118
                        $uniques[] = $value;
119
120
                        yield $value;
121
                    }
122 1
                }
123
            })($this->values),
124
        );
125 1
    }
126 1
127
    /**
128 1
     * @return Implementation<T>
129
     */
130
    public function drop(int $size): Implementation
131 1
    {
132 1
        return new self(
133 1
            (static function(\Iterator $values, int $toDrop): \Generator {
134 1
                $dropped = 0;
135
136
                /** @var T $value */
137 1
                foreach ($values as $value) {
138
                    if ($dropped < $toDrop) {
139 1
                        ++$dropped;
140
141
                        continue;
142
                    }
143
144
                    yield $value;
145
                }
146 1
            })($this->values, $size),
147
        );
148
    }
149
150 1
    /**
151
     * @return Implementation<T>
152
     */
153
    public function dropEnd(int $size): Implementation
154
    {
155
        // this cannot be optimised as the whole generator needs to be loaded
156 1
        // in order to know the elements to drop
157
        return $this->load()->dropEnd($size);
158 1
    }
159
160
    /**
161
     * @param Implementation<T> $sequence
162
     */
163
    public function equals(Implementation $sequence): bool
164
    {
165
        return $this->load()->equals($sequence);
166 7
    }
167
168
    /**
169 7
     * @param callable(T): bool $predicate
170 7
     *
171
     * @return Implementation<T>
172
     */
173 7
    public function filter(callable $predicate): Implementation
174 7
    {
175 7
        return new self(
176
            (static function(\Iterator $values, callable $predicate): \Generator {
177
                /** @var T $value */
178 7
                foreach ($values as $value) {
179
                    if ($predicate($value)) {
180
                        yield $value;
181
                    }
182
                }
183
            })($this->values, $predicate),
184
        );
185 2
    }
186
187 2
    /**
188 2
     * @param callable(T): void $function
189
     */
190 2
    public function foreach(callable $function): SideEffect
191
    {
192
        foreach ($this->values as $value) {
193
            $function($value);
194
        }
195
196
        return new SideEffect;
197
    }
198
199
    /**
200 3
     * @template D
201
     * @param callable(T): D $discriminator
202 3
     *
203
     * @return Map<D, Sequence<T>>
204
     */
205
    public function groupBy(callable $discriminator): Map
206
    {
207
        /** @var Map<D, Sequence<T>> */
208 2
        return $this->load()->groupBy($discriminator);
209
    }
210 2
211 1
    /**
212
     * @return Maybe<T>
213
     */
214 1
    public function first(): Maybe
215
    {
216
        foreach ($this->values as $value) {
217
            return Maybe::just($value);
218
        }
219
220 2
        /** @var Maybe<T> */
221
        return Maybe::nothing();
222 2
    }
223
224
    /**
225 2
     * @return Maybe<T>
226 1
     */
227
    public function last(): Maybe
228
    {
229
        foreach ($this->values as $value) {
230 1
        }
231
232
        if (!isset($value)) {
233
            /** @var Maybe<T> */
234
            return Maybe::nothing();
235
        }
236 8
237
        return Maybe::just($value);
238 8
    }
239 8
240 8
    /**
241
     * @param T $element
242
     */
243
    public function contains($element): bool
244 7
    {
245
        foreach ($this->values as $value) {
246
            if ($value === $element) {
247
                return true;
248
            }
249
        }
250
251
        return false;
252 3
    }
253
254 3
    /**
255
     * @param T $element
256 3
     *
257 2
     * @return Maybe<int>
258 2
     */
259
    public function indexOf($element): Maybe
260
    {
261 2
        $index = 0;
262
263
        foreach ($this->values as $value) {
264 1
            if ($value === $element) {
265
                return Maybe::just($index);
0 ignored issues
show
Bug introduced by
$index of type integer is incompatible with the type Innmind\Immutable\V expected by parameter $value of Innmind\Immutable\Maybe::just(). ( Ignorable by Annotation )

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

265
                return Maybe::just(/** @scrutinizer ignore-type */ $index);
Loading history...
266
            }
267
268
            ++$index;
269
        }
270
271
        /** @var Maybe<int> */
272 2
        return Maybe::nothing();
273
    }
274
275
    /**
276
     * Return the list of indices
277
     *
278 2
     * @return Implementation<int>
279 2
     */
280
    public function indices(): Implementation
281 2
    {
282
        /** @var Implementation<int> */
283 2
        return new self(
284 1
            (static function(\Iterator $values): \Generator {
285
                $index = 0;
286 2
287
                foreach ($values as $_) {
288
                    yield $index++;
289
                }
290
            })($this->values),
291
        );
292
    }
293
294
    /**
295 4
     * @template S
296
     *
297
     * @param callable(T): S $function
298 4
     *
299 4
     * @return Implementation<S>
300
     */
301
    public function map(callable $function): Implementation
302 4
    {
303
        return new self(
304 4
            (static function(\Iterator $values, callable $map): \Generator {
305 3
                /** @var T $value */
306
                foreach ($values as $value) {
307 2
                    yield $map($value);
308
                }
309 4
            })($this->values, $function),
310
        );
311
    }
312
313
    /**
314
     * @template S
315
     *
316
     * @param callable(T): Sequence<S> $map
317
     * @param callable(Sequence<S>): Implementation<S> $exfiltrate
318 1
     *
319
     * @return Sequence<S>
320
     */
321 1
    public function flatMap(callable $map, callable $exfiltrate): Sequence
322 1
    {
323
        return Sequence::defer(
324
            (static function(\Iterator $values, callable $map, callable $exfiltrate): \Generator {
325 1
                /** @var T $value */
326 1
                foreach ($values as $value) {
327 1
                    /**
328
                     * @var callable(T): Sequence<S> $map
329
                     * @var callable(Sequence<S>): Implementation<S> $exfiltrate
330 1
                     */
331 1
                    foreach ($exfiltrate($map($value))->iterator() as $inner) {
332 1
                        yield $inner;
333
                    }
334 1
                }
335
            })($this->values, $map, $exfiltrate),
336
        );
337
    }
338
339
    /**
340
     * @param T $element
341
     *
342
     * @return Implementation<T>
343 2
     */
344
    public function pad(int $size, $element): Implementation
345 2
    {
346
        return new self(
347
            (static function(\Iterator $values, int $toPad, mixed $element): \Generator {
348
                /** @var T $value */
349
                foreach ($values as $value) {
350
                    yield $value;
351 2
                    --$toPad;
352
                }
353
354 2
                while ($toPad > 0) {
355 2
                    yield $element;
356
                    --$toPad;
357 2
                }
358
            })($this->values, $size, $element),
359 2
        );
360 2
    }
361 2
362
    /**
363
     * @param callable(T): bool $predicate
364 2
     *
365
     * @return Map<bool, Sequence<T>>
366 2
     */
367
    public function partition(callable $predicate): Map
368
    {
369
        /** @var Map<bool, Sequence<T>> */
370
        return $this->load()->partition($predicate);
371
    }
372
373
    /**
374
     * @return Implementation<T>
375 1
     */
376
    public function slice(int $from, int $until): Implementation
377 1
    {
378
        return new self(
379
            (static function(\Iterator $values, int $from, int $until): \Generator {
380
                $index = 0;
381
                /** @var T $value */
382
                foreach ($values as $value) {
383 1
                    if ($index >= $from && $index < $until) {
384
                        yield $value;
385
                    }
386 1
387 1
                    ++$index;
388
                }
389 1
            })($this->values, $from, $until),
390
        );
391 1
    }
392 1
393 1
    /**
394
     * @return Implementation<T>
395
     */
396 1
    public function take(int $size): Implementation
397
    {
398 1
        return new self(
399
            (static function(\Iterator $values, int $size): \Generator {
400
                $taken = 0;
401
                /** @var T $value */
402
                foreach ($values as $value) {
403
                    if ($taken >= $size) {
404
                        return;
405 1
                    }
406
407
                    yield $value;
408
                    ++$taken;
409 1
                }
410
            })($this->values, $size),
411
        );
412
    }
413
414
    /**
415
     * @return Implementation<T>
416
     */
417 2
    public function takeEnd(int $size): Implementation
418
    {
419
        // this cannot be optimised as the whole generator needs to be loaded
420 2
        // in order to know the elements to drop
421 2
        return $this->load()->takeEnd($size);
422
    }
423
424 2
    /**
425 2
     * @param Implementation<T> $sequence
426
     *
427
     * @return Implementation<T>
428
     */
429 2
    public function append(Implementation $sequence): Implementation
430 2
    {
431
        return new self(
432 2
            (static function(\Iterator $values, Implementation $sequence): \Generator {
433
                /** @var T $value */
434
                foreach ($values as $value) {
435
                    yield $value;
436
                }
437
438
                /** @var T $value */
439
                foreach ($sequence->iterator() as $value) {
440
                    yield $value;
441 3
                }
442
            })($this->values, $sequence),
443
        );
444
    }
445 3
446 3
    /**
447
     * @param Implementation<T> $sequence
448
     *
449
     * @return Implementation<T>
450
     */
451
    public function intersect(Implementation $sequence): Implementation
452
    {
453
        return $this->filter(static function(mixed $value) use ($sequence): bool {
454 2
            /** @var T $value */
455
            return $sequence->contains($value);
456
        });
457 2
    }
458 2
459
    /**
460
     * @param callable(T, T): int $function
461 2
     *
462 2
     * @return Implementation<T>
463
     */
464
    public function sort(callable $function): Implementation
465 2
    {
466 2
        return new self(
467
            (static function(\Iterator $values, callable $function): \Generator {
468
                /** @var callable(T, T): int $sorter */
469
                $sorter = $function;
470
471
                /** @var list<T> */
472
                $values = \iterator_to_array($values);
473
                \usort($values, $sorter);
474
475 2
                foreach ($values as $value) {
476
                    yield $value;
477
                }
478 2
            })($this->values, $function),
479 2
        );
480
    }
481 2
482 2
    /**
483
     * @template R
484
     * @param R $carry
0 ignored issues
show
Bug introduced by
The type Innmind\Immutable\Sequence\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...
485 2
     * @param callable(R, T): R $reducer
486 2
     *
487
     * @return R
488 2
     */
489
    public function reduce($carry, callable $reducer)
490
    {
491
        foreach ($this->values as $value) {
492
            $carry = $reducer($carry, $value);
493
        }
494
495
        return $carry;
496
    }
497
498
    /**
499 9
     * @return Implementation<T>
500
     */
501 9
    public function clear(): Implementation
502 9
    {
503
        return new Primitive;
504
    }
505 9
506
    /**
507
     * @return Implementation<T>
508
     */
509
    public function reverse(): Implementation
510
    {
511 1
        return new self(
512
            (static function(\Iterator $values): \Generator {
513 1
                $values = \iterator_to_array($values);
514
515
                yield from \array_reverse($values);
516
            })($this->values),
517
        );
518
    }
519 1
520
    public function empty(): bool
521
    {
522 1
        /** @psalm-suppress ImpureMethodCall */
523 1
        $this->values->rewind();
524
525 1
        /** @psalm-suppress ImpureMethodCall */
526
        return !$this->values->valid();
527 1
    }
528 1
529
    /**
530
     * @return Sequence<T>
531
     */
532 2
    public function toSequence(): Sequence
533
    {
534 2
        return Sequence::defer(
535
            (static function(\Iterator $values): \Generator {
536 2
                /** @var T $value */
537
                foreach ($values as $value) {
538
                    yield $value;
539
                }
540
            })($this->values),
541
        );
542
    }
543
544
    /**
545
     * @return Set<T>
546 3
     */
547
    public function toSet(): Set
548
    {
549 3
        return Set::defer(
550
            (static function(\Iterator $values): \Generator {
551
                /** @var T $value */
552 3
                foreach ($values as $value) {
553 3
                    yield $value;
554
                }
555
            })($this->values),
556 3
        );
557
    }
558 3
559 3
    public function find(callable $predicate): Maybe
560
    {
561
        foreach ($this->values as $value) {
562 3
            if ($predicate($value) === true) {
563
                return Maybe::just($value);
564
            }
565
        }
566
567
        /** @var Maybe<T> */
568
        return Maybe::nothing();
569
    }
570
571
    public function match(callable $wrap, callable $match, callable $empty)
572
    {
573 2
        return $this
574
            ->first()
575
            ->match(
576 2
                fn($first) => $match($first, $wrap($this->drop(1))),
577
                $empty,
578
            );
579 2
    }
580 2
581
    /**
582
     * @return Implementation<T>
583 2
     */
584
    private function load(): Implementation
585 2
    {
586 2
        /** @psalm-suppress ImpureFunctionCall */
587
        return new Primitive(\array_values(\iterator_to_array($this->values)));
588
    }
589
}
590