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 ( 5fe763...8745eb )
by SignpostMarv
07:02
created

StaticMethodCollector::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 3
dl 0
loc 18
ccs 8
cts 8
cp 1
crap 2
rs 10
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, array<int, 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 4
        $this->staticMethods = $this->FilterNonZeroArray($filteredMethods);
54
55
        /**
56
        * @var string[]
57
        */
58 4
        $filteredInterfaces = $this->FilterArrayOfInterfacesOrClasses($interfaces);
59
60 4
        $this->interfaces = $filteredInterfaces;
61
62 4
        $this->autoReset = $autoReset;
63 4
    }
64
65 4
    public function Collect(string ...$implementations) : Generator
66
    {
67 4
        if ($this->autoReset) {
68 2
            $this->processedSources = [];
69 2
            $this->alreadyYielded = [];
70
        }
71
72 4
        yield from $this->CollectInterfaces(...$implementations);
73 4
    }
74
75 4
    protected function CollectInterfaces(string ...$implementations) : Generator
76
    {
77
        foreach (
78 4
            array_filter(
79 4
                $implementations,
80
                function (string $implementation) : bool {
81
                    return
82 4
                        class_exists($implementation) &&
83 4
                        ! in_array($implementation, $this->processedSources, true);
84 4
                }
85
            ) as $implementation
86
        ) {
87 4
            $this->processedSources[] = $implementation;
88 4
            yield from $this->CollectInterfacesFromImplementationCheckInterfaces($implementation);
89 4
            yield from $this->CollectInterfacesFromImplementation($implementation);
90
        }
91 4
    }
92
93 4
    final protected function CollectInterfacesFromImplementationCheckInterfaces(
94
        string $implementation
95
    ) : Generator {
96 4
        foreach ($this->interfaces as $interface) {
97
            if (
98 4
                ! in_array($implementation, $this->alreadyYielded, true) &&
99 4
                is_a($implementation, $interface, true)
100
            ) {
101
                yield $implementation;
102
                $this->alreadyYielded[] = $implementation;
103 4
                break;
104
            }
105
        }
106 4
    }
107
108 4
    final protected function CollectInterfacesFromImplementation(string $implementation) : Generator
109
    {
110 4
        $interfaces = array_keys($this->staticMethods);
111
112 4
        foreach ($this->FilterIsA($implementation, $interfaces) as $interface) {
113 4
            foreach ($this->staticMethods[$interface] as $method => $types) {
114 4
                yield from $this->CollectInterfacesFromImplementationTypes(
115 4
                    $implementation,
116 4
                    $method,
117 4
                    $types
118
                );
119
            }
120
        }
121 4
    }
122
123
    /**
124
    * @param array<int, string> $types
125
    *
126
    * @psalm-suppress InvalidStringClass
127
    */
128 4
    final protected function CollectInterfacesFromImplementationTypes(
129
        string $implementation,
130
        string $method,
131
        array $types
132
    ) : Generator {
133
        /**
134
        * @var iterable<string>
135
        */
136 4
        $methodResult = $implementation::$method();
137
138 4
        foreach ($methodResult as $result) {
139 4
            if (in_array($result, $this->alreadyYielded, true)) {
140
                continue;
141
            }
142
143 4
            foreach ($this->FilterIsA($result, $types) as $type) {
144 4
                yield $result;
145 4
                $this->alreadyYielded[] = $result;
146
            }
147
148 4
            yield from $this->CollectInterfaces($result);
149
        }
150 4
    }
151
152
    /**
153
    * @param array<int, string> $interfaces
154
    *
155
    * @return array<int, string>
156
    */
157 4
    final protected function FilterIsA(string $implementation, array $interfaces) : array
158
    {
159
        /**
160
        * @var array<int, string>
161
        */
162
        $out = array_filter($interfaces, function (string $interface) use ($implementation) : bool {
163 4
            return is_a($implementation, $interface, true);
164 4
        });
165
166 4
        return $out;
167
    }
168
169
    /**
170
    * @return string[]|array<string, mixed>
171
    */
172 4
    final protected function FilterArrayOfInterfaces(array $interfaces, int $flag = 0) : array
173
    {
174 4
        $strings = array_filter($interfaces, 'is_string', $flag);
175
176 4
        return array_filter($strings, 'interface_exists', $flag);
177
    }
178
179
    /**
180
    * @return string[]
181
    */
182 4
    final protected function FilterArrayOfInterfacesOrClasses(array $interfaces) : array
183
    {
184
        /**
185
        * @var string[]
186
        */
187 4
        $strings = array_filter($interfaces, 'is_string');
188
189
        return array_filter($strings, function (string $maybe) : bool {
190 4
            return interface_exists($maybe) || class_exists($maybe);
191 4
        });
192
    }
193
194
    /**
195
    * @return array<string, array>
196
    */
197 4
    final protected function FilterArrayOfInterfaceOffsets(array $interfaces) : array
198
    {
199
        /**
200
        * @var array<string, array>
201
        */
202 4
        $strings = $this->FilterArrayOfInterfaces($interfaces, ARRAY_FILTER_USE_KEY);
203
204 4
        return array_filter($strings, 'is_array');
205
    }
206
207 4
    final protected function MakeMethodFilter(string $interface) : Closure
208
    {
209
        return function (string $maybe) use ($interface) : bool {
210 4
            $ref = new ReflectionClass($interface);
211
212
            return
213 4
                $ref->hasMethod($maybe) &&
214 4
                $this->FilterReflectionMethod($ref->getMethod($maybe));
215 4
        };
216
    }
217
218 4
    final protected function FilterReflectionMethod(ReflectionMethod $refMethod) : bool
219
    {
220
        return
221 4
            $refMethod->isStatic() &&
222 4
            $refMethod->isPublic() &&
223 4
            0 === $refMethod->getNumberOfRequiredParameters() &&
224 4
            $this->FilterReflectionReturnType($refMethod->getReturnType());
225
    }
226
227 4
    final protected function FilterReflectionReturnType(? ReflectionType $refReturn) : bool
228
    {
229 4
        $refReturnName = ($refReturn instanceof ReflectionNamedType) ? $refReturn->getName() : '';
230
231 4
        return 'array' === $refReturnName || is_a($refReturnName, Traversable::class, true);
232
    }
233
234
    /**
235
    * @return array<string, string[]>
236
    */
237 4
    final protected function FilterMethods(string $interface, array $methods) : array
238
    {
239
        /**
240
        * @var array<string, string[]>
241
        */
242 4
        $filteredMethods = $this->FilterNonZeroArray(array_map(
243 4
            [$this, 'FilterArrayOfInterfacesOrClasses'],
244 4
            array_filter(
245 4
                array_filter($methods, 'is_string', ARRAY_FILTER_USE_KEY),
246 4
                $this->MakeMethodFilter($interface),
247 4
                ARRAY_FILTER_USE_KEY
248
            )
249
        ));
250
251 4
        return $filteredMethods;
252
    }
253
254
    /**
255
    * @return array<string, array<string, array<int, string>>>
256
    */
257 4
    final protected function FilterNonZeroArray(array $in) : array
258
    {
259
        /**
260
        * @var array<string, array<string, array<int, string>>>
261
        */
262 4
        $out = array_filter(
263 4
            $in,
264
            function (array $val) : bool {
265 4
                return count($val) > 0;
266 4
            }
267
        );
268
269 4
        return $out;
270
    }
271
}
272