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 ( 8a95fd...6ac8e4 )
by SignpostMarv
01:39
created

StaticMethodCollector::IsStringA()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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