加入收藏 | 设为首页 | 会员中心 | 我要投稿 汽车网 (https://www.0577qiche.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

PHP程序员如何理解依赖注入容器

发布时间:2023-09-25 14:49:14 所属栏目:PHP教程 来源:
导读:PHP程序员如何理解依赖注入容器(dependency injection container)

背景知识

传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方
PHP程序员如何理解依赖注入容器(dependency injection container)

背景知识

传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方法,而这个方法内需要一个Bim类,就会创建Bim类,接着做些其它工作。

<?php 
// 代码【1】 
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方法,接着做些其它工作。

<?php 
// 代码【2】 
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 
这就是控制反转模式。依赖关系的控制反转到调用链的起点。这样你可以完全控制依赖关系,通过调整不同的注入对象,来控制程序的行为。例如Foo类用到了memcache,可以在不修改Foo类代码的情况下,改用redis。

使用依赖注入容器后的思路是应用程序需要到Foo类,就从容器内取得Foo类,容器创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar注入,应用程序调用Foo方法,Foo调用Bar方法,接着做些其它工作.

总之容器负责实例化,注入依赖,处理依赖关系等工作。

代码演示 依赖注入容器 (dependency injection container)

通过一个最简单的容器类来解释一下,这段代码来自 Twittee

<?php 
 
class Container 

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

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

<?php 
 
$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); 
}; //Cuoxin.com 
 
// 从容器中取得Foo 
$foo = $c->foo; 
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething 
这段代码使用了匿名函数

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

<?php 
 
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('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 
这段代码使用了后期静态绑定

依赖注入容器 (dependency injection container) 高级功能

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

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

注释解析器(Annotations)

延迟注入(Lazy injection)

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

<?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 = 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.'); 
    } 

 
// ---- 
$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); 
/* 
Foo#10 (1) { 
  private $bar => 
  class Bar#14 (1) { 
    private $bim => 
    class Bim#16 (0) { 
    } 
  } 

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

(编辑:汽车网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章