咱们已经介绍过了PHP 反射API,阐明了什么是反射API,以及它的不一样用途,其中一种 - 最多见的是将其与 DI Container
一块儿使用,如下是本文的主要内容:php
DI Container
ReflectionClass
和 DI Container
依赖注入是一种技术,一个对象提供另外一个对象的依赖关系 - 依赖注入 - 维基百科html
这是一个很是简单的概念,你能够将对象注入另外一个对象,请查看如下示例:git
class Profile { public function deactivateProfile(Setting $setting) { $setting->isActive = false; } }
由于咱们须要在类方法内中使用 $setting
对象,因此咱们将它做为参数注入/传递。github
有不少方法能够注入对象,这里有几种经常使用的方法:app
Constructor Injection
:它是如何经过类构造函数注入对象的,看下面例子:class Profile { private $setting; public function __construct(Setting $setting) { $this->setting = $setting; } } // 要想实例化Profile, 必须先实例化 Setting $setting = new Setting; $profile = new Profile($setting);
这是最多见的状况,建立松散耦合的组件很是有用。在为应用程序编写测试时也颇有用。框架
setter
函数将对象注入到类中class Profile { private $setting; public function setSetting(Setting $setting) { $this->setting = $setting; } } // to instantiate Profile, you have the option to inject Setting object $setting = new Setting; $profile = new Profile(); $profile->setSetting($setting);
经过这种方式,能够随时在 Profile
类中使用它来注入。函数
Dependency Injection Container
是在应用程序中管理注入和读取对象和第三方库的方式。测试
PHP-FIG PSR-11 告诉你如何在应用程序中使用一个容器:this
<?php interface ContainerInterface { public function get($id); public function has($id); }
对于具备两个主要函数 get()
和 has(
的 ContainerInterface
来讲,这是一个很是简单的实现。.net
get()
:从容器中获取对象。has()
:检查容器中是否有对象。注意: PHP-FIG 的目标是为不一样的实现制定标准,并将它们引入到PHP社区,点这里 查看更多内容,稍后咱们将在另外一个主题中讨论它。
绝大多数框架都使用 DI Container
,而不单单是在前面的章节中看到的内容,并且还解决了依赖关系 Autowiring。
可是,resolving 依赖关系意味着什么,它意味着应用程序可以自动处理特定类的依赖关系,而不是手动执行。
为了理解的更加清楚点,咱们来看一个例子:
class Profile { protected $setting; public function __construct(Setting $setting) { $this->setting = $setting; } }
这是咱们实例化Profile类的方式:
$setting = new Setting; $profile = new Profile($setting);
所以,为了实例化 Profile
,你必须先实例化 Setting
,而后在实例化以前手动解析 Profile
类的依赖关系。
咱们能够以一种简洁的方式完成此操做,下面是一个很是简单的例子,它来自于 gist.github.com,使用一个 Container
类,该类自动为应用程序解析依赖关系:
<?php /** * Class Container */ class Container { /** * @var array */ protected $instances = []; /** * @param $abstract * @param null $concrete */ public function set($abstract, $concrete = NULL) { if ($concrete === NULL) { $concrete = $abstract; } $this->instances[$abstract] = $concrete; } /** * @param $abstract * @param array $parameters * * @return mixed|null|object * @throws Exception */ public function get($abstract, $parameters = []) { // if we don't have it, just register it if (!isset($this->instances[$abstract])) { $this->set($abstract); } return $this->resolve($this->instances[$abstract], $parameters); } /** * resolve single * * @param $concrete * @param $parameters * * @return mixed|object * @throws Exception */ public function resolve($concrete, $parameters) { if ($concrete instanceof Closure) { return $concrete($this, $parameters); } $reflector = new ReflectionClass($concrete); // check if class is instantiable if (!$reflector->isInstantiable()) { throw new Exception("Class {$concrete} is not instantiable"); } // get class constructor $constructor = $reflector->getConstructor(); if (is_null($constructor)) { // get new instance from class return $reflector->newInstance(); } // get constructor params $parameters = $constructor->getParameters(); $dependencies = $this->getDependencies($parameters); // get new instance with dependencies resolved return $reflector->newInstanceArgs($dependencies); } /** * get all dependencies resolved * * @param $parameters * * @return array * @throws Exception */ public function getDependencies($parameters) { $dependencies = []; foreach ($parameters as $parameter) { // get the type hinted class $dependency = $parameter->getClass(); if ($dependency === NULL) { // check if default value for a parameter is available if ($parameter->isDefaultValueAvailable()) { // get default value of parameter $dependencies[] = $parameter->getDefaultValue(); } else { throw new Exception("Can not resolve class dependency {$parameter->name}"); } } else { // get dependency resolved $dependencies[] = $this->get($dependency->name); } } return $dependencies; } }
Container
类经过 set()
方法注册不一样的类:
$container = new Container(); // let our application know that we are going to use the following // classes, and this is optional, because if you didn't do it, it // will be registered via the get() function $container->set('Profile');
不管什么时候你想实例化 Profile
类,你均可以经过下面的方式轻松完成:
$profile = $container->get('Profile');
get()
函数经过 resolve()
函数,来检查 Profile
类的 __construct()
是否具备任何依赖关系,所以它将递归地解析它们(意味着若是 Setting
类具备依赖关系,它们也将被解析),不然,它将直接实例化 Profile
类。
ReflectionClass
和 ReflectionParameter
主要用于咱们的 Container
类:
$reflector = new ReflectionClass($concrete); // check if class is instantiable $reflector->isInstantiable(); // get class constructor $reflector->getConstructor(); // get new instance from class $reflector->newInstance(); // get new instance with dependencies resolved $reflector->newInstanceArgs($dependencies); // get constructor params $constructor->getParameters(); // get the type hinted class $params->getClass() // check if default value for a parameter is available $parameter->isDefaultValueAvailable(); // get default value of parameter $parameter->getDefaultValue();
咱们主要讨论了 Reflection API
的主要用法之一以及 DI Container
的主要功能,这是Autowiring
,以及咱们如何有效地使用 Reflection API
,并经过简单的实现来阐明它。
更多PHP 知识,能够前往 PHPCasts