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 — master ( 937d02...dd9c6a )
by SignpostMarv
02:33
created

StaticMethodCollector::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 3
dl 0
loc 23
ccs 9
cts 9
cp 1
crap 2
rs 9.0856
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
/**
5
* @author SignpostMarv
6
*/
7
8
namespace SignpostMarv\DaftInterfaceCollector;
9
10
use Closure;
11
use Generator;
12
use ReflectionClass;
13
use ReflectionMethod;
14
use ReflectionNamedType;
15
use ReflectionType;
16
use Traversable;
17
18
class StaticMethodCollector
19
{
20
    /**
21
    * @var array<string, array<string, string[]>>
22
    */
23
    private $staticMethods = [];
24
25
    /**
26
    * @var string[]
27
    */
28
    private $interfaces = [];
29
30
    /**
31
    * @var array<int, string>
32
    */
33
    private $processedSources = [];
34
35
    /**
36
    * @var string[]
37
    */
38
    private $alreadyYielded = [];
39
40
    /**
41
    * @var bool
42
    */
43
    private $autoReset;
44
45 4
    public function __construct(array $staticMethods, array $interfaces, bool $autoReset = true)
46
    {
47 4
        $filteredMethods = [];
48
49 4
        foreach ($this->FilterArrayOfInterfaceOffsets($staticMethods) as $interface => $methods) {
50 4
            $filteredMethods[$interface] = $this->FilterMethods($interface, $methods);
51
        }
52
53
        /**
54
        * @var array<string, array<string, string[]>> $filteredMethods
55
        */
56 4
        $filteredMethods = $this->FilterNonZeroArray($filteredMethods);
57
58 4
        $this->staticMethods = $filteredMethods;
59
60
        /**
61
        * @var string[] $filteredInterfaces
62
        */
63 4
        $filteredInterfaces = $this->FilterArrayOfInterfacesOrClasses($interfaces);
64
65 4
        $this->interfaces = $filteredInterfaces;
66
67 4
        $this->autoReset = $autoReset;
68 4
    }
69
70 4
    public function Collect(string ...$implementations) : Generator
71
    {
72 4
        if ($this->autoReset) {
73 2
            $this->processedSources = [];
74 2
            $this->alreadyYielded = [];
75
        }
76
77 4
        yield from $this->CollectInterfaces(...$implementations);
78 4
    }
79
80 4
    protected function CollectInterfaces(string ...$implementations) : Generator
81
    {
82
        foreach (
83 4
            array_filter(
84 4
                $implementations,
85
                function (string $implementation) : bool {
86
                    return
87 4
                        class_exists($implementation) &&
88 4
                        ! in_array($implementation, $this->processedSources, true);
89 4
                }
90
            ) as $implementation
91
        ) {
92 4
            $this->processedSources[] = $implementation;
93
94 4
            foreach ($this->interfaces as $interface) {
95
                if (
96 4
                    ! in_array($implementation, $this->alreadyYielded, true) &&
97 4
                    is_a($implementation, $interface, true)
98
                ) {
99
                    yield $implementation;
100
                    $this->alreadyYielded[] = $implementation;
101 4
                    break;
102
                }
103
            }
104
105 4
            yield from $this->CollectInterfacesFromImplementation($implementation);
106
        }
107 4
    }
108
109 4
    private function CollectInterfacesFromImplementation(string $implementation) : Generator
110
    {
111 4
        $interfaces = array_keys($this->staticMethods);
112
        /**
113
        * @var string $interface
114
        */
115 4
        foreach ($this->FilterIsA($implementation, $interfaces) as $interface) {
116 4
            foreach ($this->staticMethods[$interface] as $method => $types) {
117
                /**
118
                * @var iterable<string> $methodResult
119
                */
120 4
                $methodResult = $implementation::$method();
121
122 4
                foreach ($methodResult as $result) {
123 4
                    if (in_array($result, $this->alreadyYielded, true)) {
124
                        continue;
125
                    }
126
                    /**
127
                    * @var string $type
128
                    */
129 4
                    foreach ($this->FilterIsA($result, $types) as $type) {
130 4
                        yield $result;
131 4
                        $this->alreadyYielded[] = $result;
132
                    }
133
134 4
                    yield from $this->CollectInterfaces($result);
135
                }
136
            }
137
        }
138 4
    }
139
140 4
    private function FilterIsA(string $implementation, array $interfaces) : array
141
    {
142
        return array_filter($interfaces, function (string $interface) use ($implementation) : bool {
143 4
            return is_a($implementation, $interface, true);
144 4
        });
145
    }
146
147
    /**
148
    * @return string[]|array<string, mixed>
149
    */
150 4
    private function FilterArrayOfInterfaces(array $interfaces, int $flag = 0) : array
151
    {
152 4
        $strings = array_filter($interfaces, 'is_string', $flag);
153
154 4
        return array_filter($strings, 'interface_exists', $flag);
155
    }
156
157
    /**
158
    * @return string[]
159
    */
160 4
    private function FilterArrayOfInterfacesOrClasses(array $interfaces) : array
161
    {
162
        /**
163
        * @var string[] $strings
164
        */
165 4
        $strings = array_filter($interfaces, 'is_string');
166
167
        return array_filter($strings, function (string $maybe) : bool {
168 4
            return interface_exists($maybe) || class_exists($maybe);
169 4
        });
170
    }
171
172
    /**
173
    * @return array<string, array>
174
    */
175 4
    private function FilterArrayOfInterfaceOffsets(array $interfaces) : array
176
    {
177
        /**
178
        * @var array<string, array> $strings
179
        */
180 4
        $strings = $this->FilterArrayOfInterfaces($interfaces, ARRAY_FILTER_USE_KEY);
181
182 4
        return array_filter($strings, 'is_array');
183
    }
184
185 4
    private function MakeMethodFilter(string $interface) : Closure
186
    {
187
        return function (string $maybe) use ($interface) : bool {
188 4
            $ref = new ReflectionClass($interface);
189
190
            return
191 4
                $ref->hasMethod($maybe) &&
192 4
                $this->FilterReflectionMethod($ref->getMethod($maybe));
193 4
        };
194
    }
195
196 4
    private function FilterReflectionMethod(ReflectionMethod $refMethod) : bool
197
    {
198
        return
199 4
            $refMethod->isStatic() &&
200 4
            $refMethod->isPublic() &&
201 4
            0 === $refMethod->getNumberOfRequiredParameters() &&
202 4
            $this->FilterReflectionReturnType($refMethod->getReturnType());
203
    }
204
205 4
    private function FilterReflectionReturnType(? ReflectionType $refReturn) : bool
206
    {
207 4
        $refReturnName = ($refReturn instanceof ReflectionNamedType) ? $refReturn->getName() : '';
208
209 4
        return 'array' === $refReturnName || is_a($refReturnName, Traversable::class, true);
210
    }
211
212
    /**
213
    * @return array<string, string[]>
214
    */
215 4
    private function FilterMethods(string $interface, array $methods) : array
216
    {
217
        /**
218
        * @var array<string, string[]>
219
        */
220 4
        $filteredMethods = $this->FilterNonZeroArray(array_map(
221 4
            [$this, 'FilterArrayOfInterfacesOrClasses'],
222 4
            array_filter(
223 4
                array_filter($methods, 'is_string', ARRAY_FILTER_USE_KEY),
224 4
                $this->MakeMethodFilter($interface),
225 4
                ARRAY_FILTER_USE_KEY
226
            )
227
        ));
228
229 4
        return $filteredMethods;
230
    }
231
232
    /**
233
    * @var array[]
234
    */
235 4
    private function FilterNonZeroArray(array $in) : array
236
    {
237 4
        return array_filter(
238 4
            $in,
239
            function (array $val) : bool {
240 4
                return count($val) > 0;
241 4
            }
242
        );
243
    }
244
}
245