当前位置:首页 > PHP文摘 > 列表

PHP实现依赖注入

发布:smiling 来源: PHP粉丝网  添加日期:2018-05-20 18:06:42 浏览: 评论:0 
###首先,我们来看一段代码:

  1. class A{ 
  2.         public function echo() 
  3.         { 
  4.                 echo 'A'.PHP_EOL; 
  5.         } 
  6. } 
  7. class EchoT { 
  8.         protected  $t; 
  9.         public function __construct() 
  10.         { 
  11.               $this->t = new A(); 
  12.         } 
  13.         public function echo(){ 
  14.                 $this->t->echo(); 
  15.         } 
  16. } 

初始,我们都使用new 的方式在内部进行,EchoT类严重依赖于类A。每当类A变化时,EchoT类也得进行变化。 ###我们优化一下代码:

  1. class EchoT { 
  2.         protected  $t; 
  3.         public function __construct($t)  //构造器注入由构造器注入到其中 
  4.         { 
  5.               $this->t = $t; 
  6.         } 

可以看到,这样做的话。很大程序上,我们对程序进行了解耦。类A无论你如何变动,EchoT类是不需要变动的。不再依赖于A。但是新问题又来了,我们现在只有A,万一来了B,来了CDEFG怎么办。 ###面向接口:

  1. interface T{ 
  2.         public function echo(); 
  3. } 
  4.  
  5. class A{ 
  6.         public function echo() 
  7.         { 
  8.                 echo 'A'.PHP_EOL; 
  9.         } 
  10. } 
  11.  
  12. class B implements T{ 
  13.         public function echo() 
  14.         { 
  15.                 echo 'B'.PHP_EOL; 
  16.         } 
  17. } 
  18. class EchoT { 
  19.         protected  $t; 
  20.         public function __construct(T $t)  //构造器注入由构造器注入到其中 
  21.         { 
  22.               $this->t = $t; 
  23.         } 
  24.         public function echo(){ 
  25.                 $this->t->echo(); 
  26.         } 
  27. } 

将T抽象出为接口,这样,EchoT类中的echo方法变成一个抽象的方法,不到运行那一刻,不知道他们的Method方式是怎么实现的。###工厂:

  1. function getT($str) { 
  2.     if(class_exists($str)){ 
  3.         return new $str(); 
  4.         } 
  5. } 
T要使用哪个是不明确的,因此,我们可以将其工厂化。【看上去很简单,在DI实际上有体现】
 
###DI(重点来了) 首先,我们看一下PHP的psr规范。
 
http://www.php-fig.org/psr/psr-11/
 
官方定义的接口
 
Psr\Container\ContainerInterface

包含两个方法:

function get($id);

function has($id);

仔细看上面的工厂,是不是和get($id)很一致,PHP官方将其定义为容器(Container,我个人理解,就是一个复杂的工厂)
 
dependency injection container
 
依赖注入容器.
 
  1. namespace Core; 
  2. use Psr\Container\ContainerInterface; 
  3. class Container implements ContainerInterface 
  4. { 
  5.         protected $instance = [];//对象存储的数组 
  6.         public function __construct($path) { 
  7.                 $this->_autoload($path);  //首先我们要自动加载  psr-autoload 
  8.         } 
  9.  
  10.         public function build($className) 
  11.         { 
  12.                 if(is_string($className) and $this->has($className)) { 
  13.                         return $this->get($className); 
  14.                 } 
  15.                 //反射 
  16.                 $reflector = new \ReflectionClass($className); 
  17.                 if (!$reflector->isInstantiable()) { 
  18.                         throw new \Exception("Can't instantiate ".$className); 
  19.                 } 
  20.                 // 检查类是否可实例化, 排除抽象类abstract和对象接口interface 
  21.                 if (!$reflector->isInstantiable()) { 
  22.                         throw new \Exception("Can't instantiate ".$className); 
  23.                 } 
  24.                 /** @var \ReflectionMethod $constructor 获取类的构造函数 */ 
  25.                 $constructor = $reflector->getConstructor(); 
  26.                 // 若无构造函数,直接实例化并返回 
  27.                 if (is_null($constructor)) { 
  28.                         return new $className; 
  29.                 } 
  30.                 // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表 
  31.                 $parameters = $constructor->getParameters(); 
  32.                 // 递归解析构造函数的参数 
  33.                 $dependencies = $this->getDependencies($parameters); 
  34.                 // 创建一个类的新实例,给出的参数将传递到类的构造函数。 
  35.                 $class =  $reflector->newInstanceArgs($dependencies); 
  36.                 $this->instance[$className] = $class; 
  37.                 return $class; 
  38.         } 
  39.  
  40.         /** 
  41.          * @param array $parameters 
  42.          * @return array 
  43.          */ 
  44.         public function getDependencies(array $parameters) 
  45.         { 
  46.                 $dependencies = []; 
  47.                 /** @var \ReflectionParameter $parameter */ 
  48.                 foreach ($parameters as $parameter) { 
  49.                         /** @var \ReflectionClass $dependency */ 
  50.                         $dependency = $parameter->getClass(); 
  51.                         if (is_null($dependency)) { 
  52.                                 // 是变量,有默认值则设置默认值 
  53.                                 $dependencies[] = $this->resolveNonClass($parameter); 
  54.                         } else { 
  55.                                 // 是一个类,递归解析 
  56.                                 $dependencies[] = $this->build($dependency->name); 
  57.                         } 
  58.                 } 
  59.                 return $dependencies; 
  60.         } 
  61.  
  62.         /** 
  63.          * @param \ReflectionParameter $parameter 
  64.          * @return mixed 
  65.          * @throws \Exception 
  66.          */ 
  67.         public function resolveNonClass(\ReflectionParameter $parameter) 
  68.         { 
  69.                 // 有默认值则返回默认值 
  70.                 if ($parameter->isDefaultValueAvailable()) { 
  71.                         return $parameter->getDefaultValue(); 
  72.                 } 
  73.                 throw new \Exception($parameter->getName().' must be not null'); 
  74.         } 
  75.         /** 
  76.          * 参照psr-autoload规范 
  77.          * @param $path 
  78.          */ 
  79.         public function _autoload($path) { 
  80.                 spl_autoload_register(function(string $class) use ($path) { 
  81.                         $file = DIRECTORY_SEPARATOR.str_replace('\\',DIRECTORY_SEPARATOR, $class).'.php'; 
  82.                         if(is_file($path.$file)) { 
  83.                                 include($path.$file); 
  84.                                 return true; 
  85.                         } 
  86.                         return false; 
  87.                 }); 
  88.         } 
  89.  
  90.         public function get($id) 
  91.         { 
  92.                 if($this->has($id)) { 
  93.                         return $this->instance[$id]; 
  94.                 } 
  95.                 if(class_exists($id)){ 
  96.                         return $this->build($id); 
  97.                 } 
  98.                 throw new ClassNotFoundException('class not found');  //实现的PSR规范的异常 
  99.         } 
  100.  
  101.         public function has($id) 
  102.         { 
  103.                 return isset($this->instance[$id]) ? true : false; 
  104.         } 
  105. } 

####使用示例:

  1. $container = new Container('../');//假设这是路径 
  2. $echoT = $container->get(\Test\EchoT::class);     //假设echoT类的命名空间是\Test 
  3. $echoT->echo(); 

这个时候,会出现一个问题:

  1. // 检查类是否可实例化, 排除抽象类abstract和对象接口interface 
  2.  if (!$reflector->isInstantiable()) { 
  3.      throw new \Exception("Can't instantiate ".$className); 
  4.   } 
因为接口T是无法实例化的,所以,一般在程序内,我们都加上别名(参照laravel框架).

$container->alisa(\Test\T::class,\Test\T\A::class);  //指定接口T使用类A(控制反转).

####针对接口 下面是alias方法:

  1.   public function alias(string $key, $class, bool $singleton = true)  
  2.     { 
  3.             if($singleton) { 
  4.                     $this->singleton[] = $class; 
  5.             } 
  6.             $this->aliases[$key] = $class; 
  7.             return $this; 
  8.     } 
  9. //同时,我们需要在build的时候进行判断是否为别名 
  10. lic function build($className) 
  11.     { 
  12.             if(is_string($className) and $this->has($className)) { 
  13.                     return $this->get($className); 
  14.             } 
  15.             if(isset($this->aliases[$className])) { 
  16.                     if(is_object($this->aliases[$className])) { 
  17.                            return $this->aliases[$className]; 
  18.                     } 
  19.                     $className = $this->aliases[$className]; 
  20.             } 

就此,一个简单的PHP容器就实现了。###个人实现代码.

Tags: PHP实现依赖注入

分享到: