These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * slince dependency injection component |
||
4 | * @author Tao <[email protected]> |
||
5 | */ |
||
6 | namespace Slince\Di; |
||
7 | |||
8 | use Slince\Di\Exception\ConfigException; |
||
9 | use Slince\Di\Exception\DependencyInjectionException; |
||
10 | |||
11 | class Container |
||
12 | { |
||
13 | /** |
||
14 | * 所有需要分享的类及其实例 |
||
15 | * @var array |
||
16 | */ |
||
17 | protected $shares = []; |
||
18 | |||
19 | /** |
||
20 | * 预定义的依赖,支持instance、callable、Definition、class |
||
21 | * @var array |
||
22 | */ |
||
23 | protected $definitions = []; |
||
24 | |||
25 | /** |
||
26 | * 全局接口与类绑定关系 |
||
27 | * @var array |
||
28 | */ |
||
29 | protected $contextBindings = []; |
||
30 | |||
31 | /** |
||
32 | * 参数集合 |
||
33 | * @var ParameterStore |
||
34 | */ |
||
35 | protected $parameterStore; |
||
36 | |||
37 | public function __construct() |
||
38 | { |
||
39 | //全局参数存储 |
||
40 | $this->parameterStore = new ParameterStore(); |
||
41 | } |
||
42 | |||
43 | /** |
||
44 | * 给指定类或者别名指向类设置实例化向导 |
||
45 | * @param string $name |
||
46 | * @param string $class 类名 |
||
47 | * @param array $arguments 构造函数 |
||
48 | * @param array $methodCalls setter注入 |
||
49 | * @param array $properties 属性注入 |
||
50 | * @return Definition |
||
51 | */ |
||
52 | public function define($name, $class, array $arguments, array $methodCalls = [], array $properties = []) |
||
53 | { |
||
54 | $definition = new Definition($class, $arguments, $methodCalls, $properties); |
||
55 | $this->setDefinition($name, $definition); |
||
0 ignored issues
–
show
|
|||
56 | return $definition; |
||
57 | } |
||
58 | |||
59 | /** |
||
60 | * 设置指定类的实例化代理 |
||
61 | * @param string $name |
||
62 | * @param mixed $creation 闭包及其它合法可调用的语法结构 |
||
63 | * @throws ConfigException |
||
64 | * @return $this |
||
65 | */ |
||
66 | public function delegate($name, $creation) |
||
67 | { |
||
68 | if (!is_callable($creation)) { |
||
69 | throw new ConfigException(sprintf("Delegate expects a valid callable or executable class::method string at Argument 2")); |
||
70 | } |
||
71 | $this->definitions[$name] = $creation; |
||
72 | return $this; |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * 绑定实例 |
||
77 | * ``` |
||
78 | * $container->instance('user', $user); |
||
79 | * //或者直接提供实例 |
||
80 | * $container->instance($user); |
||
81 | * |
||
82 | * ``` |
||
83 | * @param $name |
||
84 | * @param $instance |
||
85 | * @throws ConfigException |
||
86 | * @return $this |
||
87 | */ |
||
88 | public function instance($name, $instance) |
||
89 | { |
||
90 | if (func_get_args() == 1) { |
||
91 | if (!is_object($name)) { |
||
92 | throw new ConfigException(sprintf("Instance expects a valid object")); |
||
93 | } |
||
94 | $instance = $name; |
||
95 | $name = get_class($instance); |
||
96 | } |
||
97 | $this->definitions[$name] = $instance; |
||
98 | $this->share($name); |
||
99 | return $this; |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * 将name直接绑定到某个指定存在的类(可用来绑定接口或者抽象类与实现类) |
||
104 | * @param string $name |
||
105 | * @param string $class 一个可被实例化的类名 |
||
106 | * @param string|array $context 为指定的上下文设置绑定指令 |
||
107 | * @throws ConfigException |
||
108 | * @return $this |
||
109 | */ |
||
110 | public function bind($name, $class, $context = null) |
||
111 | { |
||
112 | if (is_null($context)) { |
||
113 | $this->definitions[$name] = $class; |
||
114 | } else { |
||
115 | if (is_array($context)) { |
||
116 | list($contextClass, $contextMethod) = $context; |
||
117 | } else { |
||
118 | $contextClass = $context; |
||
119 | $contextMethod = 'general'; |
||
120 | } |
||
121 | isset($this->contextBindings[$contextClass][$contextMethod]) |
||
122 | || ($this->contextBindings[$contextClass][$contextMethod] = []); |
||
123 | $this->contextBindings[$contextClass][$contextMethod][$name] = $class; |
||
124 | } |
||
125 | return $this; |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * 给指定类或者类别名设置实例化指令 |
||
130 | * ``` |
||
131 | * //直接绑定实例 |
||
132 | * $container->set('student', $student); |
||
133 | * |
||
134 | * //绑定闭包或者其它可调用结构 |
||
135 | * $container->set('student', 'StudentFactory::create'); |
||
136 | * $container->set('student', function(){ |
||
137 | * return new Student(); |
||
138 | * }); |
||
139 | * |
||
140 | * //绑定预定义 |
||
141 | * $container->set('student', new Definition('Foo\Bar\StudentClass', [ |
||
142 | * 'gender' => 'boy', |
||
143 | * 'school' => new Reference('school') |
||
144 | * ], [ |
||
145 | * 'setAge' => [18] |
||
146 | * ], [ |
||
147 | * 'father' => 'James', |
||
148 | * 'mather' => 'Sophie' |
||
149 | * ])); |
||
150 | * |
||
151 | * //绑定到指定类 |
||
152 | * $container->set('student', Foo\Bar\StudentClass); |
||
153 | * ``` |
||
154 | * @param string $name |
||
155 | * @param mixed $definition |
||
156 | * @param boolean $share |
||
157 | * @throws ConfigException |
||
158 | * @return $this |
||
159 | */ |
||
160 | public function set($name, $definition, $share = false) |
||
161 | { |
||
162 | if (is_callable($definition)) { |
||
163 | $this->delegate($name, $definition); |
||
164 | $share && $this->share($name); |
||
165 | } elseif ($definition instanceof Definition) { |
||
166 | $this->setDefinition($name, $definition, $share); |
||
0 ignored issues
–
show
The method
Slince\Di\Container::setDefinition() has been deprecated with message: Will be protected, Use define & share or set instead.
This method has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead. ![]() |
|||
167 | } elseif (is_object($definition)) { |
||
168 | $this->instance($name, $definition); //如果$definition是实例的话则只能单例 |
||
169 | } elseif (is_string($definition)) { |
||
170 | $this->bind($name, $definition); |
||
171 | $share && $this->share($name); |
||
172 | } else { |
||
173 | throw new ConfigException(sprintf("Unexpected object definition type '%s'", gettype($definition))); |
||
174 | } |
||
175 | return $this; |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * 如果不能简单获取,则使用设置定义的方式 |
||
180 | * @param string $name |
||
181 | * @param Definition $definition |
||
182 | * @param boolean $share |
||
183 | * @return Definition |
||
184 | * @deprecated Will be protected, Use define & share or set instead. |
||
185 | */ |
||
186 | public function setDefinition($name, Definition $definition, $share = false) |
||
187 | { |
||
188 | $this->definitions[$name] = $definition; |
||
189 | $share && $this->share($name); |
||
190 | return $definition; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * 设置类或者类实例的分享 |
||
195 | * @param string $name |
||
196 | * @return $this |
||
197 | */ |
||
198 | public function share($name) |
||
199 | { |
||
200 | //兼容旧的api,给出移除提示 |
||
201 | if (func_num_args() == 2) { |
||
202 | trigger_error("Use set instead, Now share only expects one argument", E_USER_DEPRECATED); |
||
203 | $arguments = func_get_args(); |
||
204 | $arguments[] = true; |
||
205 | return call_user_func_array([$this, 'set'], $arguments); |
||
206 | } |
||
207 | $this->shares[$name] = null; |
||
208 | return $this; |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * 为指定类设置一个别名 |
||
213 | * @param string $alias |
||
214 | * @param string $original |
||
215 | * @return $this |
||
216 | * @deprecated duplication,use bind instead |
||
217 | */ |
||
218 | public function alias($alias, $original) |
||
219 | { |
||
220 | return $this->bind($alias, $original); |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * 以获取一个实例 |
||
225 | * @param string $name |
||
226 | * @param array $arguments 传递给类的构造参数,会覆盖预先定义的同名参数 |
||
227 | * @return object |
||
228 | */ |
||
229 | public function get($name, $arguments = []) |
||
230 | { |
||
231 | //兼容旧的api |
||
232 | if (is_bool($arguments)) { |
||
233 | trigger_error("Argument 'new' has been deprecated", E_USER_DEPRECATED); |
||
234 | $forceNewInstance = $arguments; |
||
235 | $arguments = []; |
||
236 | } else { |
||
237 | $forceNewInstance = false; |
||
238 | } |
||
239 | //如果单例的话直接返回实例结果 |
||
240 | if (isset($this->shares[$name]) && !$forceNewInstance) { |
||
241 | return $this->shares[$name]; |
||
242 | } |
||
243 | //如果没有设置实例化指令的代理则认为当前提供的即是class,为其创建一条指向自身的bind |
||
244 | if (!isset($this->definitions[$name])) { |
||
245 | $this->bind($name, $name); |
||
246 | } |
||
247 | $definition = $this->definitions[$name]; |
||
248 | if (is_callable($definition)) { |
||
249 | $instance = call_user_func($definition, $this, $arguments); |
||
250 | } elseif ($definition instanceof Definition) { |
||
251 | $instance = $this->createFromDefinition($definition, $arguments); |
||
252 | } elseif (is_object($definition)) { |
||
253 | $instance = $definition; |
||
254 | } else { |
||
255 | list(, $instance) = $this->createReflectionAndInstance($definition, $arguments); |
||
256 | } |
||
257 | //如果设置了单例则缓存实例 |
||
258 | if (array_key_exists($name, $this->shares)) { |
||
259 | $this->shares[$name] = $instance; |
||
260 | } |
||
261 | return $instance; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * 分析类结构并自动解决依赖生成一个类实例 |
||
266 | * @param string $name 指定类或者类别名 |
||
267 | * @param array $arguments 构造参数,覆盖预定义参数 |
||
268 | * @throws DependencyInjectionException |
||
269 | * @return object |
||
270 | * @deprecated |
||
271 | */ |
||
272 | public function create($name, $arguments = []) |
||
273 | { |
||
274 | list(, $instance) = $this->createReflectionAndInstance($name, $arguments); |
||
275 | return $instance; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * 获取所有预定义参数 |
||
280 | * @return array |
||
281 | */ |
||
282 | public function getParameters() |
||
283 | { |
||
284 | return $this->parameterStore->toArray(); |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * 设置预定义参数 |
||
289 | * @param array $parameterStore |
||
290 | */ |
||
291 | public function setParameters(array $parameterStore) |
||
292 | { |
||
293 | $this->parameterStore->setParameters($parameterStore); |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * 添加预定义参数 |
||
298 | * @param array $parameters |
||
299 | */ |
||
300 | public function addParameters(array $parameters) |
||
301 | { |
||
302 | $this->parameterStore->addParameters($parameters); |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * 设置参数 |
||
307 | * @param $name |
||
308 | * @param mixed $value |
||
309 | */ |
||
310 | public function setParameter($name, $value) |
||
311 | { |
||
312 | $this->parameterStore->setParameter($name, $value); |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * 获取参数 |
||
317 | * @param $name |
||
318 | * @param null $default |
||
319 | * @return mixed|null |
||
320 | */ |
||
321 | public function getParameter($name, $default = null) |
||
322 | { |
||
323 | return $this->parameterStore->getParameter($name, $default); |
||
324 | } |
||
325 | |||
326 | /** |
||
327 | * 根据definition创建实例 |
||
328 | * @param Definition $definition |
||
329 | * @param array $arguments |
||
330 | * @throws DependencyInjectionException |
||
331 | * @return object |
||
332 | */ |
||
333 | protected function createFromDefinition(Definition $definition, array $arguments) |
||
334 | { |
||
335 | $arguments = array_replace($definition->getArguments(), $arguments); |
||
336 | list($reflection, $instance) = $this->createReflectionAndInstance($definition->getClass(), $arguments); |
||
337 | $this->prepareInstance($reflection, $instance, $definition); |
||
338 | return $instance; |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * 构建实例 |
||
343 | * @param string $class |
||
344 | * @param array $arguments |
||
345 | * @throws DependencyInjectionException |
||
346 | * @return array |
||
347 | */ |
||
348 | protected function createReflectionAndInstance($class, array $arguments) |
||
349 | { |
||
350 | $reflection = $this->reflectClass($class); |
||
351 | if (!$reflection->isInstantiable()) { |
||
352 | throw new DependencyInjectionException(sprintf("Can not instantiate [%s]", $class)); |
||
353 | } |
||
354 | $constructor = $reflection->getConstructor(); |
||
355 | if (!is_null($constructor)) { |
||
356 | $constructorArgs = $this->resolveFunctionArguments( |
||
357 | $constructor, |
||
358 | $this->resolveParameters($arguments), |
||
359 | $this->getContextBindings($class, $constructor->getName()) |
||
360 | ); |
||
361 | $instance = $reflection->newInstanceArgs($constructorArgs); |
||
362 | } else { |
||
363 | $instance = $reflection->newInstanceWithoutConstructor(); |
||
364 | } |
||
365 | return [$reflection, $instance]; |
||
366 | } |
||
367 | |||
368 | protected function prepareInstance(\ReflectionClass $reflection, $instance, Definition $definition) |
||
369 | { |
||
370 | // 触发setter函数 |
||
371 | foreach ($definition->getMethodCalls() as $method => $methodArguments) { |
||
372 | try { |
||
373 | $reflectionMethod = $reflection->getMethod($method); |
||
374 | } catch (\ReflectionException $e) { |
||
375 | throw new DependencyInjectionException(sprintf( |
||
376 | "Class '%s' has no method '%s'", |
||
377 | $definition->getClass(), |
||
378 | $method |
||
379 | )); |
||
380 | } |
||
381 | //获取该方法下所有可用的绑定 |
||
382 | $contextBindings = $this->getContextBindings($reflection->getName(), $method); |
||
0 ignored issues
–
show
![]() |
|||
383 | $reflectionMethod->invokeArgs( |
||
384 | $instance, |
||
385 | $this->resolveFunctionArguments($reflectionMethod, $methodArguments, $contextBindings) |
||
386 | ); |
||
387 | } |
||
388 | // 触发属性 |
||
389 | foreach ($definition->getProperties() as $propertyName => $propertyValue) { |
||
390 | if (property_exists($instance, $propertyName)) { |
||
391 | $instance->$propertyName = $propertyValue; |
||
392 | } else { |
||
393 | throw new DependencyInjectionException(sprintf( |
||
394 | "Class '%s' has no property '%s'", |
||
395 | $definition->getClass(), |
||
396 | $propertyName |
||
397 | )); |
||
398 | } |
||
399 | } |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * 处理方法所需要的参数 |
||
404 | * @param \ReflectionFunctionAbstract $method |
||
405 | * @param array $arguments |
||
406 | * @param array $contextBindings 该方法定义的所有依赖绑定 |
||
407 | * @throws DependencyInjectionException |
||
408 | * @return array |
||
409 | */ |
||
410 | protected function resolveFunctionArguments(\ReflectionFunctionAbstract $method, array $arguments, array $contextBindings = []) |
||
411 | { |
||
412 | $functionArguments = []; |
||
413 | $arguments = $this->resolveParameters($arguments); |
||
414 | $isNumeric = !empty($arguments) && is_numeric(key($arguments)); |
||
415 | foreach ($method->getParameters() as $parameter) { |
||
416 | //如果提供的是数字索引按照参数位置处理否则按照参数名 |
||
417 | $index = $isNumeric ? $parameter->getPosition() : $parameter->getName(); |
||
0 ignored issues
–
show
![]() |
|||
418 | // 如果定义过依赖 则直接获取 |
||
419 | if (isset($arguments[$index])) { |
||
420 | $functionArguments[] = $arguments[$index]; |
||
421 | } elseif (($dependency = $parameter->getClass()) != null) { |
||
422 | $dependencyName = $dependency->getName(); |
||
0 ignored issues
–
show
![]() |
|||
423 | //如果该依赖已经重新映射到新的依赖上则修改依赖为新指向 |
||
424 | isset($contextBindings[$dependencyName]) && $dependencyName = $contextBindings[$dependencyName]; |
||
425 | try { |
||
426 | $functionArguments[] = $this->get($dependencyName); |
||
427 | } catch (DependencyInjectionException $exception) { |
||
428 | if ($parameter->isOptional()) { |
||
429 | $functionArguments[] = $parameter->getDefaultValue(); |
||
430 | } else { |
||
431 | throw $exception; |
||
432 | } |
||
433 | } |
||
434 | } elseif ($parameter->isOptional()) { |
||
435 | $functionArguments[] = $parameter->getDefaultValue(); |
||
436 | } else { |
||
437 | throw new DependencyInjectionException(sprintf( |
||
438 | 'Missing required parameter "%s" when calling "%s"', |
||
439 | $parameter->getName(), |
||
0 ignored issues
–
show
![]() |
|||
440 | $method->getName() |
||
441 | )); |
||
442 | } |
||
443 | } |
||
444 | return $functionArguments; |
||
445 | } |
||
446 | |||
447 | /** |
||
448 | * 获取指定类的绑定关系 |
||
449 | * [ |
||
450 | * 'User' => [ |
||
451 | * 'original' => 'SchoolInterface' |
||
452 | * 'bind' => 'MagicSchool', |
||
453 | * ] |
||
454 | * ] |
||
455 | * @param string $contextClass |
||
456 | * @param string $contextMethod |
||
457 | * @return mixed |
||
458 | */ |
||
459 | protected function getContextBindings($contextClass, $contextMethod) |
||
460 | { |
||
461 | if (!isset($this->contextBindings[$contextClass])) { |
||
462 | return []; |
||
463 | } |
||
464 | $contextBindings = isset($this->contextBindings[$contextClass]['general']) |
||
465 | ? $this->contextBindings[$contextClass]['general'] : []; |
||
466 | if (isset($this->contextBindings[$contextClass][$contextMethod])) { |
||
467 | $contextBindings = array_merge($contextBindings, $this->contextBindings[$contextClass][$contextMethod]); |
||
468 | } |
||
469 | return $contextBindings; |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * 预处理参数 |
||
474 | * @param $parameters |
||
475 | * @return array |
||
476 | */ |
||
477 | protected function resolveParameters($parameters) |
||
478 | { |
||
479 | return array_map(function ($parameter) { |
||
480 | //字符类型参数处理下预定义参数的情况 |
||
481 | if (is_string($parameter)) { |
||
482 | $parameter = $this->resolveString($parameter); |
||
483 | } elseif ($parameter instanceof Reference) { //服务依赖 |
||
484 | $parameter = $this->get($parameter->getName()); |
||
485 | } elseif (is_array($parameter)) { |
||
486 | $parameter = $this->resolveParameters($parameter); |
||
487 | } |
||
488 | return $parameter; |
||
489 | }, $parameters); |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * 处理字符串 |
||
494 | * @param $value |
||
495 | * @return mixed |
||
496 | * @throws DependencyInjectionException |
||
497 | */ |
||
498 | protected function resolveString($value) |
||
499 | { |
||
500 | //%xx%类型的直接返回对应的参数 |
||
501 | if (preg_match("#^%([^%\s]+)%$#", $value, $match)) { |
||
502 | $key = $match[1]; |
||
503 | if ($parameter = $this->parameterStore->getParameter($key)) { |
||
504 | return $parameter; |
||
505 | } |
||
506 | throw new DependencyInjectionException(sprintf("Parameter [%s] is not defined", $key)); |
||
507 | } |
||
508 | //"fool%bar%baz" |
||
509 | return preg_replace_callback("#%([^%\s]+)%#", function ($matches) { |
||
510 | $key = $matches[1]; |
||
511 | if ($parameter = $this->parameterStore->getParameter($key)) { |
||
512 | return $parameter; |
||
513 | } |
||
514 | throw new DependencyInjectionException(sprintf("Parameter [%s] is not defined", $key)); |
||
515 | }, $value); |
||
516 | } |
||
517 | |||
518 | /** |
||
519 | * 获取类的反射对象 |
||
520 | * @param string $className |
||
521 | * @throws DependencyInjectionException |
||
522 | * @return \ReflectionClass |
||
523 | */ |
||
524 | protected function reflectClass($className) |
||
525 | { |
||
526 | try { |
||
527 | $reflection = new \ReflectionClass($className); |
||
528 | } catch (\ReflectionException $e) { |
||
529 | throw new DependencyInjectionException(sprintf('Class "%s" is invalid', $className)); |
||
530 | } |
||
531 | return $reflection; |
||
532 | } |
||
533 | } |
||
534 |
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.