扣丁學(xué)堂PHP培訓(xùn)之理解并實(shí)現(xiàn)依賴注入容器
2017-12-07 10:10:42
1378瀏覽
今天扣丁學(xué)堂小編給大家介紹一下關(guān)于
PHP培訓(xùn)之實(shí)現(xiàn)依賴注入的詳細(xì)介紹,首先依賴注入可能是我所知道的最簡(jiǎn)單設(shè)計(jì)模式之一,很多情況下可能你無(wú)意識(shí)中已經(jīng)使用了依賴注入。不過(guò)它也是最難解釋的一個(gè)。我認(rèn)為有一部分原因是由于大多數(shù)介紹依賴注入的例子缺乏實(shí)際意義,那么,下面我們來(lái)看一個(gè)簡(jiǎn)單的開(kāi)發(fā)實(shí)例吧。
首先,我們來(lái)看一段代碼:
classA{
publicfunctionecho()
{
echo'A'.PHP_EOL;
}
}
classEchoT{
protected$t;
publicfunction__construct()
{
$this->t=newA();
}
?publicfunctionecho(){
$this->t->echo();
}
?
}
初始,我們都使用new的方式在內(nèi)部進(jìn)行,EchoT類嚴(yán)重依賴于類A。每當(dāng)類A變化時(shí),EchoT類也得進(jìn)行變化。###我們優(yōu)化一下代碼
classEchoT{
protected$t;
publicfunction__construct($t)//構(gòu)造器注入由構(gòu)造器注入到其中
{
$this->t=$t;
}
可以看到,這樣做的話。很大程序上,我們對(duì)程序進(jìn)行了解耦。類A無(wú)論你如何變動(dòng),EchoT類是不需要變動(dòng)的。不再依賴于A。但是新問(wèn)題又來(lái)了,我們現(xiàn)在只有A,萬(wàn)一來(lái)了B,來(lái)了CDEFG怎么辦。
interfaceT{
publicfunctionecho();
}
classA{
publicfunctionecho()
{
echo'A'.PHP_EOL;
}
}
classBimplementsT{
publicfunctionecho()
{
echo'B'.PHP_EOL;
}
}
classEchoT{
protected$t;
publicfunction__construct(T$t)//構(gòu)造器注入由構(gòu)造器注入到其中
{
$this->t=$t;
}
publicfunctionecho(){
$this->t->echo();
}
}
將T抽象出為接口,這樣,EchoT類中的echo方法變成一個(gè)抽象的方法,不到運(yùn)行那一刻,不知道他們的Method方式是怎么實(shí)現(xiàn)的。###工廠
functiongetT($str){
if(class_exists($str)){
returnnew$str();
}
}
T要使用哪個(gè)是不明確的,因此,我們可以將其工廠化?!究瓷先ズ芎?jiǎn)單,在DI實(shí)際上有體現(xiàn)】首先,我們看一下PHP的psr規(guī)范。
官方定義的接口
Psr\Container\ContainerInterface
包含兩個(gè)方法
functionget($id);
functionhas($id);
仔細(xì)看上面的工廠,是不是和get($id)很一致,PHP官方將其定義為容器(Container,我個(gè)人理解,就是一個(gè)復(fù)雜的工廠)
dependencyinjectioncontainer
依賴注入容器
namespaceCore;
usePsr\Container\ContainerInterface;
classContainerimplementsContainerInterface
{
protected$instance=[];//對(duì)象存儲(chǔ)的數(shù)組
publicfunction__construct($path){
$this->_autoload($path);//首先我們要自動(dòng)加載psr-autoload
}
publicfunctionbuild($className)
{
if(is_string($className)and$this->has($className)){
return$this->get($className);
}
//反射
$reflector=new\ReflectionClass($className);
if(!$reflector->isInstantiable()){
thrownew\Exception("Can'tinstantiate".$className);
}
//檢查類是否可實(shí)例化,排除抽象類abstract和對(duì)象接口interface
if(!$reflector->isInstantiable()){
thrownew\Exception("Can'tinstantiate".$className);
}
/**@var\ReflectionMethod$constructor獲取類的構(gòu)造函數(shù)*/
$constructor=$reflector->getConstructor();
//若無(wú)構(gòu)造函數(shù),直接實(shí)例化并返回
if(is_null($constructor)){
returnnew$className;
}
//取構(gòu)造函數(shù)參數(shù),通過(guò)ReflectionParameter數(shù)組返回參數(shù)列表
$parameters=$constructor->getParameters();
//遞歸解析構(gòu)造函數(shù)的參數(shù)
$dependencies=$this->getDependencies($parameters);
//創(chuàng)建一個(gè)類的新實(shí)例,給出的參數(shù)將傳遞到類的構(gòu)造函數(shù)。
$class=$reflector->newInstanceArgs($dependencies);
$this->instance[$className]=$class;
return$class;
}
/**
*@paramarray$parameters
*@returnarray
*/
publicfunctiongetDependencies(array$parameters)
{
$dependencies=[];
/**@var\ReflectionParameter$parameter*/
foreach($parametersas$parameter){
/**@var\ReflectionClass$dependency*/
$dependency=$parameter->getClass();
if(is_null($dependency)){
//是變量,有默認(rèn)值則設(shè)置默認(rèn)值
$dependencies[]=$this->resolveNonClass($parameter);
}else{
//是一個(gè)類,遞歸解析
$dependencies[]=$this->build($dependency->name);
}
}
return$dependencies;
}
/**
*@param\ReflectionParameter$parameter
*@returnmixed
*@throws\Exception
*/
publicfunctionresolveNonClass(\ReflectionParameter$parameter)
{
//有默認(rèn)值則返回默認(rèn)值
if($parameter->isDefaultValueAvailable()){
return$parameter->getDefaultValue();
}
thrownew\Exception($parameter->getName().'mustbenotnull');
}
/**
*參照psr-autoload規(guī)范
*@param$path
*/
publicfunction_autoload($path){
spl_autoload_register(function(string$class)use($path){
$file=DIRECTORY_SEPARATOR.str_replace('\\',DIRECTORY_SEPARATOR,$class).'.php';
if(is_file($path.$file)){
include($path.$file);
returntrue;
}
returnfalse;
});
}
publicfunctionget($id)
{
if($this->has($id)){
return$this->instance[$id];
}
if(class_exists($id)){
return$this->build($id);
}
thrownewClassNotFoundException('classnotfound');//實(shí)現(xiàn)的PSR規(guī)范的異常
}
publicfunctionhas($id)
{
returnisset($this->instance[$id])?true:false;
}
}
####使用示例
$container=newContainer('../');//假設(shè)這是路徑
$echoT=$container->get(\Test\EchoT::class);//假設(shè)echoT類的命名空間是\Test
$echoT->echo();
這個(gè)時(shí)候,會(huì)出現(xiàn)一個(gè)問(wèn)題:
//檢查類是否可實(shí)例化,排除抽象類abstract和對(duì)象接口interface
if(!$reflector->isInstantiable()){
thrownew\Exception("Can'tinstantiate".$className);
}
因?yàn)榻涌赥是無(wú)法實(shí)例化的,所以,一般在程序內(nèi),我們都加上別名(參照l(shuí)aravel框架)
$container->alisa(\Test\T::class,\Test\T\A::class);//指定接口T使用類A(控制反轉(zhuǎn))
####針對(duì)接口下面是alias方法
publicfunctionalias(string$key,$class,bool$singleton=true)
{
if($singleton){
$this->singleton[]=$class;
}
$this->aliases[$key]=$class;
return$this;
}
//同時(shí),我們需要在build的時(shí)候進(jìn)行判斷是否為別名
publicfunctionbuild($className)
{
if(is_string($className)and$this->has($className)){
return$this->get($className);
}
if(isset($this->aliases[$className])){
if(is_object($this->aliases[$className])){
return$this->aliases[$className];
}
$className=$this->aliases[$className];
}
由此可見(jiàn)一個(gè)簡(jiǎn)單的PHP容器就實(shí)現(xiàn)了,以上就是關(guān)于P,最后想要學(xué)習(xí)PHP開(kāi)發(fā)技術(shù)的小伙伴不要猶豫了,扣丁學(xué)堂是你學(xué)習(xí)PHP技術(shù)的最佳選擇,想要PHP視頻教程的小伙伴現(xiàn)在就聯(lián)系我們的咨詢老師領(lǐng)取吧,機(jī)會(huì)總是留給有準(zhǔn)備的人的!扣丁學(xué)堂PHP技術(shù)交流群:374332265。
今天扣丁學(xué)堂小編給大家介紹一下關(guān)于PHP實(shí)現(xiàn)依賴注入的詳細(xì)介紹,首先依賴注入可能是我所知道的最簡(jiǎn)單設(shè)計(jì)模式之一,很多情況下可能你無(wú)意識(shí)中已經(jīng)使用了依賴注入。不過(guò)它也是最難解釋的一個(gè)。我認(rèn)為有一部分原因是由于大多數(shù)介紹依賴注入的例子缺乏實(shí)際意義,那么,下面我們來(lái)看一個(gè)簡(jiǎn)單的開(kāi)發(fā)實(shí)例吧。
首先,我們來(lái)看一段代碼:
classA{
publicfunctionecho()
{
echo'A'.PHP_EOL;
}
}
classEchoT{
protected$t;
publicfunction__construct()
{
$this->t=newA();
}
?publicfunctionecho(){
$this->t->echo();
}
?
}
初始,我們都使用new的方式在內(nèi)部進(jìn)行,EchoT類嚴(yán)重依賴于類A。每當(dāng)類A變化時(shí),EchoT類也得進(jìn)行變化。###我們優(yōu)化一下代碼
classEchoT{
protected$t;
publicfunction__construct($t)//構(gòu)造器注入由構(gòu)造器注入到其中
{
$this->t=$t;
}
可以看到,這樣做的話。很大程序上,我們對(duì)程序進(jìn)行了解耦。類A無(wú)論你如何變動(dòng),EchoT類是不需要變動(dòng)的。不再依賴于A。但是新問(wèn)題又來(lái)了,我們現(xiàn)在只有A,萬(wàn)一來(lái)了B,來(lái)了CDEFG怎么辦。
interfaceT{
publicfunctionecho();
}
classA{
publicfunctionecho()
{
echo'A'.PHP_EOL;
}
}
classBimplementsT{
publicfunctionecho()
{
echo'B'.PHP_EOL;
}
}
classEchoT{
protected$t;
publicfunction__construct(T$t)//構(gòu)造器注入由構(gòu)造器注入到其中
{
$this->t=$t;
}
publicfunctionecho(){
$this->t->echo();
}
}
將T抽象出為接口,這樣,EchoT類中的echo方法變成一個(gè)抽象的方法,不到運(yùn)行那一刻,不知道他們的Method方式是怎么實(shí)現(xiàn)的。###工廠
functiongetT($str){
if(class_exists($str)){
returnnew$str();
}
}
T要使用哪個(gè)是不明確的,因此,我們可以將其工廠化?!究瓷先ズ芎?jiǎn)單,在DI實(shí)際上有體現(xiàn)】首先,我們看一下PHP的psr規(guī)范。
官方定義的接口
Psr\Container\ContainerInterface
包含兩個(gè)方法
functionget($id);
functionhas($id);
仔細(xì)看上面的工廠,是不是和get($id)很一致,PHP官方將其定義為容器(Container,我個(gè)人理解,就是一個(gè)復(fù)雜的工廠)
dependencyinjectioncontainer
依賴注入容器
namespaceCore;
usePsr\Container\ContainerInterface;
classContainerimplementsContainerInterface
{
protected$instance=[];//對(duì)象存儲(chǔ)的數(shù)組
publicfunction__construct($path){
$this->_autoload($path);//首先我們要自動(dòng)加載psr-autoload
}
publicfunctionbuild($className)
{
if(is_string($className)and$this->has($className)){
return$this->get($className);
}
//反射
$reflector=new\ReflectionClass($className);
if(!$reflector->isInstantiable()){
thrownew\Exception("Can'tinstantiate".$className);
}
//檢查類是否可實(shí)例化,排除抽象類abstract和對(duì)象接口interface
if(!$reflector->isInstantiable()){
thrownew\Exception("Can'tinstantiate".$className);
}
/**@var\ReflectionMethod$constructor獲取類的構(gòu)造函數(shù)*/
$constructor=$reflector->getConstructor();
//若無(wú)構(gòu)造函數(shù),直接實(shí)例化并返回
if(is_null($constructor)){
returnnew$className;
}
//取構(gòu)造函數(shù)參數(shù),通過(guò)ReflectionParameter數(shù)組返回參數(shù)列表
$parameters=$constructor->getParameters();
//遞歸解析構(gòu)造函數(shù)的參數(shù)
$dependencies=$this->getDependencies($parameters);
//創(chuàng)建一個(gè)類的新實(shí)例,給出的參數(shù)將傳遞到類的構(gòu)造函數(shù)。
$class=$reflector->newInstanceArgs($dependencies);
$this->instance[$className]=$class;
return$class;
}
/**
*@paramarray$parameters
*@returnarray
*/
publicfunctiongetDependencies(array$parameters)
{
$dependencies=[];
/**@var\ReflectionParameter$parameter*/
foreach($parametersas$parameter){
/**@var\ReflectionClass$dependency*/
$dependency=$parameter->getClass();
if(is_null($dependency)){
//是變量,有默認(rèn)值則設(shè)置默認(rèn)值
$dependencies[]=$this->resolveNonClass($parameter);
}else{
//是一個(gè)類,遞歸解析
$dependencies[]=$this->build($dependency->name);
}
}
return$dependencies;
}
/**
*@param\ReflectionParameter$parameter
*@returnmixed
*@throws\Exception
*/
publicfunctionresolveNonClass(\ReflectionParameter$parameter)
{
//有默認(rèn)值則返回默認(rèn)值
if($parameter->isDefaultValueAvailable()){
return$parameter->getDefaultValue();
}
thrownew\Exception($parameter->getName().'mustbenotnull');
}
/**
*參照psr-autoload規(guī)范
*@param$path
*/
publicfunction_autoload($path){
spl_autoload_register(function(string$class)use($path){
$file=DIRECTORY_SEPARATOR.str_replace('\\',DIRECTORY_SEPARATOR,$class).'.php';
if(is_file($path.$file)){
include($path.$file);
returntrue;
}
returnfalse;
});
}
publicfunctionget($id)
{
if($this->has($id)){
return$this->instance[$id];
}
if(class_exists($id)){
return$this->build($id);
}
thrownewClassNotFoundException('classnotfound');//實(shí)現(xiàn)的PSR規(guī)范的異常
}
publicfunctionhas($id)
{
returnisset($this->instance[$id])?true:false;
}
}
####使用示例
$container=newContainer('../');//假設(shè)這是路徑
$echoT=$container->get(\Test\EchoT::class);//假設(shè)echoT類的命名空間是\Test
$echoT->echo();
這個(gè)時(shí)候,會(huì)出現(xiàn)一個(gè)問(wèn)題:
//檢查類是否可實(shí)例化,排除抽象類abstract和對(duì)象接口interface
if(!$reflector->isInstantiable()){
thrownew\Exception("Can'tinstantiate".$className);
}
因?yàn)榻涌赥是無(wú)法實(shí)例化的,所以,一般在程序內(nèi),我們都加上別名(參照l(shuí)aravel框架)
$container->alisa(\Test\T::class,\Test\T\A::class);//指定接口T使用類A(控制反轉(zhuǎn))
####針對(duì)接口下面是alias方法
publicfunctionalias(string$key,$class,bool$singleton=true)
{
if($singleton){
$this->singleton[]=$class;
}
$this->aliases[$key]=$class;
return$this;
}
//同時(shí),我們需要在build的時(shí)候進(jìn)行判斷是否為別名
publicfunctionbuild($className)
{
if(is_string($className)and$this->has($className)){
return$this->get($className);
}
if(isset($this->aliases[$className])){
if(is_object($this->aliases[$className])){
return$this->aliases[$className];
}
$className=$this->aliases[$className];
}
由此可見(jiàn)一個(gè)簡(jiǎn)單的PHP容器就實(shí)現(xiàn)了,以上就是關(guān)于P,最后想要學(xué)習(xí)PHP開(kāi)發(fā)技術(shù)的小伙伴不要猶豫了,扣丁學(xué)堂是你學(xué)習(xí)PHP技術(shù)的最佳選擇,想要PHP視頻教程的小伙伴現(xiàn)在就聯(lián)系我們的咨詢老師領(lǐng)取吧,機(jī)會(huì)總是留給有準(zhǔn)備的人的!扣丁學(xué)堂PHP技術(shù)交流群:374332265。
【關(guān)注微信公眾號(hào)免費(fèi)領(lǐng)取丁豆獲取更多的學(xué)習(xí)資料】
查看更多關(guān)于“php培訓(xùn)資訊”的相關(guān)文章>>
標(biāo)簽:
PHP培訓(xùn)
PHP視頻教程
PHP開(kāi)發(fā)培訓(xùn)