php框架容器实现(DI)的一般方式

文章最后有一个简单的容器实现示例和使用示例,可以参考理解,对应的github源码:GitHub源码

一个仿照yii2框架容器实现的代码示例:GitHub源码 ,其中register方法对应set方法,resolve方法对应set方法
或 GitHub源码

一个摘自laravel框架容器实现和使用示例:GitHub源码

psr-11容器GitHub地址规范

1.容器类一般有get和set方法。
使用容器,一般先注册:
例如把A(具体类)注册到AInterface(接口或抽象类)上

$container->set(‘AInterface’, ‘A’);

然后通过容器的get方法获取对象

$a = $container->get('AInterface');

2.当然你也可以引入别名的概念,set时传入参数机制,get时传入参数机制,根据上下文实例化不同对象的机制等等,这些都是增强功能,核心还是set和get方法

3.set方法实现
一般有一个defines数组保存AInterface 和A之间的注册关系,
set时,直接将关系保存到defines数组

4.get方法实现
1) 有一个数组instances保存已经实例化的对象
如果这里有已经实例化的,直接取出返回

2) 在defines数组中取出和AInterface相应的类A.
3) 使用类反射获取类A的构造方法和构造方法的签名:

ReflectionClass::getConstructor()获取构造方法对象
ReflectionMethod::getParameters()获取函数签名

4) 如果签名中有对象,则通过容器的get方法获取
如果有默认参数,则使用默认参数
如果传递的有参数,则覆盖原有值
5) 最后通过ReflectionClass::newInstanceArgs()方法获取类的实例化对象

5.这样在使用类时,我们只要通过容器get它的接口或抽象,容器会自动返回绑定的具体类的实例。这就实现了依赖接口或抽象,而不依赖具体类,达到可依赖倒转的目的。

容器实现示例:
class SimpleContainer
{
    private $defines = [];
    private $instances = [];

    public function set($abstract, $concrete)
    {
        $this->defines[$abstract] = $concrete;
    }

    public function get($abstract)
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        $object = null;
        if (isset($this->defines[$abstract])) {
            $concrete = $this->defines[$abstract];
            $object =  $this->build($concrete);
        } else {
            $object =  $this->build($abstract);
        }

        $this->instances[$abstract] = $object;

        return $object;
    }

    private function build($concrete)
    {
        $reflector = new ReflectionClass($concrete);

        //如果不能实例化,抛出异常
        if (!$reflector->isInstantiable()) {
            throw new Exception('class ['.$concrete.'] can not be instantiated!');
        }

        //获取构造函数方法反射对象
        $constructor = $reflector->getConstructor();

        //类不存在构造函数,直接实例化
        if (is_null($constructor)) {
            return new $concrete;
        }

        //给每个签名参数赋值,如果参数是对象则实例化,如果有默认值则赋给默认值
        foreach ($constructor->getParameters() as $param) {
            if ($param->isDefaultValueAvailable()) {
                $dependencies[] = $param->getDefaultValue();
            } else {
                $dependClass = $param->getClass();
                $isClass = !is_null($dependClass) ? true : false;
                $dependencies[] = $isClass ? $this->get($dependClass->getName()) : null;
            }
        }
        //实例化对象
        return $reflector->newInstanceArgs($dependencies);
    }
}
容器使用示例:
require_once 'SimpleContainer.php';

interface AInterface
{
    public function run();
}

class A implements AInterface
{
    public function run()
    {
        return 'I am A!';
    }
}

class B
{
    private $example;

    public function __construct(AInterface $example)
    {
        $this->example = $example;
    }

    public function run()
    {
        $str = $this->example->run();
        return '<<<< '.$str.' >>>>';
    }
}
$container = new SimpleContainer();

//示例1
$container->set('AInterface', 'A');
$a = $container->get('AInterface');
var_dump($a->run());

//示例2
$b = $container->get('B');
var_dump($b->run());

你可能感兴趣的:(PHP,设计模式)