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 ( 7e56d7...40617c )
by SignpostMarv
07:15
created

StaticMethodCollector::Collect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
ccs 5
cts 5
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 InvalidArgumentException;
13
use ReflectionClass;
14
use ReflectionMethod;
15
use ReflectionNamedType;
16
use ReflectionType;
17
use Traversable;
18
19
class StaticMethodCollector
20
{
21
    const DEFAULT_INT_ARRAY_FILTER_FLAG = 0;
22
23
    const DEFAULT_BOOL_AUTORESET = true;
24
25
    const INT_FILTER_NON_EMPTY_ARRAY = 0;
26
27
    const EXPECTED_NUMBER_OF_REQUIRED_PARAMETERS = 0;
28
29
    /**
30
    * @var array<int, class-string>
1 ignored issue
show
Documentation Bug introduced by
The doc comment array<int, class-string> at position 4 could not be parsed: Unknown type name 'class-string' at position 4 in array<int, class-string>.
Loading history...
31
    */
32
    protected $processedSources = [];
33
34
    /**
35
    * @var class-string[]
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string[].
Loading history...
36
    */
37
    protected $alreadyYielded = [];
38
39
    /**
40
    * @var bool
41
    */
42
    protected $autoReset;
43
44
    /**
45
    * @var array<class-string, array<string, array<int, class-string>>>
1 ignored issue
show
Documentation Bug introduced by
The doc comment array<class-string, arra...ay<int, class-string>>> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, array<string, array<int, class-string>>>.
Loading history...
46
    */
47
    private $staticMethods = [];
48
49
    /**
50
    * @var class-string[]
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string[].
Loading history...
51
    */
52
    private $interfaces = [];
53
54 8
    public function __construct(
55
        array $staticMethods,
56
        array $interfaces,
57
        bool $autoReset = self::DEFAULT_BOOL_AUTORESET
58
    ) {
59 8
        $filteredMethods = [];
60
61 8
        foreach ($this->FilterArrayOfInterfaceOffsets($staticMethods) as $interface => $methods) {
62 8
            $filteredMethods[$interface] = $this->FilterMethods($interface, $methods);
63
        }
64
65 8
        $this->staticMethods = $this->FilterNonZeroArray($filteredMethods);
66
67 8
        $this->interfaces = $this->FilterArrayOfInterfacesOrClasses($interfaces);
68
69 8
        $this->autoReset = $autoReset;
70 8
    }
71
72
    /**
73
    * @param class-string ...$implementations
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
74
    */
75 4
    public function Collect(string ...$implementations) : Generator
76
    {
77 4
        if ($this->autoReset) {
78 2
            $this->processedSources = [];
79 2
            $this->alreadyYielded = [];
80
        }
81
82 4
        yield from $this->CollectInterfaces(...$implementations);
83 4
    }
84
85
    /**
86
    * @param class-string ...$implementations
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
87
    */
88 8
    protected function CollectInterfaces(string ...$implementations) : Generator
89
    {
90
        foreach (
91 8
            array_filter(
92 8
                $implementations,
93
                /**
94
                * @param class-string $implementation
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
95
                */
96
                function (string $implementation) : bool {
97
                    return
98 8
                        ! static::IsStringInArray($implementation, $this->processedSources);
99 8
                }
100
            ) as $implementation
101
        ) {
102 8
            $this->processedSources[] = $implementation;
103 8
            yield from $this->CollectInterfacesFromImplementationCheckInterfaces($implementation);
104 8
            yield from $this->CollectInterfacesFromImplementation($implementation);
105
        }
106 8
    }
107
108
    /**
109
    * @param class-string $implementation
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
110
    */
111 8
    final protected function CollectInterfacesFromImplementationCheckInterfaces(
112
        string $implementation
113
    ) : Generator {
114 8
        $checking = array_filter(
115 8
            $this->interfaces,
116
            /**
117
            * @param class-string $interface
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
118
            */
119
            function (string $interface) use ($implementation) : bool {
120 8
                return static::IsStringA($implementation, $interface);
121 8
            }
122
        );
123
124
        if (
125 8
            count($checking) > self::INT_FILTER_NON_EMPTY_ARRAY &&
126 8
            ! static::IsStringInArray($implementation, $this->alreadyYielded)
127
        ) {
128
            yield $implementation;
129
            $this->alreadyYielded[] = $implementation;
130
        }
131 8
    }
132
133
    /**
134
    * @param class-string $implementation
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
135
    */
136 8
    final protected function CollectInterfacesFromImplementation(string $implementation) : Generator
137
    {
138 8
        $interfaces = array_keys($this->staticMethods);
139
140 8
        foreach ($this->FilterIsA($implementation, $interfaces) as $interface) {
141 8
            foreach ($this->staticMethods[$interface] as $method => $types) {
142 8
                yield from $this->CollectInterfacesFromImplementationTypes(
143 8
                    $implementation,
144 8
                    $method,
145 8
                    $types
146
                );
147
            }
148
        }
149 8
    }
150
151
    /**
152
    * @param class-string $implementation
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
153
    * @param array<int, class-string> $types
154
    */
155 8
    final protected function CollectInterfacesFromImplementationTypes(
156
        string $implementation,
157
        string $method,
158
        array $types
159
    ) : Generator {
160 8
        if ( ! method_exists($implementation, $method)) {
161
            throw new InvalidArgumentException(
162
                'Argument 2 passed to ' .
163
                __METHOD__ .
164
                ' is not a method on Argument 1!'
165
            );
166
        }
167
168
        /**
169
        * @var iterable<class-string>
170
        */
171 8
        $methodResult = $implementation::$method();
172
173 8
        foreach ($methodResult as $result) {
174 8
            if (static::IsStringInArray($result, $this->alreadyYielded)) {
175 2
                continue;
176
            }
177
178 8
            foreach ($this->FilterIsA($result, $types) as $type) {
179 8
                yield $result;
180 8
                $this->alreadyYielded[] = $result;
181
            }
182
183 8
            yield from $this->CollectInterfaces($result);
184
        }
185 8
    }
186
187
    /**
188
    * @template T
189
    *
190
    * @param T::class $implementation
1 ignored issue
show
Documentation Bug introduced by
The doc comment $implementation at position 0 could not be parsed: Unknown type name '$implementation' at position 0 in $implementation.
Loading history...
191
    * @param array<int, class-string> $interfaces
192
    *
193
    * @return array<int, T::class>
1 ignored issue
show
Documentation Bug introduced by
The doc comment array<int, T at position 4 could not be parsed: Expected '>' at position 4, but found 'T'.
Loading history...
194
    */
195 8
    final protected function FilterIsA(string $implementation, array $interfaces) : array
196
    {
197 8
        return array_filter(
198 8
            $interfaces,
199
            /**
200
            * @param class-string $interface
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
201
            */
202
            function (string $interface) use ($implementation) : bool {
203 8
                return static::IsStringA($implementation, $interface);
204 8
            }
205
        );
206
    }
207
208
    /**
209
    * @return string[]|array<string, mixed>
210
    */
211 8
    final protected function FilterArrayOfInterfaces(
212
        array $interfaces,
213
        int $flag = self::DEFAULT_INT_ARRAY_FILTER_FLAG
214
    ) : array {
215 8
        $strings = array_filter($interfaces, 'is_string', $flag);
216
217 8
        return array_filter($strings, 'interface_exists', $flag);
218
    }
219
220
    /**
221
    * @return class-string[]
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string[].
Loading history...
222
    */
223 8
    final protected function FilterArrayOfInterfacesOrClasses(array $interfaces) : array
224
    {
225
        /**
226
        * @var class-string[]
227
        */
228 8
        $strings = array_filter(
229 8
            array_filter($interfaces, 'is_string'),
230
            function (string $maybe) : bool {
231 8
                return interface_exists($maybe) || class_exists($maybe);
232 8
            }
233
        );
234
235 8
        return $strings;
236
    }
237
238
    /**
239
    * @return array<class-string, array>
1 ignored issue
show
Documentation Bug introduced by
The doc comment array<class-string, array> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, array>.
Loading history...
240
    */
241 8
    final protected function FilterArrayOfInterfaceOffsets(array $interfaces) : array
242
    {
243
        /**
244
        * @var array<class-string, array>
245
        */
246 8
        $strings = array_filter(
247 8
            $this->FilterArrayOfInterfaces($interfaces, ARRAY_FILTER_USE_KEY),
248 8
            'is_array'
249
        );
250
251 8
        return $strings;
252
    }
253
254
    /**
255
    * @param class-string $interface
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
256
    */
257 8
    final protected function MakeMethodFilter(string $interface) : Closure
258
    {
259
        return function (string $maybe) use ($interface) : bool {
260 8
            $ref = new ReflectionClass($interface);
261
262
            return
263 8
                $ref->hasMethod($maybe) &&
264 8
                $this->FilterReflectionMethod($ref->getMethod($maybe));
265 8
        };
266
    }
267
268 8
    final protected function FilterReflectionMethod(ReflectionMethod $refMethod) : bool
269
    {
270
        return
271 8
            $refMethod->isStatic() &&
272 8
            $refMethod->isPublic() &&
273 8
            self::EXPECTED_NUMBER_OF_REQUIRED_PARAMETERS === $refMethod->getNumberOfRequiredParameters() &&
274 8
            $this->FilterReflectionReturnType($refMethod->getReturnType());
275
    }
276
277 8
    final protected function FilterReflectionReturnType(? ReflectionType $refReturn) : bool
278
    {
279
        /**
280
        * @var string|class-string
281
        */
282 8
        $refReturnName = ($refReturn instanceof ReflectionNamedType) ? $refReturn->getName() : '';
283
284 8
        return 'array' === $refReturnName || static::IsStringA($refReturnName, Traversable::class);
285
    }
286
287
    /**
288
    * @param class-string $interface
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
289
    *
290
    * @return array<class-string, string[]>
1 ignored issue
show
Documentation Bug introduced by
The doc comment array<class-string, string[]> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, string[]>.
Loading history...
291
    */
292 8
    final protected function FilterMethods(string $interface, array $methods) : array
293
    {
294
        /**
295
        * @var array<class-string, string[]>
296
        */
297 8
        $filteredMethods = $this->FilterNonZeroArray(array_map(
298 8
            [$this, 'FilterArrayOfInterfacesOrClasses'],
299 8
            array_filter(
300 8
                array_filter($methods, 'is_string', ARRAY_FILTER_USE_KEY),
301 8
                $this->MakeMethodFilter($interface),
302 8
                ARRAY_FILTER_USE_KEY
303
            )
304
        ));
305
306 8
        return $filteredMethods;
307
    }
308
309
    /**
310
    * @return array<class-string, array<string, array<int, class-string>>>
1 ignored issue
show
Documentation Bug introduced by
The doc comment array<class-string, arra...ay<int, class-string>>> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, array<string, array<int, class-string>>>.
Loading history...
311
    */
312 8
    final protected function FilterNonZeroArray(array $in) : array
313
    {
314
        /**
315
        * @var array<class-string, array<string, array<int, class-string>>>
316
        */
317 8
        $out = array_filter(
318 8
            $in,
319
            function (array $val) : bool {
320 8
                return count($val) > self::INT_FILTER_NON_EMPTY_ARRAY;
321 8
            }
322
        );
323
324 8
        return $out;
325
    }
326
327
    /**
328
    * @param class-string $maybe
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
329
    * @param class-string[] $array
330
    */
331 8
    protected static function IsStringInArray(string $maybe, array $array) : bool
332
    {
333 8
        return in_array($maybe, $array, true);
334
    }
335
336
    /**
337
    * @param class-string $thing
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
338
    */
339 8
    protected static function IsStringA(string $maybe, string $thing) : bool
340
    {
341 8
        return is_a($maybe, $thing, true);
342
    }
343
}
344