202 Ioc 仿照laravel实现一个最简的ioc容器

本文通过逐步构建DI容器,探讨了依赖注入的基本概念和技术细节。从简单的bind和make开始,逐步引入闭包、反射和依赖解析等高级特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天感觉对ioc的掌握不是很好,所以决定实现一遍看看。

1.最基本的bind 和 make

容器类最少有两个方法,一个bind将类绑定容器中,一个make从容器中取得这个类

bind()

$mysql = new mysql();
$Ioc::bind('db',$mysql);

当然上面的new操作也可以用闭包来实现,一个效果,闭包的好处在于这里的new不会立刻执行

$Ioc::bind('db',function($Ioc){
    return new mysql();
});

make()

$mysql= Ioc::make('db');

2.最初版本的容器实现

根据上面的描述,那么最初的Ioc类差不多长这样,此处为了更加直白,所以本应该是static的方法被写成了动态

<?php
class Ioc{

    function bind($alias,$concrete){
        $this->$alias = $concrete;
    }

    function make($alias){
        return $this->$alias;
    }
}

有几个问题

  • 所有服务要一个一个bind进去、能否根据配置文件自动bind
  • 每次用make使用一个新服务都会重新new一次
  • 组件无法注入依赖
  • (继续第三点)组件是否可以自动注入依赖?
  • 是否可以绑定一个闭包而不是实例来实现延迟加载。

3.Ioc初始化时候就把所有服务bind进去

此处可以用配置文件的方法,此处为了简单直接将配置放在构造函数中

class Ioc {
    public function __construct(){
        //这了只写了一个服务
        $services = [
            'db' => 'mysql'
        ];

        foreach ($services as $alias => $concrete_name) {
            $concrete = new $concrete_name;
            $this->bind($alias,$concrete);
        }
    }

    function bind(){
        ... //跟上面一样
    }

    function make(){
        ... //跟上面一样 
    }
}

class mysql {
    function __construct(){
        echo "具体实现就不写了,这应该是个pdo的二次封装";
    }
}

$Ioc = new Ioc();
$Ioc->make('db');

4.用闭包的方法重写bind方法

$concrete处理成一个返回实例的闭包,并将ioc容器实例注入到每个闭包中。

function bind($abstract,$concrete,$share){
   // $this->$alias = $concrete;
    if (! $concrete instanceof Closure) {
        $concrete = $this->getClosure($abstract,$concrete);
    }
    $this->bindings[$abstract] = compact('concrete','shared');
}!

function getClosure($abstract,$concrete){
    //$c为Ioc容器对象
    return function($c) use ($abstract,$concrete){
        $method = ($abstract == $concrete) ? 'build' : 'make';
        return $c->$method($concrete);
    };
}

5.让组件内部可以使用容器的其他服务

重写make方法,判断是否符合isBuildable的条件,要么$concrete == $abstract,要么$concrete本身已经是闭包,如果不符合那么则调用build方法使之符合isBuildable

//生成实例对象 解决接口和实例化类之间依赖关系
public function make($abstract){
    // return $this->$abstract;
    $concrete = $this->getConcrete($abstract);
    if ($this->isBuildable($concrete,$abstract)){
        $object = $this->build($concrete);
    } else {
        $object = $this->make($concrete);
    }
    return $object;
}

public function isBuildable($concrete,$abstract){
    return $concrete == $abstract || $concrete instanceof Closure;
}

build

使用反射获取构造函数的依赖关系,并调用getPendencies获取依赖实例。

public function build($concrete){
   if ($concrete instanceof Closure){
        //Ioc容器实例注入
        return $concrete($this);
    }
    //反射对象
    $reflector = new ReflectionClass($concrete);
    //检查这个类是否可实例化
    if (!$reflector->isInstantiable()){
        echo $message = "Target [$concrete] is not instantiable.";
    }
    $constructor = $reflector->getConstructor();
    if (is_null($constructor)){
        return new $constructor;
    }
    $dependencies = $constructor->getParameters();
    $instances = $this->getPendencies($dependencies);
    return $reflector->newInstanceArgs($instances);
}

getDependencies 实现

public function getDependencies($parameters){
    $dependencies = [];
    foreach ($dependencies as $parameter) {
        $denpendency = $parameter->getClass();
        if (is_null($denpendency)){
            $denpendencies[] = NULL;
        } else {
            $dependencies[] = $this->resolveClass($parameter);
        }
    }
    return (array)$dependencies;
}

public function resolveClass(ReflectionParameter $parameter){
    return $this->make($parameter->getClass()->name);
}

6.将单例的服务单独bind

使用反射自动bind

使用facade

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值