PHP依赖注入那些事

作者:南丞 时间:2017-11-10 04:03

PHP依赖注入

依赖注入的实质就是把一个类不可更换的部分和可更换的部分分离开来 通过注入的方式使用 从而达到解耦的目的

1.简单的依赖注入实现:

<?php

/**
 *  一个数据库连接类
 */
class Mysql{
    private $host;
    private $port;
    private $username;
    private $password;
    private $db_name;

    public function __construct()
    {
        $this->host = '127.0.0.1';
        $this->port = '3306';
        $this->username = 'root';
        $this->password = 'root';
        $this->db_name  = 'laravel5';
    }

    public function connect() {
        return mysqli_connect($this->host,$this->username ,$this->password,$this->db_name,$this->port);
    }
}

/**
 *  使用
 */

$db = new Mysql();
$con = $db->connect();


//上面是我们的一个简单的数据库操作类  显然,数据库的配置是可以更换的部分  因此我们需要把它拎出来。


// 依赖注入的写法

/**
 * 配置类
 * Class MysqlConfiguration
 */
class MysqlConfiguration
{
    private $host;
    private $port;
    private $username;
    private $password;
    private $db_name;
    public function __construct( $host,  $port,  $username,  $password, $db_name)
    {
        $this->host = $host;
        $this->port = $port;
        $this->username = $username;
        $this->password = $password;
        $this->db_name = $db_name;
    }
    public function getHost()
    {
        return $this->host;
    }
    public function getPort()
    {
        return $this->port;
    }
    public function getUsername()
    {
        return $this->username;
    }
    public function getPassword()
    {
        return $this->password;
    }
    public function getDbName()
    {
        return $this->db_name;
    }
}

/**
 * 数据库操作类
 */

    class Mysql1
    {
        private $configuration;
        public function __construct($config)
        {
            $this->configuration = $config;
        }
        public function connect(){
            return mysqli_connect($this->configuration->getHost(),$this->configuration->getUsername() ,$this->configuration->getPassword,$this->configuration->getDbName(),$this->configuration->getPort());
        }
    }

/**
 *  使用的时候
 */
    $config = new MysqlConfiguration('127.0.0.1','root','','my_db',22);
    $db = new Mysql1($config);
    $con = $db->connect();

    //$config是注入Mysql的,这就是所谓的依赖注入。

2.上面是两个类的注入,在来看看三个类的注入:

<?php
class Bim{
    public function doSomething() {
        echo __METHOD__,'|';
    }
}

class Bar{
    public function doSomething(){
        $bim = new Bim();
        $bim->doSomething();
        echo __METHOD__,'|';
    }
}

class Foo{
    public function doSomething(){
        $bar = new Bar();
        $bar->doSomething();
        echo __METHOD__;
    }
}
/**
  *  $foo = new Foo();
  *  $foo->doSomething();
  */  

//Bim::doSomething|Bar::doSomething|Foo::doSomething


/**
 * 使用依赖注入的思路是应用程序用到Foo类,
 * Foo类需要Bar类,
 * Bar类需要Bim类,
 * 那么先创建Bim类,
 * 再创建Bar类并把Bim注入,
 * 再创建Foo类,并把Bar类注入,
 * 再调用Foo方法,Foo调用Bar方法,
 * 接着做些其它工作。
 */

    class Bim
    {
        public function doSomething()
        {
            echo __METHOD__, '|';
        }
    }

    class Bar
    {
        private $bim;

        public function __construct(Bim $bim)
        {
            $this->bim = $bim;
        }

        public function doSomething()
        {
            $this->bim->doSomething();
            echo __METHOD__, '|';
        }
    }

    class Foo
    {
        private $bar;

        public function __construct(Bar $bar)
        {
            $this->bar = $bar;
        }

        public function doSomething()
        {
            $this->bar->doSomething();
            echo __METHOD__;
        }
    }

    $foo = new Foo(new Bar(new Bim()));
    $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

/**
 * 这就是控制反转模式。
 * 依赖关系的控制反转到调用链的起点。
 * 这样你可以完全控制依赖关系,通过调整不同的注入对象,来控制程序的行为
 *
 */

