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 ( debc17...b4542d )
by SignpostMarv
03:48
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 ReflectionClass;
13
use ReflectionMethod;
14
use ReflectionNamedType;
15
use ReflectionType;
16
use Traversable;
17
18
class StaticMethodCollector
19
{
20
    const DEFAULT_INT_ARRAY_FILTER_FLAG = 0;
21
22
    const INT_FILTER_NON_EMPTY_ARRAY = 0;
23
24
    /**
25
    * @var array<string, array<string, array<int, string>>>
26
    */
27
    private $staticMethods = [];
28
29
    /**
30
    * @var string[]
31
    */
32
    private $interfaces = [];
33
34
    /**
35
    * @var array<int, string>
36
    */
37
    private $processedSources = [];
38
39
    /**
40
    * @var string[]
41
    */
42
    private $alreadyYielded = [];
43
44
    /**
45
    * @var bool
46
    */
47
    private $autoReset;
48
49 4
    public function __construct(array $staticMethods, array $interfaces, bool $autoReset = true)
50
    {
51 4
        $filteredMethods = [];
52
53 4
        foreach ($this->FilterArrayOfInterfaceOffsets($staticMethods) as $interface => $methods) {
54 4
            $filteredMethods[$interface] = $this->FilterMethods($interface, $methods);
55
        }
56
57 4
        $this->staticMethods = $this->FilterNonZeroArray($filteredMethods);
58
59
        /**
60
        * @var string[]
61
        */
62 4
        $filteredInterfaces = $this->FilterArrayOfInterfacesOrClasses($interfaces);
63
64 4
        $this->interfaces = $filteredInterfaces;
65
66 4
        $this->autoReset = $autoReset;
67 4
    }
68
69 4
    public function Collect(string ...$implementations) : Generator
70
    {
71 4
        if ($this->autoReset) {
72 2
            $this->processedSources = [];
73 2
            $this->alreadyYielded = [];
74
        }
75
76 4
        yield from $this->CollectInterfaces(...$implementations);
77 4
    }
78
79 4
    protected function CollectInterfaces(string ...$implementations) : Generator
80
    {
81
        foreach (
82 4
            array_filter(
83 4
                $implementations,
84
                function (string $implementation) : bool {
85
                    return
86 4
                        ! static::IsStringInArray($implementation, $this->processedSources);
87 4
                }
88
            ) as $implementation
89
        ) {
90 4
            $this->processedSources[] = $implementation;
91 4
            yield from $this->CollectInterfacesFromImplementationCheckInterfaces($implementation);
92 4
            yield from $this->CollectInterfacesFromImplementation($implementation);
93
        }
94 4
    }
95
96 4
    final protected function CollectInterfacesFromImplementationCheckInterfaces(
97
        string $implementation
98
    ) : Generator {
99 4
        $checking = array_filter(
100 4
            $this->interfaces,
101
            function (string $interface) use ($implementation) : bool {
102 4
                return static::IsStringA($implementation, $interface);
103 4
            }
104
        );
105 4
        foreach ($checking as $interface) {
106
            if (
107 4
                ! static::IsStringInArray($implementation, $this->alreadyYielded)
108
            ) {
109
                yield $implementation;
110
                $this->alreadyYielded[] = $implementation;
111 4
                break;
112
            }
113
        }
114 4
    }
115
116 4
    final protected function CollectInterfacesFromImplementation(string $implementation) : Generator
117
    {
118 4
        $interfaces = array_keys($this->staticMethods);
119
120 4
        foreach ($this->FilterIsA($implementation, $interfaces) as $interface) {
121 4
            foreach ($this->staticMethods[$interface] as $method => $types) {
122 4
                yield from $this->CollectInterfacesFromImplementationTypes(
123 4
                    $implementation,
124 4
                    $method,
125 4
                    $types
126
                );
127
            }
128
        }
129 4
    }
130
131
    /**
132
    * @param array<int, string> $types
133
    */
134 4
    final protected function CollectInterfacesFromImplementationTypes(
135
        string $implementation,
136
        string $method,
137
        array $types
138
    ) : Generator {
139
        /**
140
        * @var iterable<string>
141
        */
142 4
        $methodResult = $implementation::$method();
143
144 4
        foreach ($methodResult as $result) {
145 4
            if (static::IsStringInArray($result, $this->alreadyYielded)) {
146
                continue;
147
            }
148
149 4
            foreach ($this->FilterIsA($result, $types) as $type) {
150 4
                yield $result;
151 4
                $this->alreadyYielded[] = $result;
152
            }
153
154 4
            yield from $this->CollectInterfaces($result);
155
        }
156 4
    }
157
158
    /**
159
    * @param array<int, string> $interfaces
160
    *
161
    * @return array<int, string>
162
    */
163 4
    final protected function FilterIsA(string $implementation, array $interfaces) : array
164
    {
165
        /**
166
        * @var array<int, string>
167
        */
168
        $out = array_filter($interfaces, function (string $interface) use ($implementation) : bool {
169 4
            return static::IsStringA($implementation, $interface);
170 4
        });
171
172 4
        return $out;
173
    }
174
175
    /**
176
    * @return string[]|array<string, mixed>
177
    */
178 4
    final protected function FilterArrayOfInterfaces(
179
        array $interfaces,
180
        int $flag = self::DEFAULT_INT_ARRAY_FILTER_FLAG
181
    ) : array {
182 4
        $strings = array_filter($interfaces, 'is_string', $flag);
183
184 4
        return array_filter($strings, 'interface_exists', $flag);
185
    }
186
187
    /**
188
    * @return string[]
189
    */
190 4
    final protected function FilterArrayOfInterfacesOrClasses(array $interfaces) : array
191
    {
192
        /**
193
        * @var string[]
194
        */
195 4
        $strings = array_filter($interfaces, 'is_string');
196
197
        return array_filter($strings, function (string $maybe) : bool {
198 4
            return interface_exists($maybe) || class_exists($maybe);
199 4
        });
200
    }
201
202
    /**
203
    * @return array<string, array>
204
    */
205 4
    final protected function FilterArrayOfInterfaceOffsets(array $interfaces) : array
206
    {
207
        /**
208
        * @var array<string, array>
209
        */
210 4
        $strings = $this->FilterArrayOfInterfaces($interfaces, ARRAY_FILTER_USE_KEY);
211
212 4
        return array_filter($strings, 'is_array');
213
    }
214
215 4
    final protected function MakeMethodFilter(string $interface) : Closure
216
    {
217
        return function (string $maybe) use ($interface) : bool {
218 4
            $ref = new ReflectionClass($interface);
219
220
            return
221 4
                $ref->hasMethod($maybe) &&
222 4
                $this->FilterReflectionMethod($ref->getMethod($maybe));
223 4
        };
224
    }
225
226 4
    final protected function FilterReflectionMethod(ReflectionMethod $refMethod) : bool
227
    {
228
        return
229 4
            $refMethod->isStatic() &&
230 4
            $refMethod->isPublic() &&
231 4
            0 === $refMethod->getNumberOfRequiredParameters() &&
232 4
            $this->FilterReflectionReturnType($refMethod->getReturnType());
233
    }
234
235 4
    final protected function FilterReflectionReturnType(? ReflectionType $refReturn) : bool
236
    {
237 4
        $refReturnName = ($refReturn instanceof ReflectionNamedType) ? $refReturn->getName() : '';
238
239 4
        return 'array' === $refReturnName || static::IsStringA($refReturnName, Traversable::class);
240
    }
241
242
    /**
243
    * @return array<string, string[]>
244
    */
245 4
    final protected function FilterMethods(string $interface, array $methods) : array
246
    {
247
        /**
248
        * @var array<string, string[]>
249
        */
250 4
        $filteredMethods = $this->FilterNonZeroArray(array_map(
251 4
            [$this, 'FilterArrayOfInterfacesOrClasses'],
252 4
            array_filter(
253 4
                array_filter($methods, 'is_string', ARRAY_FILTER_USE_KEY),
254 4
                $this->MakeMethodFilter($interface),
255 4
                ARRAY_FILTER_USE_KEY
256
            )
257
        ));
258
259 4
        return $filteredMethods;
260
    }
261
262
    /**
263
    * @return array<string, array<string, array<int, string>>>
264
    */
265 4
    final protected function FilterNonZeroArray(array $in) : array
266
    {
267
        /**
268
        * @var array<string, array<string, array<int, string>>>
269
        */
270 4
        $out = array_filter(
271 4
            $in,
272
            function (array $val) : bool {
273 4
                return count($val) > self::INT_FILTER_NON_EMPTY_ARRAY;
274 4
            }
275
        );
276
277 4
        return $out;
278
    }
279
280 4
    protected static function IsStringInArray(string $maybe, array $array) : bool
281
    {
282 4
        return in_array($maybe, $array, true);
283
    }
284
285 4
    protected static function IsStringA(string $maybe, string $thing) : bool
286
    {
287 4
        return is_a($maybe, $thing, true);
288
    }
289
}
290