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 ReflectionType; |
15
|
|
|
use Traversable; |
16
|
|
|
|
17
|
|
|
class StaticMethodCollector |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* @var array<string, array<string, string[]>> |
21
|
|
|
*/ |
22
|
|
|
private $staticMethods = []; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var string[] |
26
|
|
|
*/ |
27
|
|
|
private $interfaces = []; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var array<int, string> |
31
|
|
|
*/ |
32
|
|
|
private $processedSources = []; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var string[] |
36
|
|
|
*/ |
37
|
|
|
private $alreadyYielded = []; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var bool |
41
|
|
|
*/ |
42
|
|
|
private $autoReset; |
43
|
|
|
|
44
|
4 |
|
public function __construct(array $staticMethods, array $interfaces, bool $autoReset = true) |
45
|
|
|
{ |
46
|
4 |
|
$filteredMethods = []; |
47
|
|
|
|
48
|
4 |
|
foreach ($this->FilterArrayOfInterfaceOffsets($staticMethods) as $interface => $methods) { |
49
|
4 |
|
$filteredMethods[$interface] = $this->FilterMethods($interface, $methods); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var array<string, array<string, string[]>> $filteredMethods |
54
|
|
|
*/ |
55
|
4 |
|
$filteredMethods = $this->FilterNonZeroArray($filteredMethods); |
56
|
|
|
|
57
|
4 |
|
$this->staticMethods = $filteredMethods; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @var string[] $filteredInterfaces |
61
|
|
|
*/ |
62
|
4 |
|
$filteredInterfaces = $this->FilterArrayOfInterfaces($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
|
|
|
/** |
82
|
|
|
* @var string[] $interfaces |
83
|
|
|
*/ |
84
|
4 |
|
$interfaces = array_keys($this->staticMethods); |
85
|
4 |
|
foreach (array_filter($implementations, 'class_exists') as $implementation) { |
86
|
|
|
if ( |
87
|
4 |
|
in_array($implementation, $this->processedSources, true) || |
88
|
4 |
|
in_array($implementation, $this->alreadyYielded, true) |
89
|
|
|
) { |
90
|
2 |
|
continue; |
91
|
|
|
} |
92
|
4 |
|
$this->processedSources[] = $implementation; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* @var string $interface |
96
|
|
|
*/ |
97
|
4 |
|
foreach ($this->interfaces as $interface) { |
98
|
4 |
|
if (is_a($implementation, $interface, true)) { |
99
|
|
|
yield $implementation; |
100
|
|
|
$this->alreadyYielded[] = $implementation; |
101
|
4 |
|
break; |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|
105
|
4 |
|
foreach ($interfaces as $interface) { |
106
|
4 |
|
if (is_a($implementation, $interface, true)) { |
107
|
|
|
/** |
108
|
|
|
* @var array<int, string> $types |
109
|
|
|
*/ |
110
|
4 |
|
foreach ($this->staticMethods[$interface] as $method => $types) { |
111
|
|
|
/** |
112
|
|
|
* @var iterable<string> $methodResult |
113
|
|
|
*/ |
114
|
4 |
|
$methodResult = $implementation::$method(); |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @var string $result |
118
|
|
|
*/ |
119
|
4 |
|
foreach ($methodResult as $result) { |
120
|
4 |
|
if (in_array($result, $this->alreadyYielded, true)) { |
121
|
|
|
continue; |
122
|
|
|
} |
123
|
4 |
|
foreach ($types as $type) { |
124
|
4 |
|
if (is_a($result, $type, true)) { |
125
|
4 |
|
yield $result; |
126
|
4 |
|
$this->alreadyYielded[] = $result; |
127
|
4 |
|
continue; |
128
|
|
|
} |
129
|
|
|
} |
130
|
4 |
|
foreach ($interfaces as $checkResultWithInterface) { |
131
|
4 |
|
if (is_a($result, $checkResultWithInterface, true)) { |
132
|
4 |
|
yield from $this->CollectInterfaces($result); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
} |
140
|
4 |
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @return string[]|array<string, mixed> |
144
|
|
|
*/ |
145
|
4 |
|
private function FilterArrayOfInterfaces(array $interfaces, int $flag = 0) : array |
146
|
|
|
{ |
147
|
4 |
|
$strings = array_filter($interfaces, 'is_string', $flag); |
148
|
|
|
|
149
|
4 |
|
return array_filter($strings, 'interface_exists', $flag); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @return array<string, array> |
154
|
|
|
*/ |
155
|
4 |
|
private function FilterArrayOfInterfaceOffsets(array $interfaces) : array |
156
|
|
|
{ |
157
|
|
|
/** |
158
|
|
|
* @var array<string, array> $strings |
159
|
|
|
*/ |
160
|
4 |
|
$strings = $this->FilterArrayOfInterfaces($interfaces, ARRAY_FILTER_USE_KEY); |
161
|
|
|
|
162
|
4 |
|
return array_filter($strings, 'is_array'); |
163
|
|
|
} |
164
|
|
|
|
165
|
4 |
|
private function MakeMethodFilter(string $interface) : Closure |
166
|
|
|
{ |
167
|
|
|
return function (string $maybe) use ($interface) : bool { |
168
|
4 |
|
$ref = new ReflectionClass($interface); |
169
|
|
|
|
170
|
|
|
return |
171
|
4 |
|
$ref->hasMethod($maybe) && |
172
|
4 |
|
$this->FilterReflectionMethod($ref->getMethod($maybe)); |
173
|
4 |
|
}; |
174
|
|
|
} |
175
|
|
|
|
176
|
4 |
|
private function FilterReflectionMethod(ReflectionMethod $refMethod) : bool |
177
|
|
|
{ |
178
|
|
|
return |
179
|
4 |
|
$refMethod->isStatic() && |
180
|
4 |
|
$refMethod->isPublic() && |
181
|
4 |
|
0 === $refMethod->getNumberOfRequiredParameters() && |
182
|
4 |
|
$this->FilterReflectionReturnType($refMethod->getReturnType()); |
183
|
|
|
} |
184
|
|
|
|
185
|
4 |
|
private function FilterReflectionReturnType(? ReflectionType $refReturn) : bool |
186
|
|
|
{ |
187
|
4 |
|
$refReturnName = is_null($refReturn) ? '' : $refReturn->__toString(); |
|
|
|
|
188
|
|
|
|
189
|
4 |
|
return 'array' === $refReturnName || is_a($refReturnName, Traversable::class, true); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @return array<string, string[]> |
194
|
|
|
*/ |
195
|
4 |
|
private function FilterMethods(string $interface, array $methods) : array |
196
|
|
|
{ |
197
|
|
|
/** |
198
|
|
|
* @var array<string, string[]> |
199
|
|
|
*/ |
200
|
4 |
|
$filteredMethods = $this->FilterNonZeroArray(array_map( |
201
|
4 |
|
[$this, 'FilterArrayOfInterfaces'], |
202
|
4 |
|
array_filter( |
203
|
4 |
|
array_filter($methods, 'is_string', ARRAY_FILTER_USE_KEY), |
204
|
4 |
|
$this->MakeMethodFilter($interface), |
205
|
4 |
|
ARRAY_FILTER_USE_KEY |
206
|
|
|
) |
207
|
|
|
)); |
208
|
|
|
|
209
|
4 |
|
return $filteredMethods; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* @var array[] |
214
|
|
|
*/ |
215
|
4 |
|
private function FilterNonZeroArray(array $in) : array |
216
|
|
|
{ |
217
|
4 |
|
return array_filter( |
218
|
4 |
|
$in, |
219
|
|
|
function (array $val) : bool { |
220
|
4 |
|
return count($val) > 0; |
221
|
4 |
|
} |
222
|
|
|
); |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.