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, array<int, 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 $autoResetProcessedSources; |
43
|
|
|
|
44
|
4 |
|
public function __construct( |
45
|
|
|
array $staticMethods, |
46
|
|
|
array $interfaces, |
47
|
|
|
bool $autoResetProcessedSources = true |
48
|
|
|
) { |
49
|
|
|
/** |
50
|
|
|
* @var string $interface |
51
|
|
|
* @var array<string, array<int, string>> $methods |
52
|
|
|
*/ |
53
|
|
|
foreach ( |
54
|
4 |
|
array_filter( |
55
|
4 |
|
$staticMethods, |
56
|
4 |
|
[$this, 'shouldContainInterfaces'], |
57
|
4 |
|
ARRAY_FILTER_USE_KEY |
58
|
|
|
) as $interface => $methods |
59
|
|
|
) { |
60
|
4 |
|
$ref = new ReflectionClass($interface); |
61
|
|
|
|
62
|
4 |
|
$filteredMethods = $this->FilterMethods($ref, $methods); |
63
|
4 |
|
if (count($filteredMethods) > 0) { |
64
|
4 |
|
$this->staticMethods[$interface] = $filteredMethods; |
65
|
|
|
} |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var string[] $filteredInterfaces |
70
|
|
|
*/ |
71
|
4 |
|
$filteredInterfaces = array_filter($interfaces, [$this, 'shouldContainInterfaces']); |
72
|
|
|
|
73
|
4 |
|
$this->interfaces = $filteredInterfaces; |
74
|
|
|
|
75
|
4 |
|
$this->autoResetProcessedSources = $autoResetProcessedSources; |
76
|
4 |
|
} |
77
|
|
|
|
78
|
4 |
|
public function Collect(string ...$implementations) : Generator |
79
|
|
|
{ |
80
|
4 |
|
if ($this->autoResetProcessedSources) { |
81
|
2 |
|
$this->processedSources = []; |
82
|
2 |
|
$this->alreadyYielded = []; |
83
|
|
|
} |
84
|
|
|
|
85
|
4 |
|
yield from $this->CollectInterfaces(...$implementations); |
86
|
4 |
|
} |
87
|
|
|
|
88
|
4 |
|
protected function CollectInterfaces(string ...$implementations) : Generator |
89
|
|
|
{ |
90
|
|
|
/** |
91
|
|
|
* @var string[] $interfaces |
92
|
|
|
*/ |
93
|
4 |
|
$interfaces = array_keys($this->staticMethods); |
94
|
4 |
|
foreach (array_filter($implementations, 'class_exists') as $implementation) { |
95
|
|
|
if ( |
96
|
4 |
|
in_array($implementation, $this->processedSources, true) || |
97
|
4 |
|
in_array($implementation, $this->alreadyYielded, true) |
98
|
|
|
) { |
99
|
2 |
|
continue; |
100
|
|
|
} |
101
|
4 |
|
$this->processedSources[] = $implementation; |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @var string $interface |
105
|
|
|
*/ |
106
|
4 |
|
foreach ($this->interfaces as $interface) { |
107
|
4 |
|
if (is_a($implementation, $interface, true)) { |
108
|
|
|
yield $implementation; |
109
|
|
|
$this->alreadyYielded[] = $implementation; |
110
|
4 |
|
break; |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|
114
|
4 |
|
foreach ($interfaces as $interface) { |
115
|
4 |
|
if (is_a($implementation, $interface, true)) { |
116
|
|
|
/** |
117
|
|
|
* @var array<int, string> $types |
118
|
|
|
*/ |
119
|
4 |
|
foreach ($this->staticMethods[$interface] as $method => $types) { |
120
|
|
|
/** |
121
|
|
|
* @var iterable<string> $methodResult |
122
|
|
|
*/ |
123
|
4 |
|
$methodResult = $implementation::$method(); |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @var string $result |
127
|
|
|
*/ |
128
|
4 |
|
foreach ($methodResult as $result) { |
129
|
4 |
|
if (in_array($result, $this->alreadyYielded, true)) { |
130
|
|
|
continue; |
131
|
|
|
} |
132
|
4 |
|
foreach ($types as $type) { |
133
|
4 |
|
if (is_a($result, $type, true)) { |
134
|
4 |
|
yield $result; |
135
|
4 |
|
$this->alreadyYielded[] = $result; |
136
|
4 |
|
continue; |
137
|
|
|
} |
138
|
|
|
} |
139
|
4 |
|
foreach ($interfaces as $checkResultWithInterface) { |
140
|
4 |
|
if (is_a($result, $checkResultWithInterface, true)) { |
141
|
4 |
|
yield from $this->CollectInterfaces($result); |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
} |
149
|
4 |
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* @param mixed $maybe |
153
|
|
|
*/ |
154
|
4 |
|
protected function shouldContainInterfaces($maybe) : bool |
155
|
|
|
{ |
156
|
4 |
|
return is_string($maybe) && interface_exists($maybe); |
157
|
|
|
} |
158
|
|
|
|
159
|
4 |
|
private function MakeMethodFilter(ReflectionClass $ref) : Closure |
160
|
|
|
{ |
161
|
|
|
return function (string $maybe) use ($ref) : bool { |
162
|
|
|
return |
163
|
4 |
|
$ref->hasMethod($maybe) && |
164
|
4 |
|
$this->FilterReflectionMethod($ref->getMethod($maybe)); |
165
|
4 |
|
}; |
166
|
|
|
} |
167
|
|
|
|
168
|
4 |
|
private function FilterReflectionMethod(ReflectionMethod $refMethod) : bool |
169
|
|
|
{ |
170
|
4 |
|
$refReturn = $refMethod->getReturnType(); |
171
|
|
|
|
172
|
|
|
return |
173
|
4 |
|
$refMethod->isStatic() && |
174
|
4 |
|
$refMethod->isPublic() && |
175
|
4 |
|
0 === $refMethod->getNumberOfRequiredParameters() && |
176
|
4 |
|
($refReturn instanceof ReflectionType) && |
177
|
4 |
|
$this->FilterReflectionReturnType($refReturn); |
178
|
|
|
} |
179
|
|
|
|
180
|
4 |
|
private function FilterReflectionReturnType(ReflectionType $refReturn) : bool |
181
|
|
|
{ |
182
|
4 |
|
$refReturnName = $refReturn->__toString(); |
|
|
|
|
183
|
|
|
|
184
|
4 |
|
return 'array' === $refReturnName || is_a($refReturnName, Traversable::class, true); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* @param array<string, array<int, string>> $methods |
189
|
|
|
* |
190
|
|
|
* @return array<string, array<int, string>> |
191
|
|
|
*/ |
192
|
4 |
|
private function FilterMethods(ReflectionClass $ref, array $methods) : array |
193
|
|
|
{ |
194
|
4 |
|
$filteredMethods = []; |
195
|
|
|
|
196
|
|
|
foreach ( |
197
|
4 |
|
array_filter( |
198
|
4 |
|
array_filter($methods, 'is_string', ARRAY_FILTER_USE_KEY), |
199
|
4 |
|
$this->MakeMethodFilter($ref), |
200
|
4 |
|
ARRAY_FILTER_USE_KEY |
201
|
|
|
) as $method => $interfaces |
202
|
|
|
) { |
203
|
4 |
|
$methodInterfaces = array_filter($interfaces, [$this, 'shouldContainInterfaces']); |
204
|
|
|
|
205
|
4 |
|
if (count($methodInterfaces) > 0) { |
206
|
4 |
|
$filteredMethods[$method] = $interfaces; |
207
|
|
|
} |
208
|
|
|
} |
209
|
|
|
|
210
|
4 |
|
return $filteredMethods; |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
|
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.