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