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