Pluck::__invoke()   C
last analyzed

Complexity

Conditions 15
Paths 1

Size

Total Lines 74
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 15

Importance

Changes 0
Metric Value
cc 15
eloc 29
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 74
ccs 30
cts 30
cp 1
crap 15
rs 5.9166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace loophp\collection\Operation;
6
7
use ArrayAccess;
8
use Closure;
9
use Generator;
10
use loophp\collection\Contract\Collection;
11
use ReflectionClass;
12
13
use function array_key_exists;
14
use function in_array;
15
use function is_array;
16
use function is_object;
17
use function is_scalar;
18
19
/**
20
 * @immutable
21
 *
22
 * @template TKey
23
 * @template T
24
 */
25
final class Pluck extends AbstractOperation
26
{
27
    /**
28
     * @return Closure(T):Closure(T):Closure(iterable<TKey, T>):Generator<int, T|iterable<int, T>, mixed, void>
29
     */
30 12
    public function __invoke(): Closure
31
    {
32 12
        return
33
            /**
34
             * @param T $key
35
             *
36
             * @return Closure(T): Closure(iterable<TKey, T>): Generator<int, T|iterable<int, T>, mixed, void>
37
             */
38 12
            static fn (mixed $key): Closure =>
39
                /**
40
                 * @param T $default
41
                 *
42
                 * @return Closure(iterable<TKey, T>): Generator<int, T|iterable<int, T>, mixed, void>
43
                 */
44 12
                static fn (mixed $default): Closure =>
45
                    /**
46
                     * @param iterable<TKey, T> $iterable
47
                     *
48
                     * @return Generator<int, T|iterable<int, T>, mixed, void>
49
                     */
50 12
                    static function (iterable $iterable) use ($key, $default): Generator {
51 12
                        $pick =
52
                            /**
53
                             * @param iterable<TKey, T> $iterable
54
                             * @param iterable<TKey, T>|T $target
55
                             * @param array<int, string> $key
56
                             * @param T $default
57
                             *
58
                             * @return iterable<int, T>|T
59
                             */
60 12
                            static function (iterable $iterable, mixed $target, array $key, mixed $default = null) use (&$pick) {
61 12
                                while (null !== $segment = array_shift($key)) {
62 12
                                    if ('*' === $segment) {
63 4
                                        if (!is_iterable($target)) {
64 2
                                            return $default;
65
                                        }
66
67
                                        /** @var array<int, T> $result */
68 2
                                        $result = [];
69
70 2
                                        foreach ($target as $item) {
71 2
                                            $result[] = $pick($iterable, $item, $key);
72
                                        }
73
74
                                        /** @var Generator<TKey, T> $collapse */
75 2
                                        $collapse = (new Collapse())()($result);
76
77 2
                                        return in_array('*', $key, true) ? $collapse : $result;
78
                                    }
79
80 12
                                    if (is_array($target) && array_key_exists($segment, $target)) {
81
                                        /** @var T $target */
82 10
                                        $target = $target[$segment];
83 12
                                    } elseif (($target instanceof ArrayAccess) && ($target->offsetExists($segment))) {
84
                                        /** @var T $target */
85 8
                                        $target = $target[$segment];
86 12
                                    } elseif ($target instanceof Collection) {
87
                                        /** @var T $target */
88 12
                                        $target = ((new Get())()($segment)($default)($target))->current();
89 12
                                    } elseif (is_object($target) && property_exists($target, $segment)) {
90
                                        /** @var T $target */
91 8
                                        $target = (new ReflectionClass($target))->getProperty($segment)->getValue($target);
92
                                    } else {
93 4
                                        $target = $default;
94
                                    }
95
                                }
96
97 10
                                return $target;
98 12
                            };
99
100 12
                        $key = is_scalar($key) ? explode('.', trim((string) $key, '.')) : $key;
101
102 12
                        foreach ($iterable as $value) {
103 12
                            yield $pick($iterable, $value, $key, $default);
104
                        }
105 12
                    };
106
    }
107
}
108