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 ( b4542d...bce059 )
by SignpostMarv
02:16
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
    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
106
        if (
107 4
            count($checking) > self::INT_FILTER_NON_EMPTY_ARRAY &&
108 4
            ! static::IsStringInArray($implementation, $this->alreadyYielded)
109
        ) {
110
            yield $implementation;
111
            $this->alreadyYielded[] = $implementation;
112
        }
113 4
    }
114
115 4
    final protected function CollectInterfacesFromImplementation(string $implementation) : Generator
116
    {
117 4
        $interfaces = array_keys($this->staticMethods);
118
119 4
        foreach ($this->FilterIsA($implementation, $interfaces) as $interface) {
120 4
            foreach ($this->staticMethods[$interface] as $method => $types) {
121 4
                yield from $this->CollectInterfacesFromImplementationTypes(
122 4
                    $implementation,
123 4
                    $method,
124 4
                    $types
125
                );
126
            }
127
        }
128 4
    }
129
130
    /**
131
    * @param array<int, string> $types
132
    */
133 4
    final protected function CollectInterfacesFromImplementationTypes(
134
        string $implementation,
135
        string $method,
136
        array $types
137
    ) : Generator {
138
        /**
139
        * @var iterable<string>
140
        */
141 4
        $methodResult = $implementation::$method();
142
143 4
        foreach ($methodResult as $result) {
144 4
            if (static::IsStringInArray($result, $this->alreadyYielded)) {
145
                continue;
146
            }
147
148 4
            foreach ($this->FilterIsA($result, $types) as $type) {
149 4
                yield $result;
150 4
                $this->alreadyYielded[] = $result;
151
            }
152
153 4
            yield from $this->CollectInterfaces($result);
154
        }
155 4
    }
156
157
    /**
158
    * @param array<int, string> $interfaces
159
    *
160
    * @return array<int, string>
161
    */
162 4
    final protected function FilterIsA(string $implementation, array $interfaces) : array
163
    {
164
        /**
165
        * @var array<int, string>
166
        */
167
        $out = array_filter($interfaces, function (string $interface) use ($implementation) : bool {
168 4
            return static::IsStringA($implementation, $interface);
169 4
        });
170
171 4
        return $out;
172
    }
173
174
    /**
175
    * @return string[]|array<string, mixed>
176
    */
177 4
    final protected function FilterArrayOfInterfaces(
178
        array $interfaces,
179
        int $flag = self::DEFAULT_INT_ARRAY_FILTER_FLAG
180
    ) : array {
181 4
        $strings = array_filter($interfaces, 'is_string', $flag);
182
183 4
        return array_filter($strings, 'interface_exists', $flag);
184
    }
185
186
    /**
187
    * @return string[]
188
    */
189 4
    final protected function FilterArrayOfInterfacesOrClasses(array $interfaces) : array
190
    {
191
        /**
192
        * @var string[]
193
        */
194 4
        $strings = array_filter($interfaces, 'is_string');
195
196
        return array_filter($strings, function (string $maybe) : bool {
197 4
            return interface_exists($maybe) || class_exists($maybe);
198 4
        });
199
    }
200
201
    /**
202
    * @return array<string, array>
203
    */
204 4
    final protected function FilterArrayOfInterfaceOffsets(array $interfaces) : array
205
    {
206
        /**
207
        * @var array<string, array>
208
        */
209 4
        $strings = $this->FilterArrayOfInterfaces($interfaces, ARRAY_FILTER_USE_KEY);
210
211 4
        return array_filter($strings, 'is_array');
212
    }
213
214 4
    final protected function MakeMethodFilter(string $interface) : Closure
215
    {
216
        return function (string $maybe) use ($interface) : bool {
217 4
            $ref = new ReflectionClass($interface);
218
219
            return
220 4
                $ref->hasMethod($maybe) &&
221 4
                $this->FilterReflectionMethod($ref->getMethod($maybe));
222 4
        };
223
    }
224
225 4
    final protected function FilterReflectionMethod(ReflectionMethod $refMethod) : bool
226
    {
227
        return
228 4
            $refMethod->isStatic() &&
229 4
            $refMethod->isPublic() &&
230 4
            0 === $refMethod->getNumberOfRequiredParameters() &&
231 4
            $this->FilterReflectionReturnType($refMethod->getReturnType());
232
    }
233
234 4
    final protected function FilterReflectionReturnType(? ReflectionType $refReturn) : bool
235
    {
236 4
        $refReturnName = ($refReturn instanceof ReflectionNamedType) ? $refReturn->getName() : '';
237
238 4
        return 'array' === $refReturnName || static::IsStringA($refReturnName, Traversable::class);
239
    }
240
241
    /**
242
    * @return array<string, string[]>
243
    */
244 4
    final protected function FilterMethods(string $interface, array $methods) : array
245
    {
246
        /**
247
        * @var array<string, string[]>
248
        */
249 4
        $filteredMethods = $this->FilterNonZeroArray(array_map(
250 4
            [$this, 'FilterArrayOfInterfacesOrClasses'],
251 4
            array_filter(
252 4
                array_filter($methods, 'is_string', ARRAY_FILTER_USE_KEY),
253 4
                $this->MakeMethodFilter($interface),
254 4
                ARRAY_FILTER_USE_KEY
255
            )
256
        ));
257
258 4
        return $filteredMethods;
259
    }
260
261
    /**
262
    * @return array<string, array<string, array<int, string>>>
263
    */
264 4
    final protected function FilterNonZeroArray(array $in) : array
265
    {
266
        /**
267
        * @var array<string, array<string, array<int, string>>>
268
        */
269 4
        $out = array_filter(
270 4
            $in,
271
            function (array $val) : bool {
272 4
                return count($val) > self::INT_FILTER_NON_EMPTY_ARRAY;
273 4
            }
274
        );
275
276 4
        return $out;
277
    }
278
279 4
    protected static function IsStringInArray(string $maybe, array $array) : bool
280
    {
281 4
        return in_array($maybe, $array, true);
282
    }
283
284 4
    protected static function IsStringA(string $maybe, string $thing) : bool
285
    {
286 4
        return is_a($maybe, $thing, true);
287
    }
288
}
289