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 ( 25c653...7e56d7 )
by SignpostMarv
02:20
created

StaticMethodCollector::__construct()   A

Complexity

Conditions 2
Paths 2

Size

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