3,上面的三个类互相依赖 使用依赖注入容器后的思路是应用程序需要到Foo类,就从容器内取得Foo类,容器创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar注入,应用程序调用Foo方法,Foo调用Bar方法,接着做些其它工作.这样就可以控制依赖关系,通过调整不同的注入对象,来控制程序的行为。例如Foo类用到了memcache,可以在不修改Foo类代码的情况下,改用redis。 总之容器负责实例化,注入依赖,处理依赖关系等工作。代码实现如下

 class Container
    {
        private $s = array();

        function __set($k, $c)
        {
            $this->s[$k] = $c;
        }

        function __get($k)
        {
            return $this->s[$k]($this);
        }
    }

    //使用魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。

    //调用

        $c = new Container();

        $c->bim = function () {
            return new Bim();
        };
        $c->bar = function ($c) {
            return new Bar($c->bim);
        };
        $c->foo = function ($c) {
            return new Foo($c->bar);
        };

        // 从容器中取得Foo
        $foo = $c->foo;
        $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

4, 使用静态化的容器代码来实现容器:

  /**
   *  静态的注入容器
   * Class Ioc
   */

    class Ioc{
        protected static $registry = [];

        public static function bind($name, $resolver){
            static::$registry[$name] = $resolver;
        }

        public static function make($name){
            if(isset(self::$registry[$name])) {
                $resolver = self::$registry[$name];
                return $resolver();
            }
            throw new Exception('容器中没哟这个类');
        }
    }

    /**
     * 使用容器
     */
    IoC::bind('bim', function () {
        return new Bim();
    });
    IoC::bind('bar', function () {
        return new Bar(IoC::make('bim'));
    });
    IoC::bind('foo', function () {
        return new Foo(IoC::make('bar'));
    });


    // 从容器中取得Foo
    $foo = IoC::make('foo');
    $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

5, 从上面代码中我们会发现 我们所涉及到的类 直接实例化 没有 构造方法也没有参数 所以得写一个解析 如下代码:

    <?php

    /**
     *  依赖注入的高级操作   自动绑定
     */

    /**
     *  如下的三个基础类互相依赖
     */
    class Bim
    {
        public function doSomething()
        {
            echo __METHOD__, '|';
        }
    }

    class Bar
    {
        private $bim;

        public function __construct(Bim $bim)
        {
            $this->bim = $bim;
        }

        public function doSomething()
        {
            $this->bim->doSomething();
            echo __METHOD__, '|';
        }
    }

    class Foo
    {
        private $bar;

        public function __construct(Bar $bar)
        {
            $this->bar = $bar;
        }

        public function doSomething()
        {
            $this->bar->doSomething();
            echo __METHOD__;
        }
    }

    /**
     * 做一个依赖容器
     */
    class Container{
        private  $s = [];

        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)
         */
        public function build($className) {
            // 如果是匿名函数(Anonymous functions),也叫闭包函数(closures)
            if ($className instanceof Closure) {
                // 执行闭包函数,并将结果
                return $className($this);
            }

            //得到一个反射类
            $reflector = new ReflectionClass($className);

            /** 获取类的构造函数 */
            $constructor = $reflector->getConstructor();

            // 若无构造函数,直接实例化并返回
            if (is_null($constructor)) {
                return new $className;
            }

            // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
            $parameters = $constructor->getParameters();

            // 递归解析构造函数的参数
            $dependencies = $this->getDependencies($parameters);

            // 创建一个类的新实例,给出的参数将传递到类的构造函数。
            return $reflector->newInstanceArgs($dependencies);
        }

        /**
         * 解析带参数的类
         * @param $parameters
         * @return array
         */
        public function getDependencies($parameters)
        {
            $dependencies = [];
            /** 循环所有的参数 查看类型 */
            foreach ($parameters as $parameter) {

                $dependency = $parameter->getClass();

                if (is_null($dependency)) {
                    // 是变量,有默认值则设置默认值
                    $dependencies[] = $this->resolveNonClass($parameter);
                } else {
                    // 是一个类,递归解析
                    $dependencies[] = $this->build($dependency->name);
                }
            }
            return $dependencies;
        }

        /**
         * 给参数设置默认值
         * @param $parameter
         * @return mixed
         * @throws Exception
         */
        public function resolveNonClass($parameter)
        {
            // 有默认值则返回默认值
            if ($parameter->isDefaultValueAvailable()) {
                return $parameter->getDefaultValue();
            }

            throw new Exception('没有默认值');
        }
    }


    /**
     * 使用一哈哈
     */

    $c = new Container();
    $c->bar = 'Bar';
    $c->foo = function ($c) {
        return new Foo($c->bar);
    };
    // 从容器中取得Foo
    $foo = $c->foo;
    $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

    // ----
    $di = new Container();

    $di->foo = 'Foo';

    /** @var Foo $foo */
    $foo = $di->foo;

    var_dump($foo);

