1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
|
4
|
|
|
namespace ByJG\Config; |
5
|
|
|
|
6
|
|
|
use ByJG\Config\Exception\DependencyInjectionException; |
7
|
|
|
use ByJG\Config\Exception\KeyNotFoundException; |
8
|
|
|
use Psr\Container\ContainerInterface; |
9
|
|
|
use ReflectionClass; |
10
|
|
|
use ReflectionException; |
11
|
|
|
use ReflectionMethod; |
12
|
|
|
|
13
|
|
|
class DependencyInjection |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @var ContainerInterface |
17
|
|
|
*/ |
18
|
|
|
protected $containerInterface; |
19
|
|
|
|
20
|
|
|
protected $class; |
21
|
|
|
|
22
|
|
|
protected $args = []; |
23
|
|
|
|
24
|
|
|
protected $instance; |
25
|
|
|
|
26
|
|
|
protected $singleton = false; |
27
|
|
|
|
28
|
|
|
protected $factory = null; |
29
|
|
|
|
30
|
|
|
protected $methodCall = []; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @param $containerInterface ContainerInterface |
34
|
|
|
* @return DependencyInjection |
35
|
|
|
*/ |
36
|
|
|
public function injectContainer($containerInterface) |
37
|
|
|
{ |
38
|
|
|
$this->containerInterface = $containerInterface; |
39
|
|
|
return $this; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @return mixed |
44
|
|
|
*/ |
45
|
|
|
protected function getClass() |
46
|
|
|
{ |
47
|
|
|
return $this->class; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param mixed $class |
52
|
|
|
* @throws DependencyInjectionException |
53
|
|
|
*/ |
54
|
|
|
protected function setClass($class) |
55
|
|
|
{ |
56
|
|
|
if (!class_exists($class)) { |
57
|
|
|
throw new DependencyInjectionException("Class $class does not exists"); |
58
|
|
|
} |
59
|
|
|
$this->class = $class; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @return mixed |
64
|
|
|
*/ |
65
|
|
|
protected function getArgs() |
66
|
|
|
{ |
67
|
|
|
return array_map(function ($value) { |
68
|
|
|
if ($value instanceof Param) { |
69
|
|
|
try { |
70
|
|
|
return $this->containerInterface->get($value->getParam()); |
71
|
|
|
} catch (KeyNotFoundException $ex) { |
72
|
|
|
throw new KeyNotFoundException($ex->getMessage() . " injected from '" . $this->getClass() . "'"); |
73
|
|
|
} |
74
|
|
|
} |
75
|
|
|
return $value; |
76
|
|
|
}, $this->args); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @param mixed $args |
81
|
|
|
* @return DependencyInjection |
82
|
|
|
* @throws DependencyInjectionException |
83
|
|
|
*/ |
84
|
|
View Code Duplication |
public function withConstructorArgs($args) |
|
|
|
|
85
|
|
|
{ |
86
|
|
|
if (!is_null($args) && !is_array($args)) { |
87
|
|
|
throw new DependencyInjectionException("Arguments should be an array"); |
88
|
|
|
} |
89
|
|
|
$this->args = $args; |
|
|
|
|
90
|
|
|
|
91
|
|
|
return $this; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* @param mixed $args |
96
|
|
|
* @return DependencyInjection |
97
|
|
|
* @throws DependencyInjectionException |
98
|
|
|
*/ |
99
|
|
View Code Duplication |
public function withFactoryMethod($method, $args = []) |
|
|
|
|
100
|
|
|
{ |
101
|
|
|
if (!is_null($args) && !is_array($args)) { |
102
|
|
|
throw new DependencyInjectionException("Arguments should be an array"); |
103
|
|
|
} |
104
|
|
|
$this->args = $args; |
|
|
|
|
105
|
|
|
|
106
|
|
|
$this->factory = $method; |
107
|
|
|
|
108
|
|
|
return $this; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* DependencyInjection constructor. |
113
|
|
|
* @param $class |
114
|
|
|
* @throws DependencyInjectionException |
115
|
|
|
*/ |
116
|
|
|
protected function __construct($class) |
117
|
|
|
{ |
118
|
|
|
$this->setClass($class); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @param $class |
123
|
|
|
* @return DependencyInjection |
124
|
|
|
* @throws DependencyInjectionException |
125
|
|
|
*/ |
126
|
|
|
public static function bind($class) |
127
|
|
|
{ |
128
|
|
|
return new DependencyInjection($class); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* @return DependencyInjection |
133
|
|
|
* @throws DependencyInjectionException |
134
|
|
|
* @throws ReflectionException |
135
|
|
|
*/ |
136
|
|
|
public function withInjectedConstructor() |
137
|
|
|
{ |
138
|
|
|
$reflection = new ReflectionMethod($this->getClass(), "__construct"); |
139
|
|
|
$params = $reflection->getParameters(); |
140
|
|
|
|
141
|
|
|
if (count($params) > 0) { |
142
|
|
|
$args = []; |
143
|
|
|
foreach ($params as $param) { |
144
|
|
|
$type = $param->getType(); |
145
|
|
|
if (is_null($type)) { |
146
|
|
|
throw new DependencyInjectionException("The parameter '$" . $param->getName() . "' has no type defined in class '" . $this->getClass() . "'"); |
|
|
|
|
147
|
|
|
} |
148
|
|
|
$args[] = Param::get(ltrim($type, "\\")); |
149
|
|
|
} |
150
|
|
|
return $this->withConstructorArgs($args); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
return $this->withNoConstructor(); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* @return DependencyInjection |
158
|
|
|
* @throws DependencyInjectionException |
159
|
|
|
* @throws ReflectionException |
160
|
|
|
*/ |
161
|
|
|
public function withInjectedLegacyConstructor() |
162
|
|
|
{ |
163
|
|
|
$reflection = new ReflectionMethod($this->getClass(), "__construct"); |
164
|
|
|
|
165
|
|
|
$docComments = str_replace("\n", " ", $reflection->getDocComment()); |
166
|
|
|
|
167
|
|
|
$methodParams = $reflection->getParameters(); |
168
|
|
|
|
169
|
|
|
$params = []; |
170
|
|
|
$result = preg_match_all('/@param\s+([\d\w_\\\\]+)\s+\$[\w_\d]+/', $docComments, $params); |
171
|
|
|
|
172
|
|
|
if (count($methodParams) <> $result) { |
173
|
|
|
throw new DependencyInjectionException("The class " . $this->getClass() . " does not have annotations with the param type."); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
if (count($methodParams) > 0) { |
177
|
|
|
$args = []; |
178
|
|
|
foreach ($params[1] as $param) { |
179
|
|
|
$args[] = Param::get(ltrim($param, "\\")); |
180
|
|
|
} |
181
|
|
|
return $this->withConstructorArgs($args); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
return $this->withNoConstructor(); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* @return DependencyInjection |
189
|
|
|
*/ |
190
|
|
|
public function withNoConstructor() |
191
|
|
|
{ |
192
|
|
|
$this->args = null; |
|
|
|
|
193
|
|
|
return $this; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
public function withMethodCall($method, $args = []) |
197
|
|
|
{ |
198
|
|
|
$this->methodCall[] = [$method, $args]; |
199
|
|
|
return $this; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @return DependencyInjection |
204
|
|
|
*/ |
205
|
|
|
public function toSingleton() |
206
|
|
|
{ |
207
|
|
|
$this->singleton = true; |
208
|
|
|
return $this; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* @return DependencyInjection |
213
|
|
|
* @throws DependencyInjectionException |
214
|
|
|
* @throws ReflectionException |
215
|
|
|
*/ |
216
|
|
|
public function toEagerSingleton() |
217
|
|
|
{ |
218
|
|
|
$this->singleton = true; |
219
|
|
|
$this->getInstance(); |
220
|
|
|
return $this; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* @return DependencyInjection |
225
|
|
|
*/ |
226
|
|
|
public function toInstance() |
227
|
|
|
{ |
228
|
|
|
$this->singleton = false; |
229
|
|
|
return $this; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* @return object |
234
|
|
|
* @throws DependencyInjectionException |
235
|
|
|
* @throws ReflectionException |
236
|
|
|
*/ |
237
|
|
|
public function getInstance() |
238
|
|
|
{ |
239
|
|
|
$instance = $this->getInternalInstance(); |
240
|
|
|
|
241
|
|
|
if (is_null($instance)) { |
242
|
|
|
throw new DependencyInjectionException("Could not get a instance of " . $this->getClass()); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
return $instance; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @return object |
250
|
|
|
* @throws ReflectionException |
251
|
|
|
*/ |
252
|
|
|
protected function getInternalInstance() |
253
|
|
|
{ |
254
|
|
|
if ($this->singleton) { |
255
|
|
|
return $this->getSingletonInstace(); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
return $this->getNewInstance(); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* @return object |
263
|
|
|
* @throws ReflectionException |
264
|
|
|
*/ |
265
|
|
|
protected function getNewInstance() |
266
|
|
|
{ |
267
|
|
|
if (!empty($this->factory)) { |
268
|
|
|
return $this->callMethods(call_user_func_array([$this->getClass(), $this->factory], $this->getArgs())); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
$reflectionClass = new ReflectionClass($this->getClass()); |
272
|
|
|
|
273
|
|
|
if (is_null($this->args)) { |
274
|
|
|
return $this->callMethods($reflectionClass->newInstanceWithoutConstructor()); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
return $this->callMethods($reflectionClass->newInstanceArgs($this->getArgs())); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* @param $instance |
282
|
|
|
* @return mixed |
283
|
|
|
*/ |
284
|
|
|
protected function callMethods($instance) |
285
|
|
|
{ |
286
|
|
|
foreach ($this->methodCall as $methodDefinition) { |
287
|
|
|
if (is_null($methodDefinition[1])) { |
288
|
|
|
call_user_func([$instance, $methodDefinition[0]]); |
289
|
|
|
} else { |
290
|
|
|
call_user_func_array([$instance, $methodDefinition[0]], $methodDefinition[1]); |
291
|
|
|
} |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
return $instance; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* @return object |
299
|
|
|
* @throws ReflectionException |
300
|
|
|
*/ |
301
|
|
|
protected function getSingletonInstace() |
302
|
|
|
{ |
303
|
|
|
if (empty($this->instance)) { |
304
|
|
|
$this->instance = $this->getNewInstance(); |
305
|
|
|
} |
306
|
|
|
return $this->instance; |
307
|
|
|
} |
308
|
|
|
} |
309
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.