Maison  >  Article  >  développement back-end  >  Comment analyser l'injection de dépendance PHP

Comment analyser l'injection de dépendance PHP

巴扎黑
巴扎黑original
2017-08-16 09:27:511400parcourir


Résumé : Les modules de haut niveau ne devraient pas dépendre de modules de bas niveau, les deux devraient dépendre d'abstractions. Les abstractions ne devraient pas dépendre des détails, les détails devraient dépendre des abstractions.

Tout d'abord, regardons un morceau de code :

class A{        public function echo()
        {                echo 'A'.PHP_EOL;
        }
}class EchoT {        protected  $t;        public function __construct()
        {              $this->t = new A();
        }        public function echo(){                $this->t->echo();
        }
}

Au départ, nous utilisons tous la nouvelle méthode en interne, et la classe EchoT s'appuie fortement sur la classe A. Chaque fois que la classe A change, la classe EchoT doit également changer.

Optimisons le code

class EchoT {        protected  $t;        public function __construct($t)  //构造器注入由构造器注入到其中        {              $this->t = $t;
        }

Vous pouvez le voir si nous faisons cela. À grande échelle, nous dissocions le programme. Quelle que soit la manière dont vous modifiez la classe A, la classe EchoT n'a pas besoin d'être modifiée. Ne dépend plus de A. Mais un nouveau problème est revenu. Nous n'avons plus que A maintenant. Et si B ou CDEFG arrivait ?

面向接口

interface T{        public function echo();
}class A{        public function echo()
        {                echo 'A'.PHP_EOL;
        }
}class B implements T{        public function echo()
        {                echo 'B'.PHP_EOL;
        }
}class EchoT {        protected  $t;        public function __construct(T $t)  //构造器注入由构造器注入到其中        {              $this->t = $t;
        }        public function echo(){                $this->t->echo();
        }
}

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

工厂

function getT($str) {    if(class_exists($str)){        return new $str();
        }
}

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

依赖注入容器

namespace Core;use Psr\Container\ContainerInterface;class Container implements ContainerInterface{        protected $instance = [];//对象存储的数组
        public function __construct($path) {                $this->_autoload($path);  //首先我们要自动加载  psr-autoload
        }        public function build($className)
        {                if(is_string($className) and $this->has($className)) {                        return $this->get($className);
                }                //反射
                $reflector = new \ReflectionClass($className);                if (!$reflector->isInstantiable()) {                        throw new \Exception("Can't instantiate ".$className);
                }                // 检查类是否可实例化, 排除抽象类abstract和对象接口interface
                if (!$reflector->isInstantiable()) {                        throw new \Exception("Can't instantiate ".$className);
                }                /** @var \ReflectionMethod $constructor 获取类的构造函数 */
                $constructor = $reflector->getConstructor();                // 若无构造函数,直接实例化并返回
                if (is_null($constructor)) {                        return new $className;
                }                // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
                $parameters = $constructor->getParameters();                // 递归解析构造函数的参数
                $dependencies = $this->getDependencies($parameters);                // 创建一个类的新实例,给出的参数将传递到类的构造函数。
                $class =  $reflector->newInstanceArgs($dependencies);                $this->instance[$className] = $class;                return $class;
        }        /**
         * @param array $parameters
         * @return array
         */
        public function getDependencies(array $parameters)
        {
                $dependencies = [];                /** @var \ReflectionParameter $parameter */
                foreach ($parameters as $parameter) {                        /** @var \ReflectionClass $dependency */
                        $dependency = $parameter->getClass();                        if (is_null($dependency)) {                                // 是变量,有默认值则设置默认值
                                $dependencies[] = $this->resolveNonClass($parameter);
                        } else {                                // 是一个类,递归解析
                                $dependencies[] = $this->build($dependency->name);
                        }
                }                return $dependencies;
        }        /**
         * @param \ReflectionParameter $parameter
         * @return mixed
         * @throws \Exception
         */
        public function resolveNonClass(\ReflectionParameter $parameter)
        {                // 有默认值则返回默认值
                if ($parameter->isDefaultValueAvailable()) {                        return $parameter->getDefaultValue();
                }                throw new \Exception($parameter->getName().' must be not null');
        }        /**
         * 参照psr-autoload规范
         * @param $path
         */
        public function _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);                                return true;
                        }                        return false;
                });
        }        public function get($id)
        {                if($this->has($id)) {                        return $this->instance[$id];
                }                if(class_exists($id)){                        return $this->build($id);
                }                throw new ClassNotFoundException('class not found');  //实现的PSR规范的异常
        }        public function has($id)
        {                return isset($this->instance[$id]) ? true : false;
        }
}

使用示例

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

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

  // 检查类是否可实例化, 排除抽象类abstract和对象接口interface
                if (!$reflector->isInstantiable()) {                        throw new \Exception("Can't instantiate ".$className);
                }
因为接口T是无法实例化的,所以,一般在程序内,我们都加上别名(参照laravel框架)
$container->alisa(\Test\T::class,\Test\T\A::class);  //指定接口T使用类A(控制反转)

针对接口

下面是alias方法

      public function alias(string $key, $class, bool $singleton = true) 
        {                if($singleton) {                        $this->singleton[] = $class;
                }                $this->aliases[$key] = $class;                return $this;
        }    //同时,我们需要在build的时候进行判断是否为别名
 public function build($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];
                }

就此,一个简单的PHP容器就实现了。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn