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 ( 8745eb...322155 )
by SignpostMarv
08:51
created

StaticMethodCollector   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 266
Duplicated Lines 0 %

Test Coverage

Coverage 96.91%

Importance

Changes 0
Metric Value
eloc 88
dl 0
loc 266
ccs 94
cts 97
cp 0.9691
rs 9.52
c 0
b 0
f 0
wmc 36

17 Methods

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