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 ( fee3b1...012797 )
by SignpostMarv
02:49
created

CollectInterfacesFromImplementationCheckInterfaces()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4.3731

Importance

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