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

CollectInterfacesFromImplementation()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

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