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 ( 322155...debc17 )
by SignpostMarv
02:31
created

CollectInterfacesFromImplementationCheckInterfaces()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.072

Importance

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