摘要:也正式因?yàn)轭?lèi)的反射很多框架才能實(shí)現(xiàn)依賴(lài)注入自動(dòng)解決類(lèi)與類(lèi)之間的依賴(lài)關(guān)系,這給我們平時(shí)的開(kāi)發(fā)帶來(lái)了很大的方便。為了更好地理解,我們通過(guò)一個(gè)例子來(lái)看類(lèi)的反射,以及如何實(shí)現(xiàn)依賴(lài)注入。反射出方法的參數(shù)其返回值為對(duì)象構(gòu)成的數(shù)組。
PHP具有完整的反射 API,提供了對(duì)類(lèi)、接口、函數(shù)、方法和擴(kuò)展進(jìn)行逆向工程的能力。通過(guò)類(lèi)的反射提供的能力我們能夠知道類(lèi)是如何被定義的,它有什么屬性、什么方法、方法都有哪些參數(shù),類(lèi)文件的路徑是什么等很重要的信息。也正式因?yàn)轭?lèi)的反射很多PHP框架才能實(shí)現(xiàn)依賴(lài)注入自動(dòng)解決類(lèi)與類(lèi)之間的依賴(lài)關(guān)系,這給我們平時(shí)的開(kāi)發(fā)帶來(lái)了很大的方便。 本文主要是講解如何利用類(lèi)的反射來(lái)實(shí)現(xiàn)依賴(lài)注入(Dependency Injection),并不會(huì)去逐條講述PHP Reflection里的每一個(gè)API,詳細(xì)的API參考信息請(qǐng)查閱官方文檔
再次聲明這里實(shí)現(xiàn)的依賴(lài)注入非常簡(jiǎn)單,并不能應(yīng)用到實(shí)際開(kāi)發(fā)中去,可以參考我的另一篇文章來(lái)了解laravel框架的服務(wù)容器(IocContainer), 在那里會(huì)介紹Laravel的服務(wù)容器是如何實(shí)現(xiàn)依賴(lài)注入的。
為了更好地理解,我們通過(guò)一個(gè)例子來(lái)看類(lèi)的反射,以及如何實(shí)現(xiàn)依賴(lài)注入。
下面這個(gè)類(lèi)代表了坐標(biāo)系里的一個(gè)點(diǎn),有兩個(gè)屬性橫坐標(biāo)x和縱坐標(biāo)y。
/** * Class Point */ class Point { public $x; public $y; /** * Point constructor. * @param int $x horizontal value of point"s coordinate * @param int $y vertical value of point"s coordinate */ public function __construct($x = 0, $y = 0) { $this->x = $x; $this->y = $y; } }
接下來(lái)這個(gè)類(lèi)代表圓形,可以看到在它的構(gòu)造函數(shù)里有一個(gè)參數(shù)是Point類(lèi)的,即Circle類(lèi)是依賴(lài)與Point類(lèi)的。
class Circle { /** * @var int */ public $radius;//半徑 /** * @var Point */ public $center;//圓心點(diǎn) const PI = 3.14; public function __construct(Point $point, $radius = 1) { $this->center = $point; $this->radius = $radius; } //打印圓點(diǎn)的坐標(biāo) public function printCenter() { printf("center coordinate is (%d, %d)", $this->center->x, $this->center->y); } //計(jì)算圓形的面積 public function area() { return 3.14 * pow($this->radius, 2); } }ReflectionClass
下面我們通過(guò)反射來(lái)對(duì)Circle這個(gè)類(lèi)進(jìn)行反向工程。
把Circle類(lèi)的名字傳遞給reflectionClass來(lái)實(shí)例化一個(gè)ReflectionClass類(lèi)的對(duì)象。
$reflectionClass = new reflectionClass(Circle::class); //返回值如下 object(ReflectionClass)#1 (1) { ["name"]=> string(6) "Circle" }反射出類(lèi)的常量
$reflectionClass->getConstants();
返回一個(gè)由常量名稱(chēng)和值構(gòu)成的關(guān)聯(lián)數(shù)組
array(1) { ["PI"]=> float(3.14) }通過(guò)反射獲取屬性
$reflectionClass->getProperties();
返回一個(gè)由ReflectionProperty對(duì)象構(gòu)成的數(shù)組
array(2) { [0]=> object(ReflectionProperty)#2 (2) { ["name"]=> string(6) "radius" ["class"]=> string(6) "Circle" } [1]=> object(ReflectionProperty)#3 (2) { ["name"]=> string(6) "center" ["class"]=> string(6) "Circle" } }反射出類(lèi)中定義的方法
$reflectionClass->getMethods();
返回ReflectionMethod對(duì)象構(gòu)成的數(shù)組
array(3) { [0]=> object(ReflectionMethod)#2 (2) { ["name"]=> string(11) "__construct" ["class"]=> string(6) "Circle" } [1]=> object(ReflectionMethod)#3 (2) { ["name"]=> string(11) "printCenter" ["class"]=> string(6) "Circle" } [2]=> object(ReflectionMethod)#4 (2) { ["name"]=> string(4) "area" ["class"]=> string(6) "Circle" } }
我們還可以通過(guò)getConstructor()來(lái)多帶帶獲取類(lèi)的構(gòu)造方法,其返回值為一個(gè)ReflectionMethod對(duì)象。
$constructor = $reflectionClass->getConstructor();反射出方法的參數(shù)
$parameters = $constructor->getParameters();
其返回值為ReflectionParameter對(duì)象構(gòu)成的數(shù)組。
array(2) { [0]=> object(ReflectionParameter)#3 (1) { ["name"]=> string(5) "point" } [1]=> object(ReflectionParameter)#4 (1) { ["name"]=> string(6) "radius" } }依賴(lài)注入
好了接下來(lái)我們編寫(xiě)一個(gè)名為make的函數(shù),傳遞類(lèi)名稱(chēng)給make函數(shù)返回類(lèi)的對(duì)象,在make里它會(huì)幫我們注入類(lèi)的依賴(lài),即在本例中幫我們注入Point對(duì)象給Circle類(lèi)的構(gòu)造方法。
//構(gòu)建類(lèi)的對(duì)象 function make($className) { $reflectionClass = new ReflectionClass($className); $constructor = $reflectionClass->getConstructor(); $parameters = $constructor->getParameters(); $dependencies = getDependencies($parameters); return $reflectionClass->newInstanceArgs($dependencies); } //依賴(lài)解析 function getDependencies($parameters) { $dependencies = []; foreach($parameters as $parameter) { $dependency = $parameter->getClass(); if (is_null($dependency)) { if($parameter->isDefaultValueAvailable()) { $dependencies[] = $parameter->getDefaultValue(); } else { //不是可選參數(shù)的為了簡(jiǎn)單直接賦值為字符串0 //針對(duì)構(gòu)造方法的必須參數(shù)這個(gè)情況 //laravel是通過(guò)service provider注冊(cè)closure到IocContainer, //在closure里可以通過(guò)return new Class($param1, $param2)來(lái)返回類(lèi)的實(shí)例 //然后在make時(shí)回調(diào)這個(gè)closure即可解析出對(duì)象 //具體細(xì)節(jié)我會(huì)在另一篇文章里面描述 $dependencies[] = "0"; } } else { //遞歸解析出依賴(lài)類(lèi)的對(duì)象 $dependencies[] = make($parameter->getClass()->name); } } return $dependencies; }
定義好make方法后我們通過(guò)它來(lái)幫我們實(shí)例化Circle類(lèi)的對(duì)象:
$circle = make("Circle"); $area = $circle->area(); /*var_dump($circle, $area); object(Circle)#6 (2) { ["radius"]=> int(1) ["center"]=> object(Point)#11 (2) { ["x"]=> int(0) ["y"]=> int(0) } } float(3.14)*/
通過(guò)上面這個(gè)實(shí)例我簡(jiǎn)單描述了一下如何利用PHP類(lèi)的反射來(lái)實(shí)現(xiàn)依賴(lài)注入,Laravel的依賴(lài)注入也是通過(guò)這個(gè)思路來(lái)實(shí)現(xiàn)的,只不過(guò)設(shè)計(jì)的更精密大量地利用了閉包回調(diào)來(lái)應(yīng)對(duì)各種復(fù)雜的依賴(lài)注入,詳情可以參考我的另一篇介紹Laravel服務(wù)容器的文章。
本文的示例代碼的下載鏈接
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/26282.html
摘要:依賴(lài)注入控制反轉(zhuǎn)的一種具體實(shí)現(xiàn)方法。接下來(lái),我們使用依賴(lài)注入實(shí)現(xiàn)控制反轉(zhuǎn),使依賴(lài)關(guān)系倒置依賴(lài)被動(dòng)傳入。從單元測(cè)試的角度看,依賴(lài)注入更方便和操作,方便了測(cè)試人員寫(xiě)出質(zhì)量更高的測(cè)試代碼。 前言 好的設(shè)計(jì)會(huì)提高程序的可復(fù)用性和可維護(hù)性,也間接的提高了開(kāi)發(fā)人員的生產(chǎn)力。今天,我們就來(lái)說(shuō)一下在很多框架中都使用的依賴(lài)注入。 一些概念 要搞清楚什么是依賴(lài)注入如何依賴(lài)注入,首先我們要明確一些概念。 D...
摘要:控制反轉(zhuǎn)容器控制反轉(zhuǎn)使依賴(lài)注入變得更加便捷。有瑕疵控制反轉(zhuǎn)容器是實(shí)現(xiàn)的控制翻轉(zhuǎn)容器的一種替代方案。容器的獨(dú)立使用即使沒(méi)有使用框架,我們?nèi)匀豢梢栽陧?xiàng)目中使用安裝組件來(lái)使用的控制反轉(zhuǎn)容器。在沒(méi)有給定任何信息的情況下,容器是無(wú)法實(shí)例化相關(guān)依賴(lài)的。 聲明:本文并非博主原創(chuàng),而是來(lái)自對(duì)《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味...
摘要:代碼這就是控制反轉(zhuǎn)模式。是變量有默認(rèn)值則設(shè)置默認(rèn)值是一個(gè)類(lèi),遞歸解析有默認(rèn)值則返回默認(rèn)值從容器中取得以上代碼的原理參考官方文檔反射,具有完整的反射,添加了對(duì)類(lèi)接口函數(shù)方法和擴(kuò)展進(jìn)行反向工程的能力。 PHP程序員如何理解依賴(lài)注入容器(dependency injection container) 背景知識(shí) 傳統(tǒng)的思路是應(yīng)用程序用到一個(gè)Foo類(lèi),就會(huì)創(chuàng)建Foo類(lèi)并調(diào)用Foo類(lèi)的方法,假如這...
摘要:從使用到原理學(xué)習(xí)線程池關(guān)于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實(shí)現(xiàn)在軟件開(kāi)發(fā)中,分散于應(yīng)用中多出的功能被稱(chēng)為橫切關(guān)注點(diǎn)如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計(jì)模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過(guò)的點(diǎn)點(diǎn)滴滴,依然清楚的記得當(dāng)初愛(ài)情萌芽的模樣…… Java 進(jìn)階面試問(wèn)題列表 -...
閱讀 3378·2023-04-26 01:40
閱讀 3086·2021-11-24 09:39
閱讀 1397·2021-10-27 14:19
閱讀 2641·2021-10-12 10:11
閱讀 1302·2021-09-26 09:47
閱讀 1842·2021-09-22 15:21
閱讀 2697·2021-09-06 15:00
閱讀 883·2021-08-10 09:44