前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP 依赖注入(DI)和控制反转(IoC)

PHP 依赖注入(DI)和控制反转(IoC)

作者头像
友儿
发布2022-09-11 14:22:53
5840
发布2022-09-11 14:22:53
举报
文章被收录于专栏:友儿友儿

PHP 依赖注入(DI)和控制反转(IoC)

说明|优点
  • 用来减少程序间耦合的一种设计模式
  • 依赖注入可以有效分离对象和所需资源,是整个体系变的灵活

概念
  • 依赖注入控制反转对同一件事情的不同描述(描述的角度不同)
  • 依赖注入:应用程序依赖容器创建并注入它所需要的外部资源
  • 控制反转:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源

以代码为例子,来深刻理解一下
这个例子为正常User类调用FileLog类时的写法
代码语言:javascript
复制
<?php

//文件记录日志
class FileLog{
     public function write(){
            echo 'file log write...';
        }
}
//登录成功,记录登录日志
class User
{
    protected $log;
    public function __construct()
    {
        $this->log = new FileLog();
    }
    public function login()
    {
        echo 'login success...';
        $this->log->write();
    }
}
$user = new User();
$user->login();
/**
*当User类需要FileLog类时,FileLog类就相当于User类的外部资源
*主动实例化FileLog类
*弊端:当我们不想用文件存储日志时,我们需要改动User类中构造方法
*/
?>
这个例子为使用了IoC/DI容器后例子
代码语言:javascript
复制
<?php
//文件记录日志
class FileLog{
     public function write(){
            echo 'file log write...';
        }
}
//登录成功,记录登录日志
class User
{
    private $log;
    public function setLog(FileLog $log){
        $this->log = $log;
    }
    public function write(){
        $this->log->write();
    }
}
$fileLog = new FileLog();
$user = new User();
/**当User类需要FileLog类的时候,采用注入的方式*/
$user->steLog($fileLog);
$user->write();
?>
在了解下面代码之前,我们需要简单的回顾一下匿名函数以及它使用外部参数和如何调用,同样,我们以一段代码为例子来简单说明一下
代码语言:javascript
复制
<?php
//定义一个简单的匿名函数
$p1 = 'p1';//匿名函数外部参数需要use($param)才能使用
$test = function ($p0)use ($p1){
    echo $p0,'------',$p1;
};
//调用匿名函数
$test('p0');
/**
* 输出:
* p0------p1
*/
?>
上面简单的通过代码了解了上面代码,下面我们把上面代码完善优化一下
代码语言:javascript
复制
<?php

interface Log
{
    public function write();
}

// 文件记录日志
class FileLog implements Log
{
    public function write(){
        echo 'file log write...';
    }
}

// 数据库记录日志
class DatabaseLog implements Log
{
    public function write(){
        echo 'database log write...';
    }
}

class User
{
    protected $log;
    public function __construct(Log $log)
    {
        $this->log = $log;
    }
    public function login()
    {
        // 登录成功,记录登录日志
        echo 'login success...';
        $this->log->write();
    }
}

class Ioc
{
    public $binding = [];

    public function bind($abstract, $concrete)
    {
        //这里为什么要返回一个closure呢?因为bind的时候还不需要创建User对象,所以采用closure等make的时候再创建FileLog;
        $this->binding[$abstract]['concrete'] = function ($ioc) use ($concrete) {
            echo 3,"-----";
            return $ioc->build($concrete);
        };

    }

    public function make($abstract)
    {
        //根据key获取binding的值
        echo 1,"-----";
        $concrete = $this->binding[$abstract]['concrete'];
        echo 2,"-----";
        return $concrete($this);
    }

    // 创建对象
    public function build($concrete) {
        echo $concrete,"----";
        $reflector = new ReflectionClass($concrete);
        echo 4,"-----";
        $constructor = $reflector->getConstructor(); //获取反射类的构造方法
        var_dump(is_null($constructor));
        if(is_null($constructor)) {
            echo "--------",5,"-------";
            return $reflector->newInstance(); // 从指定的参数创建一个新的类实例
        }else {
            echo "--------",6,"-------";
            $dependencies = $constructor->getParameters(); //获取参数
            $instances = $this->getDependencies($dependencies); // 获取依赖
            return $reflector->newInstanceArgs($instances); //从给出的参数创建一个新的类实例
        }
    }