注意: 反射的一些常识:

    new ReflectionClass(类名)  //对某个类做反射
    ReflectionClass::__construct  初始化 ReflectionClass 
    ReflectionClass::export  导出一个类
    ReflectionClass::getConstant  获取定义过的一个常量
    ReflectionClass::getConstants  获取一组常量
    ReflectionClass::getConstructor  获取类的构造函数
    ReflectionClass::getDefaultProperties  获取默认属性
    ReflectionClass::getDocComment  获取文档注释
    ReflectionClass::getEndLine  获取最后一行的行数
    ReflectionClass::getExtension  根据已定义的类获取所在扩展的 ReflectionExtension 对象
    ReflectionClass::getExtensionName  获取定义的类所在的扩展的名称
    ReflectionClass::getFileName  获取定义类的文件名
    ReflectionClass::getInterfaceNames  获取接口interface名称
    ReflectionClass::getInterfaces  获取接口
    ReflectionClass::getMethod  获取一个类方法的 ReflectionMethod
    ReflectionClass::getMethods  获取方法的数组
    ReflectionClass::getModifiers  获取类的修饰符
    ReflectionClass::getName  获取类名
    ReflectionClass::getNamespaceName  获取命名空间的名称
    ReflectionClass::getParentClass  获取父类
    ReflectionClass::getProperties  获取一组属性
    ReflectionClass::getProperty  获取类的一个属性的 ReflectionProperty
    ReflectionClass::getShortName  获取短名
    ReflectionClass::getStartLine  获取起始行号
    ReflectionClass::getStaticProperties  获取静态static属性
    ReflectionClass::getStaticPropertyValue  获取静态static属性的值
    ReflectionClass::getTraitAliases  返回 trait 别名的一个数组
    ReflectionClass::getTraitNames  返回这个类所使用 traits 的名称的数组
    ReflectionClass::getTraits  返回这个类所使用的 traits 数组
    ReflectionClass::hasConstant  检查常量是否已经定义
    ReflectionClass::hasMethod  检查方法是否已定义
    ReflectionClass::hasProperty  检查属性是否已定义
    ReflectionClass::implementsInterface  接口的实现
    ReflectionClass::inNamespace  检查是否位于命名空间中
    ReflectionClass::isAbstract  检查类是否是抽象类abstract
    ReflectionClass::isAnonymous  检查类是否是匿名类
    ReflectionClass::isCloneable  返回了一个类是否可复制
    ReflectionClass::isFinal  检查类是否声明为 final
    ReflectionClass::isInstance  检查类的实例
    ReflectionClass::isInstantiable  检查类是否可实例化
    ReflectionClass::isInterface  检查类是否是一个接口interface
    ReflectionClass::isInternal  检查类是否由扩展或核心在内部定义
    ReflectionClass::isIterateable  检查是否可迭代iterateable
    ReflectionClass::isSubclassOf  检查是否为一个子类
    ReflectionClass::isTrait  返回了是否为一个 trait
    ReflectionClass::isUserDefined  检查是否由用户定义的
    ReflectionClass::newInstance  从指定的参数创建一个新的类实例
    ReflectionClass::newInstanceArgs  从给出的参数创建一个新的类实例
    ReflectionClass::newInstanceWithoutConstructor  创建一个新的类实例而不调用它的构造函数
    ReflectionClass::setStaticPropertyValue  设置静态属性的值
    ReflectionClass::__toString  返回 ReflectionClass 对象字符串的表示形式

评论:

回到顶部
回到顶部