php

php的依赖注入容器

dafenqi
2023-08-11 / 0 评论 / 18 阅读 / 正在检测是否收录...

php的依赖注入容器

这里接着上一篇 php依赖注入,直接贴出完整代码如下:

复制代码
<?php

class C
{

public function doSomething()
{
    echo __METHOD__, '我是C类|';
}

}

class B
{

private $c;

public function __construct(C $c)
{
    $this->c = $c;
}

public function doSomething()
{
    $this->c->doSomething();
    echo __METHOD__, '我是B类|';
}

}
class A
{

private $b;

public function __construct(B $b)
{
    $this->b = $b;
}

public function doSomething()
{
    $this->b->doSomething();
    echo __METHOD__, '我是A类|';;
}

}

//这段代码使用了
魔术方法

,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。
class Container
{

private $s = array();

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

function __get($k)
{
    return $this->s[$k]($this);
}

}
$class = new Container();

$class->c = function () {

return new C();

};
$class->b = function ($class) {

return new B($class->c);

};
$class->a = function ($class) {

return new A($class->b);

};

// 从容器中取得A
$model = $class->a;
$model->doSomething(); // C::doSomething我是C类|B::doSomething我是B类|A::doSomething我是A类|
复制代码
再来一段简单的代码演示一下,容器代码来自simple di container,完整如下:

复制代码
<?php

class C
{

public function doSomething()
{
    echo __METHOD__, '我是C类|';
}

}

class B
{

private $c;

public function __construct(C $c)
{
    $this->c = $c;
}

public function doSomething()
{
    $this->c->doSomething();
    echo __METHOD__, '我是B类|';
}

}
class A
{

private $b;

public function __construct(B $b)
{
    $this->b = $b;
}

public function doSomething()
{
    $this->b->doSomething();
    echo __METHOD__, '我是A类|';;
}

}
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('c', function () {

return new C();

});
IoC::bind('b', function () {

return new B(IoC::make('c'));

});
IoC::bind('a', function () {

return new A(IoC::make('b'));

});

// 从容器中取得A
$foo = IoC::make('a');
$foo->doSomething(); // C::doSomething我是C类|B::doSomething我是B类|A::doSomething我是A类|
复制代码
这段代码使用了后期静态绑定

依赖注入容器的高级功能
真实的dependency injection container会提供更多的特性,如

自动绑定(Autowiring)或 自动解析(Automatic Resolution)
注释解析器(Annotations)
延迟注入(Lazy injection)
下面的代码在Twittee的基础上,实现了Autowiring。

复制代码
<?php
class C
{

public function doSomething()
{
    echo __METHOD__, '我是周伯通C|';
}

}

class B
{

private $c;

public function __construct(C $c)
{
    $this->c = $c;
}

public function doSomething()
{
    $this->c->doSomething();
    echo __METHOD__, '我是周伯通B|';
}

}

class A
{

private $b;

public function __construct(B $b)
{
    $this->b = $b;
}

public function doSomething()
{
    $this->b->doSomething();
    echo __METHOD__, '我是周伯通A|';;
}

}

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

}

// ----
$class = new Container();
$class->b = 'B';
$class->a = function ($class) {

return new A($class->b);

};
// 从容器中取得A
$model = $class->a;
$model->doSomething();

$di = new Container();
$di->php7 = 'A';
/* @var A $php7 /
$foo = $di->php7;
var_dump($foo);

$foo->doSomething(); //C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|object(A)#10 (1) { ["b":"A":private]=> object(B)#14 (1) { ["c":"B":private]=> object(C)#16 (0) } } C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|

?>
复制代码
以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。

若想进一步提供一个数组访问接口,如$di->php7可以写成$di'php7'],则需用到[ArrayAccess(数组式访问)接口 。

一些复杂的容器会有许多特性,欢迎博友们补充。

无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。

0

Deprecated: strtolower(): Passing null to parameter #1 ($string) of type string is deprecated in /www/wwwroot/testblog.58heshihu.com/var/Widget/Archive.php on line 1032

评论 (0)

取消