    // 获取参数的依赖
    protected function getDependencies($paramters) {
        $dependencies = [];
        echo 7,"-------";
        foreach ($paramters as $paramter) {
            $dependencies[] = $this->make($paramter->getClass()->name);  //getClass 返回对象实例 obj 所属类的名字。如果 obj 不是一个对象则返回 FALSE。
        }
        return $dependencies;
    }

}

//实例化IoC容器
$ioc = new Ioc();
$ioc->bind('Log','FileLog');
$ioc->bind('user','User');
$user = $ioc->make('user');
$user->login();
/**
*运行
*$ php Test.php
* 1-----2-----3-----User----4-----bool(false)------6-------7-------
* 1-----2-----3-----FileLog----4-----bool(true)-------5-------
* login success...file log write...
* 由上面可以看出来,
*    $ioc = new Ioc(); //实例化Ioc容器
*
*    //匿名函数外部参数需要use($param)才能使用
*    $ioc->bind('Log','FileLog');//把参数为外部参数为FileLog的匿名函数赋值给以$this->binding['Log']['concrete']
*    $ioc->bind('user','User');//把参数为外部参数为User的匿名函数赋值给以$this->binding['user']['concrete']
*
*    $user = $ioc->make('user');//获取  $this->binding['user']['concrete']  的匿名函数,然后调用匿名函数  $concrete($this),    
*   走3,调用build($concrete)方法,利用反射类获取到原始类的属性和方法。判断 is_null($constructor) 为false(因为User类有构造方法),  
*   走6,获取构造方法需要传入的参数,走7,走1, 获取依赖类 $this->binding['Log']['concrete'] ,往次反复,加载到所需要的依赖类。
*   最后执行 
*    $user->login();
*    login success...file log write... 
*/
?>

运行

代码语言:javascript
复制
$ php Test.php
1-----2-----3-----User----4-----bool(false)------6-------7-------
1-----2-----3-----FileLog----4-----bool(true)-------5-------
login success...file log write...

由上面可以看出来

代码语言:javascript
复制
//实例化Ioc容器
$ioc = new Ioc();
// 匿名函数外部参数需要use($param)才能使用
//把参数为外部参数为FileLog的匿名函数赋值给以$this->binding['Log']['concrete']
$ioc->bind('Log','FileLog');
//把参数为外部参数为User的匿名函数赋值给以$this->binding['user']['concrete']
$ioc->bind('user','User');
//获取  $this->binding['user']['concrete']  的匿名函数,然后调用匿名函数  $concrete($this)
$user = $ioc->make('user');
//走3,调用build($concrete)方法,利用反射类获取到原始类的属性和方法。判断 is_null($constructor) 为false(因为User类有构造方法
//走6,获取构造方法需要传入的参数,走7,走1, 获取依赖类 $this->binding['Log']['concrete'] ,往次反复,加载到所需要的依赖类。最后执行 
$user->login();
//login success...file log write... 

Ioc 容器维护 binding 数组记录 bind 方法传入的键值对如:log=>FileLog, user=>User

拓展:
  • ReflectionClass 类报告了一个类的有关信息。
代码语言:javascript
复制
<?php

class X {
   
}

class_alias('X','Y');
class_alias('Y','Z');
$z = new ReflectionClass('Z');
echo $z->getName(); // X

?>
代码语言:javascript
复制
<?php
/**
* abc
*/
class a{}

$ref = new ReflectionClass('a');
$ref = unserialize(serialize($ref));
var_dump($ref);
var_dump($ref->getDocComment());

// object(ReflectionClass)#2 (1) {
//   ["name"]=>
//   string(1) "a"
// }
// PHP Fatal error:  ReflectionClass::getDocComment(): Internal error: Failed to retrieve the reflection object
?>

了解更多ReflectionClass的更多用法请移步到PHP官网

另:欢迎大家一起交流学习,以上为个人理解,如有错误,请在评论区留言纠正,不甚感激。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • PHP 依赖注入(DI)和控制反转(IoC)
    • 说明|优点
      • 概念
        • 以代码为例子,来深刻理解一下
          • 这个例子为正常User类调用FileLog类时的写法
          • 这个例子为使用了IoC/DI容器后例子
        • 在了解下面代码之前,我们需要简单的回顾一下匿名函数以及它使用外部参数和如何调用,同样,我们以一段代码为例子来简单说明一下
          • 上面简单的通过代码了解了上面代码,下面我们把上面代码完善优化一下
            • 拓展:
              • 另:欢迎大家一起交流学习,以上为个人理解,如有错误,请在评论区留言纠正,不甚感激。
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档