zoukankan      html  css  js  c++  java
  • php的依赖注入容器

    这里接着上一篇 php依赖注入,直接贴出完整代码如下:

    <?php
    
    class C
    {
        public function doSomething()
        {
            echo __METHOD__, '我是C类|';
        }
    }
    
    class B
    {
        private $c;
    
        public function __construct(C $c)
        {
            $this->c = $c;
        }
    
        public function doSomething()
        {
            $this->c->doSomething();
            echo __METHOD__, '我是B类|';
        }
    }
    class A
    {
        private $b;
    
        public function __construct(B $b)
        {
            $this->b = $b;
        }
    
        public function doSomething()
        {
            $this->b->doSomething();
            echo __METHOD__, '我是A类|';;
        }
    }
    
    //这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。
    class Container { private $s = array(); function __set($k, $c) { $this->s[$k] = $c; } function __get($k) { return $this->s[$k]($this); } }
    $class = new Container(); $class->c = function () { return new C(); }; $class->b = function ($class) { return new B($class->c); }; $class->a = function ($class) { return new A($class->b); }; // 从容器中取得A $model = $class->a; $model->doSomething(); // C::doSomething我是C类|B::doSomething我是B类|A::doSomething我是A类|

    再来一段简单的代码演示一下,容器代码来自simple di container,完整如下:

    <?php
    
    class C
    {
        public function doSomething()
        {
            echo __METHOD__, '我是C类|';
        }
    }
    
    class B
    {
        private $c;
    
        public function __construct(C $c)
        {
            $this->c = $c;
        }
    
        public function doSomething()
        {
            $this->c->doSomething();
            echo __METHOD__, '我是B类|';
        }
    }
    class A
    {
        private $b;
    
        public function __construct(B $b)
        {
            $this->b = $b;
        }
    
        public function doSomething()
        {
            $this->b->doSomething();
            echo __METHOD__, '我是A类|';;
        }
    }
    class IoC
    {
        protected static $registry = [];
    
        public static function bind($name, Callable $resolver)
        {
            static::$registry[$name] = $resolver;
        }
    
        public static function make($name)
        {
            if (isset(static::$registry[$name])) {
                $resolver = static::$registry[$name];
                return $resolver();
            }
            throw new Exception('Alias does not exist in the IoC registry.');
        }
    }
    
    IoC::bind('c', function () {
        return new C();
    });
    IoC::bind('b', function () {
        return new B(IoC::make('c'));
    });
    IoC::bind('a', function () {
        return new A(IoC::make('b'));
    });
    
    
    // 从容器中取得A
    $foo = IoC::make('a');
    $foo->doSomething(); // C::doSomething我是C类|B::doSomething我是B类|A::doSomething我是A类|

    这段代码使用了后期静态绑定

    依赖注入容器的高级功能

    真实的dependency injection container会提供更多的特性,如

    • 自动绑定(Autowiring)或 自动解析(Automatic Resolution)

    • 注释解析器(Annotations)

    • 延迟注入(Lazy injection)

    下面的代码在Twittee的基础上,实现了Autowiring。

    <?php
    class C
    {
        public function doSomething()
        {
            echo __METHOD__, '我是周伯通C|';
        }
    }
    
    class B
    {
        private $c;
    
        public function __construct(C $c)
        {
            $this->c = $c;
        }
    
        public function doSomething()
        {
            $this->c->doSomething();
            echo __METHOD__, '我是周伯通B|';
        }
    }
    
    class A
    {
        private $b;
    
        public function __construct(B $b)
        {
            $this->b = $b;
        }
    
        public function doSomething()
        {
            $this->b->doSomething();
            echo __METHOD__, '我是周伯通A|';;
        }
    }
    
    class Container
    {
        private $s = array();
    
        public function __set($k, $c)
        {
            $this->s[$k] = $c;
        }
    
        public function __get($k)
        {
            // return $this->s[$k]($this);
            return $this->build($this->s[$k]);
        }
    
        /**
         * 自动绑定(Autowiring)自动解析(Automatic Resolution)
         *
         * @param string $className
         * @return object
         * @throws Exception
         */
        public function build($className)
        {
            // 如果是匿名函数(Anonymous functions),也叫闭包函数(closures)
            if ($className instanceof Closure) {
                // 执行闭包函数,并将结果
                return $className($this);
            }
    
            /** @var ReflectionClass $reflector */
            $reflector = new ReflectionClass($className);
    
            // 检查类是否可实例化, 排除抽象类abstract和对象接口interface
            if (!$reflector->isInstantiable()) {
                throw new Exception("Can't instantiate this.");
            }
    
            /** @var ReflectionMethod $constructor 获取类的构造函数 */
            $constructor = $reflector->getConstructor();
    
            // 若无构造函数,直接实例化并返回
            if (is_null($constructor)) {
                return new $className;
            }
    
            // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
            $parameters = $constructor->getParameters();
    
            // 递归解析构造函数的参数
            $dependencies = $this->getDependencies($parameters);
    
            // 创建一个类的新实例,给出的参数将传递到类的构造函数。
            return $reflector->newInstanceArgs($dependencies);
        }
    
        /**
         * @param array $parameters
         * @return array
         * @throws Exception
         */
        public function getDependencies($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($parameter)
        {
            // 有默认值则返回默认值
            if ($parameter->isDefaultValueAvailable()) {
                return $parameter->getDefaultValue();
            }
    
            throw new Exception('I have no idea what to do here.');
        }
    }
    
    // ----
    $class = new Container();
    $class->b = 'B';
    $class->a = function ($class) {
        return new A($class->b);
    };
    // 从容器中取得A
    $model = $class->a;
    $model->doSomething(); 
    
    
    $di = new Container();
    $di->php7 = 'A';
    /** @var A $php7 */
    $foo = $di->php7;
    var_dump($foo);
    
    $foo->doSomething(); //C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|object(A)#10 (1) { ["b":"A":private]=> object(B)#14 (1) { ["c":"B":private]=> object(C)#16 (0) { } } } C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|
    
    ?>

    以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。

    若想进一步提供一个数组访问接口,如$di->php7可以写成$di'php7'],则需用到[ArrayAccess(数组式访问)接口 

    一些复杂的容器会有许多特性,欢迎博友们补充。

  • 相关阅读:
    part17 一些知识总结
    part16 php面向对象
    part15 php函数
    part14 php foreach循环
    part13 数组排序
    part12 php数组
    part11 php条件语句
    part10 php运算符
    part09 php字符串变量
    part08 php常量
  • 原文地址:https://www.cnblogs.com/phpper/p/7781810.html
Copyright © 2011-2022 走看看