手撸php框架6.1--构建DI容器之实现

本文详细介绍了一个DI容器的具体实现过程,包括类的注册、单例注册、依赖注入等核心功能,并提供了具体的代码示例。

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

只考虑实例化一个类的话,一个简单的思路是:

class B
{}
class A
{
public function __construction(B $b)
}
set('a', 'A');//将类A注册为a
get('a');

在get时会这么解决实例化A的问题:
1)首先利用php的反射类ReflectionClass,构造类A的反射对象。
2)然后利用反射对象的getConstructor方法解析构造方法的参数
3)再然后是获取构造函数参数的值,有默认值得给默认值,如果是依赖其他类的实现,则调用get来实现(这相当于一个递归)
4)最后是使用反射对象的newInstanceArgs(构造函数的参数数组)方法,传入构造函数需要的参数并实例化一个对象

值得注意的是,在解析构造方法依赖时,对于不具有默认值的参数,我们会生成一个Instance对象代替,之后会替换为相应的参数对象。
当然除了类实例化的情况,还有其他情况需要解决,不过相对来说就比较简单了,我们直接上代码。
1.注册方法

public function register($class, $define, array $params = [])
{
    $define = $this->formatDefine($class, $define);
    $this->defineMap[$class] = $define;
    $this->paramsMap[$class] = $params;
    unset($this->singletonMap[$class]);
    return $this;
}

2.注册单例方法

public function registerSingleton($class, $define, array $params = [])
{
    $define = $this->formatDefine($class, $define);
    $this->defineMap[$class] = $define;
    $this->paramsMap[$class] = $params;
    $this->singletonMap[$class] = null;
    return $this;
}

3.格式化定义数组的方法

public function formatDefine($class, $define)
{
    $result = [];

    if (empty($define)) {
        $result['class'] = $class;
    } else if (is_string($define)) {
        $result['class'] = $define;
    } else if (is_callable($define, true)) {
        $result = $define;
    } else if (is_object($define)) {
        $result = $define;
    } else if (is_array($define)) {
        if (!array_key_exists('class', $define) && (strpos($class, '\\') !== false)) {
            $define['class'] = $class;
        } else {
            throw new Exception('Please provide class value!');
        }
        $result = $define;
    } else {
        throw new Exception('Define is illegal!');
    }

    return $result;
}

4.获取对象方法

public function resolve($class, $params = [], $config = [])
{
    $result = null;

    if (isset($this->singletonMap[$class])) {
        $result = $this->singletonMap[$class];
    } else if (!isset($this->defineMap[$class])) {
        $result = $this->generateObject($class, $params, $config);
    } else if (is_callable($this->defineMap[$class], true)) {
        $defineParams = isset($this->paramsMap[$class]) ? $this->paramsMap[$class] : [];
        $currentParams = array_merge($defineParams, $params);


        $define = $this->defineMap[$class];
        $result = call_user_func($define, $this, $currentParams, $config);

    } else if (is_object($this->defineMap[$class])) {
        $result = $this->defineMap[$class];
        $this->singletonMap[$class] = $this->defineMap[$class];
    } else if (is_array($this->defineMap[$class])) {
        $define = $this->defineMap[$class];
        $concrete = $define['class'];
        unset($define['class']);

        $config = array_merge($define, $config);
        $defineParams = isset($this->paramsMap[$class]) ? $this->paramsMap[$class] : [];
        $currentParams = array_merge($defineParams, $params);

        if ($concrete === $class) {
            $result = $this->generateObject($class, $currentParams, $config);
        } else {
            $result = $this->resolve($concrete, $currentParams, $config);
        }
    } else {
        throw new Exception('Error');
    }

    if (array_key_exists($class, $this->singletonMap) && is_object($result)) {
        $this->singletonMap[$class] = $result;
    }
    return $result;
}

5.实例化类的方法

protected function generateObject($class, array $params = [], array $config = [])
{
    list ($reflection, $dependencies) = $this->parseConstructorDependency($class);

    foreach ($params as $index => $param) {
        $dependencies[$index] = $param;
    }

    $dependencies = $this->resolveConstructorDependency($dependencies, $reflection);
    if (empty($config)) {
        return $reflection->newInstanceArgs($dependencies);
    }

    if (!empty($dependencies) && is_a($class, 'tank\base\BaseObject', true)) {
        $dependencies[count($dependencies) - 1] = $config;
        return $reflection->newInstanceArgs($dependencies);
    } else {
        $object = $reflection->newInstanceArgs($dependencies);
        foreach ($config as $name => $value) {
            $object->$name = $value;
        }
        return $object;
    }
}

6.解析构造函数依赖的方法

protected function parseConstructorDependency($class)
{
    if (isset($this->reflectionMap[$class])) {
        return [$this->reflectionMap[$class], $this->dependencyMap[$class]];
    }

    $dependencies = [];
    $reflection = new \ReflectionClass($class);

    $constructor = $reflection->getConstructor();
    if ($constructor !== null) {
        foreach ($constructor->getParameters() as $param) {
            if ($param->isDefaultValueAvailable()) {
                $dependencies[] = $param->getDefaultValue();
            } else {
                $dependClass = $param->getClass();
                $isClass = !is_null($dependClass) ? true : false;
                $dependencies[] = Instance::generate($isClass ? $dependClass->getName() : null);
            }
        }
    }

    $this->reflectionMap[$class] = $reflection;
    $this->dependencyMap[$class] = $dependencies;

    return [$reflection, $dependencies];
}

7.解决构造函数依赖的方法

protected function resolveConstructorDependency($dependencies, $reflection = null)
{
    foreach ($dependencies as $index => $dependency) {
        if ($dependency instanceof Instance) {
            if (!is_null($dependency->id)) {
                $dependencies[$index] = $this->resolve($dependency->id);
            } elseif (!is_null($reflection)) {
                $name = $reflection->getConstructor()->getParameters()[$index]->getName();
                $class = $reflection->getName();
                throw new Exception($class." missing required parameter ".$name);
            }
        }
    }
    return $dependencies;
}

最后,我们再构建BaseTank类时,留下了一个generateObject方法未实现,现在,我们的DI容器实现完毕,是时候实现这个方法了。

public static function generateObject($defineConfig, array $params = [])
{
    if (is_string($defineConfig)) {
        return static::$container->resolve($defineConfig, $params);
    } elseif (is_array($defineConfig) && isset($defineConfig['class'])) {
        $class = $defineConfig['class'];
        unset($defineConfig['class']);
        return static::$container->resolve($class, $params, $defineConfig);
    } elseif (is_callable($defineConfig, true)) {
        return call_user_func($defineConfig, $params);
    } else {
        throw new Exception('Generate Object Error!');
    }
}

ok,今儿个先到这,欲知后事如何,且听下回分解……
github源码:https://github.com/2lovecode/tank

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值