当前位置:首页 > 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. class EchoT { 
  7.         protected  $t
  8.         public function __construct() 
  9.         { 
  10.               $this->t = new A(); 
  11.         } 
  12.         public function echo(){ 
  13.                 $this->t->echo(); 
  14.         } 

初始,我们都使用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. class A{ 
  5.         public function echo() 
  6.         { 
  7.                 echo 'A'.PHP_EOL; 
  8.         } 
  9.  
  10. class B implements T{ 
  11.         public function echo() 
  12.         { 
  13.                 echo 'B'.PHP_EOL; 
  14.         } 
  15. class EchoT { 
  16.         protected  $t
  17.         public function __construct(T $t)  //构造器注入由构造器注入到其中 
  18.         { 
  19.               $this->t = $t
  20.         } 
  21.         public function echo(){ 
  22.                 $this->t->echo(); 
  23.         } 

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

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

####使用示例:

  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($classNameand $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实现依赖注入

分享到: