咱們已經介紹過了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