1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Spiral Framework. |
4
|
|
|
* |
5
|
|
|
* @license MIT |
6
|
|
|
* @author Anton Titov (Wolfy-J) |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Spiral\Core; |
10
|
|
|
|
11
|
|
|
use Interop\Container\ContainerInterface; |
12
|
|
|
use ReflectionFunctionAbstract as ContextFunction; |
13
|
|
|
use Spiral\Core\Container\Autowire; |
14
|
|
|
use Spiral\Core\Container\InjectableInterface; |
15
|
|
|
use Spiral\Core\Container\InjectorInterface; |
16
|
|
|
use Spiral\Core\Container\SingletonInterface; |
17
|
|
|
use Spiral\Core\Exceptions\Container\ArgumentException; |
18
|
|
|
use Spiral\Core\Exceptions\Container\AutowireException; |
19
|
|
|
use Spiral\Core\Exceptions\Container\ContainerException; |
20
|
|
|
use Spiral\Core\Exceptions\Container\InjectionException; |
21
|
|
|
use Spiral\Core\Exceptions\Container\NotFoundException; |
22
|
|
|
use Spiral\Core\Exceptions\LogicException; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Auto-wiring container: declarative singletons, contextual injections, outer delegation and |
26
|
|
|
* ability to lazy wire. |
27
|
|
|
* |
28
|
|
|
* Container does not support setter injections, private properties and etc. Normally it will work |
29
|
|
|
* with classes only to be as much invisible as possible. Attention, this is hungry implementation |
30
|
|
|
* of container, meaning it WILL try to resolve dependency unless you specified custom lazy factory. |
31
|
|
|
* |
32
|
|
|
* You can use injectors to delegate class resolution to external container. |
33
|
|
|
* |
34
|
|
|
* @see \Spiral\Core\Container::registerInstance() to add your own behaviours. |
35
|
|
|
* |
36
|
|
|
* @see InjectableInterface |
37
|
|
|
* @see SingletonInterface |
38
|
|
|
*/ |
39
|
|
|
class Container extends Component implements ContainerInterface, FactoryInterface, ResolverInterface |
40
|
|
|
{ |
41
|
|
|
/** |
42
|
|
|
* Outer container responsible for low level dependency configuration (i.e. config based). |
43
|
|
|
* |
44
|
|
|
* @var ContainerInterface |
45
|
|
|
*/ |
46
|
|
|
private $outerContainer = null; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* IoC bindings. |
50
|
|
|
* |
51
|
|
|
* @what-if private |
52
|
|
|
* @invisible |
53
|
|
|
* |
54
|
|
|
* @var array |
55
|
|
|
*/ |
56
|
|
|
protected $bindings = [ |
57
|
|
|
ContainerInterface::class => self::class, |
58
|
|
|
FactoryInterface::class => self::class, |
59
|
|
|
ResolverInterface::class => self::class |
60
|
|
|
]; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* List of classes responsible for handling specific instance or interface. Provides ability to |
64
|
|
|
* delegate container functionality. |
65
|
|
|
* |
66
|
|
|
* @what-if private |
67
|
|
|
* @invisible |
68
|
|
|
* |
69
|
|
|
* @var array |
70
|
|
|
*/ |
71
|
|
|
protected $injectors = []; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Provide outer container in order to proxy get and has requests. |
75
|
|
|
* |
76
|
|
|
* @param ContainerInterface|null $outerContainer |
77
|
|
|
*/ |
78
|
61 |
|
public function __construct(ContainerInterface $outerContainer = null) |
79
|
|
|
{ |
80
|
61 |
|
$this->outerContainer = $outerContainer; |
81
|
61 |
|
$this->bindings[static::class] = self::class; |
82
|
61 |
|
$this->bindings[self::class] = $this; |
83
|
61 |
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Container can not be cloned. |
87
|
|
|
*/ |
88
|
1 |
|
public function __clone() |
89
|
|
|
{ |
90
|
1 |
|
throw new LogicException("Container is not clonable"); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* {@inheritdoc} |
95
|
|
|
*/ |
96
|
6 |
|
public function has($alias) |
97
|
|
|
{ |
98
|
6 |
|
if ($this->outerContainer !== null && $this->outerContainer->has($alias)) { |
99
|
1 |
|
return true; |
100
|
|
|
} |
101
|
|
|
|
102
|
5 |
|
return array_key_exists($alias, $this->bindings); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* {@inheritdoc} |
107
|
|
|
* |
108
|
|
|
* Context parameter will be passed to class injectors, which makes possible to use this method |
109
|
|
|
* as: |
110
|
|
|
* $this->container->get(DatabaseInterface::class, 'default'); |
111
|
|
|
* |
112
|
|
|
* Attention, context ignored when outer container has instance by alias. |
113
|
|
|
* |
114
|
|
|
* @param string|null $context Call context. |
115
|
|
|
* |
116
|
|
|
* @throws ContainerException |
117
|
|
|
*/ |
118
|
35 |
|
public function get($alias, string $context = null) |
119
|
|
|
{ |
120
|
35 |
|
if ($this->outerContainer !== null && $this->outerContainer->has($alias)) { |
121
|
1 |
|
return $this->outerContainer->get($alias); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
if ($alias instanceof Autowire) { |
125
|
34 |
|
return $alias->resolve($this); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
//Direct bypass to construct, i might think about this option... or not. |
129
|
|
|
return $this->make($alias, [], $context); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
49 |
|
* {@inheritdoc} |
134
|
|
|
* |
135
|
49 |
|
* @param string|null $context Related to parameter caused injection if any. |
136
|
|
|
*/ |
137
|
39 |
|
final public function make(string $alias, $parameters = [], string $context = null) |
138
|
|
|
{ |
139
|
|
|
if (!isset($this->bindings[$alias])) { |
140
|
31 |
|
//No direct instructions how to construct class, make is automatically |
141
|
|
|
return $this->autowire($alias, $parameters, $context); |
142
|
16 |
|
} |
143
|
|
|
|
144
|
|
|
if (is_object($binding = $this->bindings[$alias])) { |
145
|
21 |
|
//When binding is instance, assuming singleton |
146
|
|
|
return $binding; |
147
|
11 |
|
} |
148
|
|
|
|
149
|
|
|
if (is_string($binding)) { |
150
|
11 |
|
//Binding is pointing to something else |
151
|
|
|
return $this->make($binding, $parameters, $context); |
152
|
1 |
|
} |
153
|
11 |
|
|
154
|
7 |
|
$instance = $this->evaluateBinding($alias, $binding[0], $parameters, $context); |
155
|
|
|
|
156
|
|
|
if ($binding[1]) { |
157
|
7 |
|
//Indicates singleton |
158
|
7 |
|
$this->bindings[$alias] = $instance; |
159
|
|
|
} |
160
|
5 |
|
|
161
|
|
|
return $instance; |
162
|
4 |
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
4 |
|
* {@inheritdoc} |
166
|
4 |
|
* |
167
|
4 |
|
* @param string $context |
168
|
|
|
*/ |
169
|
|
|
final public function resolveArguments( |
170
|
4 |
|
ContextFunction $reflection, |
171
|
|
|
array $parameters = [], |
172
|
4 |
|
string $context = null |
173
|
|
|
): array { |
174
|
|
|
$arguments = []; |
175
|
|
|
foreach ($reflection->getParameters() as $parameter) { |
176
|
1 |
|
try { |
177
|
|
|
//Information we need to know about argument in order to resolve it's value |
178
|
|
|
$name = $parameter->getName(); |
179
|
10 |
|
$class = $parameter->getClass(); |
180
|
|
|
} catch (\Throwable $e) { |
181
|
5 |
|
//Possibly invalid class definition or syntax error |
182
|
|
|
throw new ContainerException($e->getMessage(), $e->getCode(), $e); |
183
|
|
|
} |
184
|
10 |
|
|
185
|
|
|
if (isset($parameters[$name]) && is_object($parameters[$name])) { |
186
|
2 |
|
if ($parameters[$name] instanceof Autowire) { |
187
|
|
|
//Supplied by user as late dependency |
188
|
|
|
$arguments[] = $parameters[$name]->resolve($this); |
189
|
8 |
|
} else { |
190
|
|
|
//Supplied by user as object |
191
|
|
|
$arguments[] = $parameters[$name]; |
192
|
|
|
} |
193
|
|
|
continue; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
//No declared type or scalar type or array |
197
|
29 |
|
if (empty($class)) { |
198
|
|
|
//Provided from outside |
199
|
|
|
if (array_key_exists($name, $parameters)) { |
200
|
|
|
//Make sure it's properly typed |
201
|
|
|
$this->assertType($parameter, $reflection, $parameters[$name]); |
202
|
29 |
|
$arguments[] = $parameters[$name]; |
203
|
29 |
|
continue; |
204
|
|
|
} |
205
|
|
|
|
206
|
24 |
|
if ($parameter->isDefaultValueAvailable()) { |
207
|
24 |
|
//Default value |
208
|
1 |
|
$arguments[] = $parameter->getDefaultValue(); |
209
|
|
|
continue; |
210
|
1 |
|
} |
211
|
|
|
|
212
|
|
|
//Unable to resolve scalar argument value |
213
|
23 |
|
throw new ArgumentException($parameter, $reflection); |
214
|
1 |
|
} |
215
|
|
|
|
216
|
1 |
|
try { |
217
|
|
|
//Requesting for contextual dependency |
218
|
|
|
$arguments[] = $this->get($class->getName(), $name); |
219
|
|
|
|
220
|
|
|
continue; |
221
|
1 |
|
} catch (AutowireException $e) { |
222
|
|
|
if ($parameter->isOptional()) { |
223
|
|
|
//This is optional dependency, skip |
224
|
|
|
$arguments[] = null; |
225
|
23 |
|
continue; |
226
|
|
|
} |
227
|
21 |
|
|
228
|
|
|
throw $e; |
229
|
17 |
|
} |
230
|
16 |
|
} |
231
|
16 |
|
|
232
|
|
|
return $arguments; |
233
|
|
|
} |
234
|
9 |
|
|
235
|
|
|
/** |
236
|
8 |
|
* Bind value resolver to container alias. Resolver can be class name (will be constructed |
237
|
8 |
|
* for each method call), function array or Closure (executed every call). Only object resolvers |
238
|
|
|
* supported by this method. |
239
|
|
|
* |
240
|
|
|
* @param string $alias |
241
|
1 |
|
* @param string|array|callable $resolver |
242
|
|
|
* |
243
|
|
|
* @return self |
244
|
|
|
*/ |
245
|
|
|
final public function bind(string $alias, $resolver): Container |
246
|
7 |
|
{ |
247
|
|
|
if (is_array($resolver) || $resolver instanceof \Closure || $resolver instanceof Autowire) { |
248
|
5 |
|
//Array means = execute me, false = not singleton |
249
|
2 |
|
$this->bindings[$alias] = [$resolver, false]; |
250
|
2 |
|
|
251
|
|
|
return $this; |
252
|
1 |
|
} |
253
|
1 |
|
|
254
|
|
|
$this->bindings[$alias] = $resolver; |
255
|
|
|
|
256
|
1 |
|
return $this; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
21 |
|
* Bind value resolver to container alias to be executed as cached. Resolver can be class name |
261
|
|
|
* (will be constructed only once), function array or Closure (executed only once call). |
262
|
|
|
* |
263
|
|
|
* @param string $alias |
264
|
|
|
* @param string|array|callable $resolver |
265
|
|
|
* |
266
|
|
|
* @return self |
267
|
|
|
*/ |
268
|
|
|
final public function bindSingleton(string $alias, $resolver): Container |
269
|
|
|
{ |
270
|
|
|
if (is_object($resolver) && !$resolver instanceof \Closure && !$resolver instanceof Autowire) { |
271
|
|
|
//Direct binding to an instance |
272
|
|
|
$this->bindings[$alias] = $resolver; |
273
|
25 |
|
|
274
|
|
|
return $this; |
275
|
25 |
|
} |
276
|
|
|
|
277
|
7 |
|
$this->bindings[$alias] = [$resolver, true]; |
278
|
|
|
|
279
|
7 |
|
return $this; |
280
|
|
|
} |
281
|
|
|
|
282
|
19 |
|
/** |
283
|
|
|
* Specify binding which has to be used for class injection. |
284
|
19 |
|
* |
285
|
|
|
* @param string $class |
286
|
|
|
* @param string|object $injector |
287
|
|
|
* |
288
|
|
|
* @return self |
289
|
|
|
*/ |
290
|
|
|
public function bindInjector(string $class, $injector): Container |
291
|
|
|
{ |
292
|
|
|
if (!is_string($injector)) { |
293
|
|
|
throw new \InvalidArgumentException('Injector can only be set as string binding'); |
294
|
|
|
} |
295
|
|
|
|
296
|
9 |
|
$this->injectors[$class] = $injector; |
297
|
|
|
|
298
|
9 |
|
return $this; |
299
|
|
|
} |
300
|
4 |
|
|
301
|
|
|
/** |
302
|
4 |
|
* Check if given class has associated injector. |
303
|
|
|
* |
304
|
|
|
* @param \ReflectionClass $reflection |
305
|
5 |
|
* |
306
|
|
|
* @return bool |
307
|
5 |
|
*/ |
308
|
|
|
public function hasInjector(\ReflectionClass $reflection): bool |
309
|
|
|
{ |
310
|
|
|
if (isset($this->injectors[$reflection->getName()])) { |
311
|
|
|
return true; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
//Auto injection! |
315
|
|
|
return $reflection->isSubclassOf(InjectableInterface::class); |
316
|
|
|
} |
317
|
|
|
|
318
|
5 |
|
/** |
319
|
|
|
* Check if alias points to constructed instance (singleton). |
320
|
5 |
|
* |
321
|
1 |
|
* @param string $alias |
322
|
|
|
* |
323
|
|
|
* @return bool |
324
|
4 |
|
*/ |
325
|
|
|
final public function hasInstance(string $alias): bool |
326
|
4 |
|
{ |
327
|
|
|
if (!$this->has($alias)) { |
328
|
|
|
return false; |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
while (isset($this->bindings[$alias]) && is_string($this->bindings[$alias])) { |
332
|
|
|
//Checking alias tree |
333
|
|
|
$alias = $this->bindings[$alias]; |
334
|
|
|
} |
335
|
|
|
|
336
|
25 |
|
return isset($this->bindings[$alias]) && is_object($this->bindings[$alias]); |
337
|
|
|
} |
338
|
25 |
|
|
339
|
3 |
|
/** |
340
|
|
|
* @param string $alias |
341
|
|
|
*/ |
342
|
|
|
final public function removeBinding(string $alias) |
343
|
23 |
|
{ |
344
|
|
|
unset($this->bindings[$alias]); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* @param string $class |
349
|
|
|
*/ |
350
|
|
|
final public function removeInjector(string $class) |
351
|
|
|
{ |
352
|
|
|
unset($this->injectors[$class]); |
353
|
2 |
|
} |
354
|
|
|
|
355
|
2 |
|
/** |
356
|
2 |
|
* Every declared Container binding. Must not be used in production code due container format is |
357
|
|
|
* vary. |
358
|
|
|
* |
359
|
2 |
|
* @return array |
360
|
|
|
*/ |
361
|
1 |
|
final public function getBindings(): array |
362
|
|
|
{ |
363
|
|
|
return $this->bindings; |
364
|
2 |
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Every binded injector. |
368
|
|
|
* |
369
|
|
|
* @return array |
370
|
1 |
|
*/ |
371
|
|
|
final public function getInjectors(): array |
372
|
1 |
|
{ |
373
|
1 |
|
return $this->injectors; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
/** |
377
|
|
|
* Automatically create class. |
378
|
1 |
|
* |
379
|
|
|
* @param string $class |
380
|
1 |
|
* @param array $parameters |
381
|
1 |
|
* @param string $context |
382
|
|
|
* |
383
|
|
|
* @return object |
384
|
|
|
* |
385
|
|
|
* @throws AutowireException |
386
|
|
|
*/ |
387
|
|
|
final protected function autowire(string $class, array $parameters, string $context = null) |
388
|
|
|
{ |
389
|
1 |
|
try { |
390
|
|
|
if (!class_exists($class)) { |
391
|
1 |
|
throw new NotFoundException("Undefined class or binding '{$class}'"); |
392
|
|
|
} |
393
|
|
|
} catch (\Error $e) { |
394
|
|
|
//Issues with syntax or class definition |
395
|
|
|
throw new ContainerException($e->getMessage(), $e->getCode(), $e); |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
//Automatically create instance |
399
|
1 |
|
$instance = $this->createInstance($class, $parameters, $context); |
400
|
|
|
|
401
|
1 |
|
//Apply registration functions to created instance |
402
|
|
|
return $this->registerInstance($instance, $parameters); |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* Register instance in container, might perform methods like auto-singletons, log populations |
407
|
|
|
* and etc. Can be extended. |
408
|
|
|
* |
409
|
|
|
* @param object $instance Created object. |
410
|
|
|
* @param array $parameters Parameters which been passed with created instance. |
411
|
|
|
* |
412
|
|
|
* @return object |
413
|
|
|
*/ |
414
|
|
|
protected function registerInstance($instance, array $parameters) |
415
|
39 |
|
{ |
416
|
|
|
//Declarative singletons (only when class received via direct get) |
417
|
|
|
if (empty($parameters) && $instance instanceof SingletonInterface) { |
418
|
39 |
|
$alias = get_class($instance); |
419
|
38 |
|
|
420
|
|
|
if (!isset($this->bindings[$alias])) { |
421
|
6 |
|
$this->bindings[$alias] = $instance; |
422
|
|
|
} |
423
|
1 |
|
} |
424
|
|
|
|
425
|
|
|
//Your code can go here (for example LoggerAwareInterface, custom hydration and etc) |
426
|
|
|
|
427
|
38 |
|
return $instance; |
428
|
|
|
} |
429
|
|
|
|
430
|
25 |
|
/** |
431
|
|
|
* @param string $alias |
432
|
|
|
* @param mixed $target Value binded by user. |
433
|
|
|
* @param array $parameters |
434
|
|
|
* @param string|null $context |
435
|
|
|
* |
436
|
|
|
* @return mixed|null|object |
437
|
|
|
* |
438
|
|
|
* @throws \Spiral\Core\Exceptions\Container\ContainerException |
439
|
|
|
*/ |
440
|
|
|
private function evaluateBinding( |
441
|
|
|
string $alias, |
442
|
25 |
|
$target, |
443
|
|
|
array $parameters, |
444
|
|
|
string $context = null |
445
|
25 |
|
) { |
446
|
1 |
|
if (is_string($target)) { |
447
|
|
|
//Reference |
448
|
1 |
|
return $this->make($target, $parameters, $context); |
449
|
1 |
|
} |
450
|
|
|
|
451
|
|
|
if ($target instanceof Autowire) { |
452
|
|
|
return $target->resolve($this, $parameters); |
453
|
|
|
} |
454
|
|
|
|
455
|
25 |
|
if ($target instanceof \Closure) { |
456
|
|
|
$reflection = new \ReflectionFunction($target); |
457
|
|
|
|
458
|
|
|
//Invoking Closure with resolved arguments |
459
|
|
|
return $reflection->invokeArgs( |
460
|
|
|
$this->resolveArguments($reflection, $parameters, $context) |
461
|
|
|
); |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
if (is_array($target) && isset($target[1])) { |
465
|
|
|
//In a form of resolver and method |
466
|
|
|
list($resolver, $method) = $target; |
467
|
|
|
|
468
|
|
|
//Resolver instance (i.e. [ClassName::class, 'method']) |
|
|
|
|
469
|
38 |
|
$resolver = $this->get($resolver); |
470
|
|
|
$method = new \ReflectionMethod($resolver, $method); |
471
|
38 |
|
$method->setAccessible(true); |
472
|
|
|
|
473
|
|
|
//Invoking factory method with resolved arguments |
474
|
38 |
|
return $method->invokeArgs( |
475
|
11 |
|
$resolver, |
476
|
|
|
$this->resolveArguments($method, $parameters, $context) |
477
|
6 |
|
); |
478
|
1 |
|
} |
479
|
|
|
|
480
|
|
|
throw new ContainerException("Invalid binding for '{$alias}'"); |
481
|
5 |
|
} |
482
|
|
|
|
483
|
|
|
/** |
484
|
28 |
|
* Create instance of desired class. |
485
|
1 |
|
* |
486
|
|
|
* @param string $class |
487
|
|
|
* @param array $parameters Constructor parameters. |
488
|
27 |
|
* @param string|null $context |
489
|
|
|
* |
490
|
21 |
|
* @return object |
491
|
21 |
|
* |
492
|
|
|
* @throws ContainerException |
493
|
|
|
*/ |
494
|
|
|
private function createInstance(string $class, array $parameters, string $context = null) |
495
|
8 |
|
{ |
496
|
|
|
$reflection = new \ReflectionClass($class); |
497
|
|
|
|
498
|
20 |
|
//We have to construct class using external injector when we know exact context |
499
|
|
|
if (empty($parameters) && $this->hasInjector($reflection)) { |
500
|
|
|
$instance = $this->getInjector($reflection)->createInjection($reflection, $context); |
501
|
|
|
|
502
|
|
|
if (!$reflection->isInstance($instance)) { |
503
|
|
|
throw new InjectionException("Invalid injection response for '{$reflection->getName()}'"); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
return $instance; |
507
|
|
|
} |
508
|
11 |
|
|
509
|
|
|
if (!$reflection->isInstantiable()) { |
510
|
11 |
|
throw new ContainerException("Class '{$class}' can not be constructed"); |
511
|
|
|
} |
512
|
3 |
|
|
513
|
|
|
if (!empty($constructor = $reflection->getConstructor())) { |
514
|
|
|
//Using constructor with resolved arguments |
515
|
8 |
|
$instance = $reflection->newInstanceArgs( |
516
|
|
|
$this->resolveArguments($constructor, $parameters) |
517
|
|
|
); |
518
|
8 |
|
} else { |
519
|
2 |
|
//No constructor specified |
520
|
2 |
|
$instance = $reflection->newInstance(); |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
return $instance; |
524
|
6 |
|
} |
525
|
|
|
|
526
|
|
|
/** |
527
|
|
|
* Get injector associated with given class. |
528
|
|
|
* |
529
|
|
|
* @param \ReflectionClass $reflection |
530
|
|
|
* |
531
|
|
|
* @return InjectorInterface |
532
|
|
|
*/ |
533
|
|
|
private function getInjector(\ReflectionClass $reflection): InjectorInterface |
534
|
|
|
{ |
535
|
|
|
if (isset($this->injectors[$reflection->getName()])) { |
536
|
17 |
|
//Stated directly |
537
|
|
|
$injector = $this->get($this->injectors[$reflection->getName()]); |
538
|
|
|
} else { |
539
|
|
|
//Auto-injection! |
540
|
|
|
$injector = $this->get($reflection->getConstant('INJECTOR')); |
541
|
17 |
|
} |
542
|
2 |
|
|
543
|
1 |
|
if (!$injector instanceof InjectorInterface) { |
544
|
|
|
throw new InjectionException( |
545
|
|
|
"Class '" . get_class($injector) . "' must be an instance of InjectorInterface for '{$reflection->getName()}'" |
546
|
1 |
|
); |
547
|
|
|
} |
548
|
|
|
|
549
|
16 |
|
return $injector; |
550
|
|
|
} |
551
|
16 |
|
|
552
|
1 |
|
/** |
553
|
|
|
* Assert that given value are matched parameter type. |
554
|
|
|
* |
555
|
16 |
|
* @param \ReflectionParameter $parameter |
556
|
2 |
|
* @param \ReflectionFunctionAbstract $context |
557
|
|
|
* @param mixed $value |
558
|
|
|
* |
559
|
16 |
|
* @throws ArgumentException |
560
|
1 |
|
*/ |
561
|
|
|
private function assertType( |
562
|
16 |
|
\ReflectionParameter $parameter, |
563
|
|
|
\ReflectionFunctionAbstract $context, |
564
|
|
|
$value |
565
|
|
|
) { |
566
|
|
|
if (is_null($value)) { |
567
|
|
|
if ( |
568
|
|
|
!$parameter->isOptional() |
569
|
|
|
&& !($parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() === null) |
570
|
|
|
) { |
571
|
|
|
throw new ArgumentException($parameter, $context); |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
return; |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
$type = $parameter->getType(); |
578
|
|
|
|
579
|
|
|
if ($type == 'array' && !is_array($value)) { |
580
|
|
|
throw new ArgumentException($parameter, $context); |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
if (($type == 'int' || $type == 'float') && !is_numeric($value)) { |
584
|
|
|
throw new ArgumentException($parameter, $context); |
585
|
|
|
} |
586
|
|
|
|
587
|
|
|
if ($type == 'bool' && !is_bool($value) && !is_numeric($value)) { |
588
|
|
|
throw new ArgumentException($parameter, $context); |
589
|
|
|
} |
590
|
|
|
} |
591
|
|
|
} |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.