對於php
的框架,不管是yii
,symfony
或者是laravel
,你們都在工做中有涉獵。對於在框架中的存放着資源包vendor
文件夾,入口文件(index.php
或者 app.php
),你們也都與他們天天碰面。可是你真的熟悉這些文件/文件夾嗎?一個完整的項目是如何從一個純淨框架發展而來?各個部分又在框架這個大廈中又起到了怎麼樣的做用?php
依賴注入不是sql注入o( ̄ヘ ̄o#)
當提到依賴注入DI
的時候,90%的文章都會提到一個叫作 Reflection
的東西,致使不少的看官(也包括我)認爲二自己就是一體的,但實際上雙方是兩個風馬牛不相及的概念。Reflection
是反射類,而是一個依賴注入DI
是一種設計模式,反射類怎麼會和設計模式屬於同一種東西?因此他們的關係是依賴關係——既依賴注入是依賴於反射類而完成的設計模式。laravel
寫一個很是簡單的類A,這個類裏面只有一個對象$a
,一個方法a()
。sql
class A { public $a; public function a() { echo __FUNCTION__; } }
以後咱們開始調用反射類Reflection
設計模式
$A = new A(); $reflect = new ReflectionObject($A); $props = $reflect->getProperties(); // 獲取該類全部的對象 ...
咱們能夠經過反射知道該類全部咱們想知道的內容,好像是x光同樣獲取一個類裏面的全部細節。可是,當我完反射類以後,又陷入了迷茫。設計這種反射類究竟是爲了什麼?若是想知道原來類裏面有多少方法/對象直接看原來的類不就能夠了嗎?爲何要畫蛇添足呢?這又和依賴注入有什麼關係呢?
先不要着急,不妨讓咱們先說說別的,而後回頭來看看。app
如今有這樣的一個場景,我想實例化B
,可是B
依賴於A
。這該怎麼作?很簡單以下就能夠解決。框架
Class A{ // do sth. } Class B{ public function __construct(A $a){ // B類依賴於A } } $a = new A(); $b = new B($a);
很簡單,對不對?兩下就能夠解決,這沒有什麼困難的。可是若是如今出現了這樣一種狀況:一共有A-Z
一共26個類,B
類依賴於A
,C
類依賴於B
,以此類推。那麼我若是想實例化Z
類,你須要怎麼作?難道還使用上圖中的方法?那豈不是實例化Z
須要寫26行?yii
那麼有沒有一種方法可以直接實例化B,而後就能夠把其餘的類所有自動加載進來?還真有。就須要用到上面剛剛講過的反射類。函數
<?php /** * 工具類,使用該類來實現自動依賴注入。 */ class Ioc { // 得到類的對象實例 public static function getInstance($className) { $paramArr = self::getMethodParams($className); return (new ReflectionClass($className))->newInstanceArgs($paramArr); } /** * 得到類的方法參數,只得到有類型的參數。 * 經過遞歸方法 * @param [type] $className [類名] * @param [type] $methodsName [方法名] * @return [mixed] */ protected static function getMethodParams($className, $methodsName = '__construct') { // 經過反射得到該類 $class = new ReflectionClass($className); $paramArr = []; // 記錄參數,和參數類型 // 判斷該類是否有構造函數 if ($class->hasMethod($methodsName)) { // 得到構造函數 $construct = $class->getMethod($methodsName); // 判斷構造函數是否有參數 $params = $construct->getParameters(); if (count($params) > 0) { // 判斷參數類型 foreach ($params as $key => $param) { if ($paramClass = $param->getClass()) { // 得到參數類型名稱 $paramClassName = $paramClass->getName(); /** * 得到參數類型 * 在這裏使用遞歸 */ $args = self::getMethodParams($paramClassName); $paramArr[] = (new ReflectionClass($paramClass->getName()))->newInstanceArgs($args); } } } } return $paramArr; } /** * 執行類的方法 * @param [type] $className [類名] * @param [type] $methodName [方法名稱] * @param [type] $params [額外的參數] * @return [type] [description] */ public static function make($className, $methodName, $params = []) { // 獲取類的實例 $instance = self::getInstance($className); // 獲取該方法所須要依賴注入的參數 $paramArr = self::getMethodParams($className, $methodName); return $instance->{$methodName}(...array_merge($paramArr, $params)); } } class A { // ... } class B { public function __construct(A $a) { // ... } } class C { public function __construct(B $b) { // ... } } $cObj = Ioc::getInstance('C');
讓咱們來着重的看一下Ioc
這個類的getMethodParams
方法。工具
先說一下大致的狀況:既該方法是使用遞歸對被加載的類進行遍從來達到將全部被依賴的類所有加載進來的目的。this
這個方法首先實例化Reflection
,而後使用getMethod
方法獲取被加載進來的類的__construct
方法,而後看看這個__construct
中是否有其餘的類做爲參數,若是有,則遞歸重複
以上流程,直到沒有依賴爲止。
以上就是使用Reflection
進行依賴注入的一個簡單例子。雖然在實際框架中會比上面的例子複雜一些,不過依然大同小異。
好比說,在不少的框架中,會使用一種叫作容器container
的概念--使用$this->getContainer()->get(YOUR_CLASS_NAME)
的方式獲取類。這個容器是什麼高科技的東西?不用擔憂,說到底他仍是依賴注入。
咱們會在後面刨根問底,來看看symfony的container是怎麼設計的。