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.

Lazy::filter()   A
last analyzed

Complexity

Conditions 3
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 3
eloc 6
c 1
b 0
f 1
nc 1
nop 1
dl 0
loc 10
rs 10
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
    SideEffect,
13
};
14
15
/**
16
 * @template T
17
 * @psalm-immutable
18
 * @psalm-type RegisterCleanup = callable(callable(): void): void
19
 */
20
final class Lazy implements Implementation
21
{
22
    /** @var \Closure(RegisterCleanup): \Generator<int, T> */
23
    private \Closure $values;
24
25
    /**
26
     * @param callable(RegisterCleanup): \Generator<T> $generator
27
     */
28
    public function __construct(callable $generator)
29
    {
30
        /** @var \Closure(RegisterCleanup): \Generator<int, T> */
31
        $this->values = \Closure::fromCallable($generator);
0 ignored issues
show
Bug introduced by
The property values does not seem to exist on Closure.
Loading history...
32
    }
33
34
    /**
35
     * @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...
36
     *
37
     * @return Implementation<T>
38
     */
39
    public function __invoke($element): Implementation
40
    {
41
        $values = $this->values;
42
43
        return new self(
44
            static function(callable $registerCleanup) use ($values, $element): \Generator {
45
                /** @var RegisterCleanup $registerCleanup */
46
                foreach ($values($registerCleanup) as $value) {
47
                    yield $value;
48
                }
49
50
                yield $element;
51
            },
52
        );
53
    }
54
55
    public function size(): int
56
    {
57
        $size = 0;
58
59
        foreach ($this->iterator() as $_) {
60
            ++$size;
61
        }
62
63
        return $size;
64
    }
65
66
    public function count(): int
67
    {
68
        return $this->size();
69
    }
70
71
    /**
72
     * @return \Iterator<int, T>
73
     */
74
    public function iterator(): \Iterator
75
    {
76
        // when accessing the iterator from the outside we cannot know when it
77
        // will be stopped being iterated over so we can't have a way to notify
78
        // the generator to cleanup its ressources, so we pass an empty function
79
        // that does nothing
80
        return ($this->values)(self::bypassCleanup());
81
    }
82
83
    /**
84
     * @return Maybe<T>
85
     */
86
    public function get(int $index): Maybe
87
    {
88
        $iteration = 0;
89
        $cleanup = self::noCleanup();
90
        $generator = ($this->values)(static function(callable $userDefinedCleanup) use (&$cleanup) {
91
            $cleanup = $userDefinedCleanup;
92
        });
93
94
        foreach ($generator as $value) {
95
            if ($index === $iteration) {
96
                /** @psalm-suppress MixedFunctionCall Due to the reference in the closure above */
97
                $cleanup();
98
99
                return Maybe::just($value);
100
            }
101
102
            ++$iteration;
103
        }
104
105
        /** @var Maybe<T> */
106
        return Maybe::nothing();
107
    }
108
109
    /**
110
     * @param Implementation<T> $sequence
111
     *
112
     * @return Implementation<T>
113
     */
114
    public function diff(Implementation $sequence): Implementation
115
    {
116
        return $this->filter(static function(mixed $value) use ($sequence): bool {
117
            /** @var T $value */
118
            return !$sequence->contains($value);
119
        });
120
    }
121
122
    /**
123
     * @return Implementation<T>
124
     */
125
    public function distinct(): Implementation
126
    {
127
        $values = $this->values;
128
129
        return new self(
130
            static function(callable $registerCleanup) use ($values): \Generator {
131
                /** @var list<T> */
132
                $uniques = [];
133
134
                /** @var RegisterCleanup $registerCleanup */
135
                foreach ($values($registerCleanup) as $value) {
136
                    if (!\in_array($value, $uniques, true)) {
137
                        $uniques[] = $value;
138
139
                        yield $value;
140
                    }
141
                }
142
            },
143
        );
144
    }
145
146
    /**
147
     * @return Implementation<T>
148
     */
149
    public function drop(int $size): Implementation
150
    {
151
        $values = $this->values;
152
153
        return new self(
154
            static function(callable $registerCleanup) use ($values, $size): \Generator {
155
                /** @var RegisterCleanup $registerCleanup */
156
                $dropped = 0;
157
158
                foreach ($values($registerCleanup) as $value) {
159
                    if ($dropped < $size) {
160
                        ++$dropped;
161
162
                        continue;
163
                    }
164
165
                    yield $value;
166
                }
167
            },
168
        );
169
    }
170
171
    /**
172
     * @return Implementation<T>
173
     */
174
    public function dropEnd(int $size): Implementation
175
    {
176
        // this cannot be optimised as the whole generator needs to be loaded
177
        // in order to know the elements to drop
178
        return $this->load()->dropEnd($size);
179
    }
180
181
    /**
182
     * @param Implementation<T> $sequence
183
     */
184
    public function equals(Implementation $sequence): bool
185
    {
186
        return $this->load()->equals($sequence);
187
    }
188
189
    /**
190
     * @param callable(T): bool $predicate
191
     *
192
     * @return Implementation<T>
193
     */
194
    public function filter(callable $predicate): Implementation
195
    {
196
        $values = $this->values;
197
198
        return new self(
199
            static function(callable $registerCleanup) use ($values, $predicate): \Generator {
200
                /** @var RegisterCleanup $registerCleanup */
201
                foreach ($values($registerCleanup) as $value) {
202
                    if ($predicate($value)) {
203
                        yield $value;
204
                    }
205
                }
206
            },
207
        );
208
    }
209
210
    /**
211
     * @param callable(T): void $function
212
     */
213
    public function foreach(callable $function): SideEffect
214
    {
215
        foreach ($this->iterator() as $value) {
216
            $function($value);
217
        }
218
219
        return new SideEffect;
220
    }
221
222
    /**
223
     * @template D
224
     * @param callable(T): D $discriminator
225
     *
226
     * @return Map<D, Sequence<T>>
227
     */
228
    public function groupBy(callable $discriminator): Map
229
    {
230
        /** @var Map<D, Sequence<T>> */
231
        return $this->load()->groupBy($discriminator);
232
    }
233
234
    /**
235
     * @return Maybe<T>
236
     */
237
    public function first(): Maybe
238
    {
239
        return $this->get(0);
240
    }
241
242
    /**
243
     * @return Maybe<T>
244
     */
245
    public function last(): Maybe
246
    {
247
        foreach ($this->iterator() as $value) {
248
        }
249
250
        if (!isset($value)) {
251
            /** @var Maybe<T> */
252
            return Maybe::nothing();
253
        }
254
255
        return Maybe::just($value);
256
    }
257
258
    /**
259
     * @param T $element
260
     */
261
    public function contains($element): bool
262
    {
263
        $cleanup = self::noCleanup();
264
        $generator = ($this->values)(static function(callable $userDefinedCleanup) use (&$cleanup) {
265
            $cleanup = $userDefinedCleanup;
266
        });
267
268
        foreach ($generator as $value) {
269
            if ($value === $element) {
270
                /** @psalm-suppress MixedFunctionCall Due to the reference in the closure above */
271
                $cleanup();
272
273
                return true;
274
            }
275
        }
276
277
        return false;
278
    }
279
280
    /**
281
     * @param T $element
282
     *
283
     * @return Maybe<int>
284
     */
285
    public function indexOf($element): Maybe
286
    {
287
        $index = 0;
288
        $cleanup = self::noCleanup();
289
        $generator = ($this->values)(static function(callable $userDefinedCleanup) use (&$cleanup) {
290
            $cleanup = $userDefinedCleanup;
291
        });
292
293
        foreach ($generator as $value) {
294
            if ($value === $element) {
295
                /** @psalm-suppress MixedFunctionCall Due to the reference in the closure above */
296
                $cleanup();
297
298
                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

298
                return Maybe::just(/** @scrutinizer ignore-type */ $index);
Loading history...
299
            }
300
301
            ++$index;
302
        }
303
304
        /** @var Maybe<int> */
305
        return Maybe::nothing();
306
    }
307
308
    /**
309
     * Return the list of indices
310
     *
311
     * @return Implementation<int>
312
     */
313
    public function indices(): Implementation
314
    {
315
        $values = $this->values;
316
317
        /** @var Implementation<int> */
318
        return new self(
319
            static function(callable $registerCleanup) use ($values): \Generator {
320
                /** @var RegisterCleanup $registerCleanup */
321
                $index = 0;
322
323
                foreach ($values($registerCleanup) as $_) {
324
                    yield $index++;
325
                }
326
            },
327
        );
328
    }
329
330
    /**
331
     * @template S
332
     *
333
     * @param callable(T): S $function
334
     *
335
     * @return Implementation<S>
336
     */
337
    public function map(callable $function): Implementation
338
    {
339
        $values = $this->values;
340
341
        return new self(
342
            static function(callable $registerCleanup) use ($values, $function): \Generator {
343
                /** @var RegisterCleanup $registerCleanup */
344
                foreach ($values($registerCleanup) as $value) {
345
                    yield $function($value);
346
                }
347
            },
348
        );
349
    }
350
351
    /**
352
     * @template S
353
     *
354
     * @param callable(T): Sequence<S> $map
355
     * @param callable(Sequence<S>): Implementation<S> $exfiltrate
356
     *
357
     * @return Sequence<S>
358
     */
359
    public function flatMap(callable $map, callable $exfiltrate): Sequence
360
    {
361
        $values = $this->values;
362
363
        return Sequence::lazy(
364
            static function(callable $registerCleanup) use ($values, $map, $exfiltrate): \Generator {
365
                foreach ($values($registerCleanup) as $value) {
366
                    $generator = self::open(
367
                        $exfiltrate($map($value)),
368
                        $registerCleanup,
369
                    );
370
371
                    foreach ($generator as $inner) {
372
                        yield $inner;
373
                    }
374
                }
375
            },
376
        );
377
    }
378
379
    /**
380
     * @param T $element
381
     *
382
     * @return Implementation<T>
383
     */
384
    public function pad(int $size, $element): Implementation
385
    {
386
        $values = $this->values;
387
388
        return new self(
389
            static function(callable $registerCleanup) use ($values, $size, $element): \Generator {
390
                /** @var RegisterCleanup $registerCleanup */
391
                foreach ($values($registerCleanup) as $value) {
392
                    yield $value;
393
                    --$size;
394
                }
395
396
                while ($size > 0) {
397
                    yield $element;
398
                    --$size;
399
                }
400
            },
401
        );
402
    }
403
404
    /**
405
     * @param callable(T): bool $predicate
406
     *
407
     * @return Map<bool, Sequence<T>>
408
     */
409
    public function partition(callable $predicate): Map
410
    {
411
        /** @var Map<bool, Sequence<T>> */
412
        return $this->load()->partition($predicate);
413
    }
414
415
    /**
416
     * @return Implementation<T>
417
     */
418
    public function slice(int $from, int $until): Implementation
419
    {
420
        $values = $this->values;
421
422
        return new self(
423
            static function(callable $registerCleanup) use ($values, $from, $until): \Generator {
424
                $index = 0;
425
426
                /** @var RegisterCleanup $registerCleanup */
427
                foreach ($values($registerCleanup) as $value) {
428
                    if ($index >= $from && $index < $until) {
429
                        yield $value;
430
                    }
431
432
                    ++$index;
433
                }
434
            },
435
        );
436
    }
437
438
    /**
439
     * @return Implementation<T>
440
     */
441
    public function take(int $size): Implementation
442
    {
443
        $values = $this->values;
444
445
        return new self(
446
            static function(callable $registerCleanup) use ($values, $size): \Generator {
447
                $taken = 0;
448
                // We intercept the registering of the cleanup function here
449
                // because this generator can be stopped when we reach the number
450
                // of elements to take so we have to cleanup here. In this case
451
                // the parent sequence may not need to cleanup as it could
452
                // iterate over the whole generator but this inner one still
453
                // needs to free resources correctly
454
                $cleanup = self::noCleanup();
455
                $middleware = static function(callable $userDefinedCleanup) use (&$cleanup, $registerCleanup): void {
456
                    $cleanup = $userDefinedCleanup;
457
                    $registerCleanup($userDefinedCleanup);
458
                };
459
460
                foreach ($values($middleware) as $value) {
461
                    if ($taken >= $size) {
462
                        /** @psalm-suppress MixedFunctionCall Due to the reference in the closure above */
463
                        $cleanup();
464
465
                        return;
466
                    }
467
468
                    yield $value;
469
                    ++$taken;
470
                }
471
            },
472
        );
473
    }
474
475
    /**
476
     * @return Implementation<T>
477
     */
478
    public function takeEnd(int $size): Implementation
479
    {
480
        // this cannot be optimised as the whole generator needs to be loaded
481
        // in order to know the elements to drop
482
        return $this->load()->takeEnd($size);
483
    }
484
485
    /**
486
     * @param Implementation<T> $sequence
487
     *
488
     * @return Implementation<T>
489
     */
490
    public function append(Implementation $sequence): Implementation
491
    {
492
        $values = $this->values;
493
494
        return new self(
495
            static function(callable $registerCleanup) use ($values, $sequence): \Generator {
496
                /** @var RegisterCleanup $registerCleanup */
497
                foreach ($values($registerCleanup) as $value) {
498
                    yield $value;
499
                }
500
501
                foreach (self::open($sequence, $registerCleanup) as $value) {
502
                    yield $value;
503
                }
504
            },
505
        );
506
    }
507
508
    /**
509
     * @param Implementation<T> $sequence
510
     *
511
     * @return Implementation<T>
512
     */
513
    public function intersect(Implementation $sequence): Implementation
514
    {
515
        return $this->filter(static function(mixed $value) use ($sequence): bool {
516
            /** @var T $value */
517
            return $sequence->contains($value);
518
        });
519
    }
520
521
    /**
522
     * @param callable(T, T): int $function
523
     *
524
     * @return Implementation<T>
525
     */
526
    public function sort(callable $function): Implementation
527
    {
528
        $values = $this->values;
529
530
        return new self(
531
            static function() use ($values, $function): \Generator {
532
                // bypass the registering of cleanup function as we iterate over
533
                // the whole generator
534
                $values = \iterator_to_array($values(self::bypassCleanup()));
535
                \usort($values, $function);
536
537
                foreach ($values as $value) {
538
                    yield $value;
539
                }
540
            },
541
        );
542
    }
543
544
    /**
545
     * @template R
546
     * @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...
547
     * @param callable(R, T): R $reducer
548
     *
549
     * @return R
550
     */
551
    public function reduce($carry, callable $reducer)
552
    {
553
        foreach ($this->iterator() as $value) {
554
            $carry = $reducer($carry, $value);
555
        }
556
557
        return $carry;
558
    }
559
560
    /**
561
     * @return Implementation<T>
562
     */
563
    public function clear(): Implementation
564
    {
565
        return new Primitive;
566
    }
567
568
    /**
569
     * @return Implementation<T>
570
     */
571
    public function reverse(): Implementation
572
    {
573
        $values = $this->values;
574
575
        return new self(
576
            static function() use ($values): \Generator {
577
                // bypass the registering of cleanup function as we iterate over
578
                // the whole generator
579
                $values = \iterator_to_array($values(self::bypassCleanup()));
580
581
                yield from \array_reverse($values);
582
            },
583
        );
584
    }
585
586
    public function empty(): bool
587
    {
588
        /** @psalm-suppress ImpureMethodCall */
589
        return !$this->iterator()->valid();
590
    }
591
592
    /**
593
     * @return Sequence<T>
594
     */
595
    public function toSequence(): Sequence
596
    {
597
        $values = $this->values;
598
599
        return Sequence::lazy(
600
            static function(callable $registerCleanup) use ($values): \Generator {
601
                foreach ($values($registerCleanup) as $value) {
602
                    yield $value;
603
                }
604
            },
605
        );
606
    }
607
608
    /**
609
     * @return Set<T>
610
     */
611
    public function toSet(): Set
612
    {
613
        $values = $this->values;
614
615
        return Set::lazy(
616
            static function(callable $registerCleanup) use ($values): \Generator {
617
                foreach ($values($registerCleanup) as $value) {
618
                    yield $value;
619
                }
620
            },
621
        );
622
    }
623
624
    public function find(callable $predicate): Maybe
625
    {
626
        $cleanup = self::noCleanup();
627
        $generator = ($this->values)(static function(callable $userDefinedCleanup) use (&$cleanup) {
628
            $cleanup = $userDefinedCleanup;
629
        });
630
631
        foreach ($generator as $value) {
632
            if ($predicate($value) === true) {
633
                /** @psalm-suppress MixedFunctionCall Due to the reference in the closure above */
634
                $cleanup();
635
636
                return Maybe::just($value);
637
            }
638
        }
639
640
        /** @var Maybe<T> */
641
        return Maybe::nothing();
642
    }
643
644
    public function match(callable $wrap, callable $match, callable $empty)
645
    {
646
        $generator = ($this->values)(self::bypassCleanup());
647
648
        return (new Defer($generator))->match($wrap, $match, $empty);
649
    }
650
651
    /**
652
     * @return Implementation<T>
653
     */
654
    private function load(): Implementation
655
    {
656
        /** @psalm-suppress ImpureFunctionCall */
657
        return new Primitive(\array_values(\iterator_to_array($this->iterator())));
658
    }
659
660
    /**
661
     * @param RegisterCleanup $registerCleanup
0 ignored issues
show
Bug introduced by
The type Innmind\Immutable\Sequence\RegisterCleanup 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...
662
     *
663
     * @return \Iterator<int, T>
664
     */
665
    private static function open(
666
        Implementation $sequence,
667
        callable $registerCleanup,
668
    ): \Iterator {
669
        if ($sequence instanceof self) {
670
            return ($sequence->values)($registerCleanup);
671
        }
672
673
        return $sequence->iterator();
674
    }
675
676
    /**
677
     * @psalm-pure
678
     *
679
     * @return callable(): void
680
     */
681
    private static function noCleanup(): callable
682
    {
683
        return static function(): void {
684
            // nothing to do
685
        };
686
    }
687
688
    /**
689
     * @psalm-pure
690
     *
691
     * @return RegisterCleanup
692
     */
693
    private static function bypassCleanup(): callable
694
    {
695
        return static function(): void {
0 ignored issues
show
Bug Best Practice introduced by
The expression return function(...) { /* ... */ } returns the type callable which is incompatible with the documented return type Innmind\Immutable\Sequence\RegisterCleanup.
Loading history...
696
            // nothing to do
697
        };
698
    }
699
}
700