依赖注入 理解 php,php 依赖注入的理解

依赖注入是应用于一个类的实例化需要依赖另外一个类的场景。

class Bread

{

}

class Hamburger

{

protected $materials;

public function __construct(Bread $bread)

{

$this->materials = [$bread];

}

/**

* 测试函数

*/

public function getName()

{

echo '111';

}

}

class Container

{

/**

* @var Closure[]

*/

public $binds = [];

/**

* Bind class by closure.

*

* @param string $class

* @param Closure $closure

* @return $this

*/

public function bind(string $class, Closure $closure)

{

$this->binds[$class] = $closure;

return $this;

}

/**

* Get object by class

*

* @param string $class

* @param array $params

* @return object

*/

public function make(string $class, array $params = [])

{

if (isset($this->binds[$class])) {

// 或者 if (array_key_exists($class,$this->binds)) {

return ($this->binds[$class])->call($this, $this, ...$params);

}

return new $class(...$params);

}

}

$container = new Container();

$container->bind(Hamburger::class, function (Container $container) {

// 此时使用 make 得到i的是 return new $class(...$params); 也就是返回的 Bread 类的实例,因为没有 bind Bread 这个类

$bread = $container->make(Bread::class);

return new Hamburger($bread);

});

//

echo '

';

echo '$container类';

print_r($container);

echo '


';

echo 'Hamburger参数值
';

print_r(($container->binds['Hamburger'])->call($container, $container)); // 直接使用类名或者类都可以

echo '或者
';

print_r(($container->binds[Hamburger::class])->call($container, $container));

echo '


';

echo 'Hamburger 的闭包
';

print_r(($container->binds[Hamburger::class]));

e99c4ee31dbf

image.png

在调用 bind 方法的时候,第一个参数并不一点非要传递类,是可以传递任何字符串的,只要保证 bind(‘string’) 与 make('string') 中 string 值保持一致即可,这也是laravel 中 alias别名的原理。 别传递类,是为了在使用的时候方便知道make 出来的是哪个类。这可以说是一种约定

以上方式,为了好理解,我直接取出了 make中的 return 处理,在实例使用中,如果需要使用某个类,在绑定之后,可以直接使用以下方式就可以获取到类的实例化

$hambuger = $container->make(Hamburger::class);

$container->binds[Hamburger::class] 对应的值是一个闭包。因为 Hamburger::class 在使用到 bind 方法的时候,调用第二个参数 Closure 的时候是需要传递值的,所以 ($this->binds[$class])->call($this, $this, ...$params) 中的第一个

math?formula=this%20%E6%98%AF%E5%B0%86%E6%9C%AC%E9%97%AD%E5%8C%85%E5%87%BD%E6%95%B0%E7%BB%91%E5%AE%9A%E5%88%B0%E5%BD%93%E5%89%8D%E7%B1%BB%EF%BC%88%E4%B9%9F%E5%B0%B1%E6%98%AFContainer%E4%B8%8A%EF%BC%89%EF%BC%8C%E7%AC%AC%E4%BA%8C%E4%B8%AA%E5%8F%82%E6%95%B0%E6%98%AF%20bind%20%E5%87%BD%E6%95%B0%E4%B8%AD%2C%E7%AC%AC%E4%BA%8C%E4%B8%AA%E5%8F%82%E6%95%B0%20%60function%20(Containercontainer) 中Container $container` 的实参

如果我们使用回掉的时候不需要传递参数,如以下方式,则 call(),就不需要传递第二个参数

使用 Closure::call 可以得到参数中类的实例化,们以上的测试是为了获取到 Hamburger 类的实例化

但是如果直接使用闭包,则无法获取到想要的类

Container 类不变,如果改成以下使用方式

$container = new Container();

$container->bind(Hamburger::class, function () {

return 111;

});

echo '

';

print_r(($container->binds[Hamburger::class])->call($container));

echo '


';

print_r(($container->binds[Hamburger::class])());

make 方法也可以改成

public function make(string $class, array $params = [])

{

if (isset($this->binds[$class])) {

return ($this->binds[$class])->call($this, ...$params);

}

return new $class(...$params);

}

e99c4ee31dbf

image.png

不过如开篇所说,依赖注入是应用于一个类的实例化需要依赖另外一个类的场景 如果一个类的实例化不需要依赖别的类,那么我们就不需要使用 Container 类的这种方式了

以上方式的依赖注入,需要先使用 bind 函数绑定相应的类,并在绑定的回掉中将构造函数需要的类 Bread 先实例化,这样其实并没有得到很好的优化。我们可以使用PHP的反射机制,来自动的实例化需要的参数类,而不用手动 bind

/**

* @param string $class

* @param array $params

* @return mixed|object

* @throws ReflectionException

*/

public function make(string $class, array $params = [])

{

if (isset($this->binds[$class])) {

return ($this->binds[$class])->call($this, $this, ...$params);

}

return $this->binds[$class] = $this->resolve($class, $params);

}

/**

* Get object by reflection

*

* @param $abstract

* @return object

* @throws ReflectionException

*/

protected function resolve($abstract)

{

// 获取反射对象

$constructor = (new ReflectionClass($abstract))->getConstructor();

// 构造函数未定义,直接实例化对象

if (is_null($constructor)) {

// 如果没有构造函数,则直接返回实 $abstract 的例化

return new $abstract;

}

// 获取构造函数参数

$parameters = $constructor->getParameters();

$arguments = [];

foreach ($parameters as $parameter) {

// 获得参数的类型提示类

$paramClassName = $parameter->getClass()->getName();

// 参数没有类型提示类,抛出异常

if (is_null($paramClassName)) {

throw new Exception('Fail to get instance by reflection');

}

// 实例化参数 $paramClassName 参数没有绑定的情况下,还是会回掉到 resolve 函数,但是总会有不需要类实例为参数的类,为节点,则一步步走出来。

// 但是这种情况下是使用与实例传递的是类的情况,而不适用于其他参数 如果在 Bread 中添加一个构造函数,你就会发现报错

//不过 $constructor = (new ReflectionClass($abstract))->getConstructor(); 就是限制的查看构造函数的处理。

$arguments[] = $this->make($paramClassName);

}

return new $abstract(...$arguments);

}

使用

$container = new Container();

$hambuger = $container->make(Hamburger::class);

$hambuger->getName();

namespace Core;

/**

* 容器文件

* Class Container

* @package Core

*/

class Container

{

protected $binds = [];

/**

* 获取一个类的实例化

* @param $class

* @param array $params

* @return mixed

* @throws \ReflectionException

*/

public function make($class, array $params = [])

{

if (isset($this->binds[$class])) {

return ($this->binds[$class])->call($this, $this, ...$params);

}

return $this->binds[$class] = $this->resolve($class, $params);

}

/**

* 解析一个类的构造函数,并且实例化

* @param $abstract

* @param $request_params

* @return mixed

* @throws \ReflectionException

*/

protected function resolve($abstract, $request_params)

{

// 获取构造函数

$constructor = (new \ReflectionClass($abstract))->getConstructor();

// 没有构造函数,则直接返回类的实例化

if (is_null($constructor)) {

return new $abstract;

}

// 获取构造函数中的参数

$params = $constructor->getParameters();

$arguments = [];

foreach ($params as $param) {

if (is_null($param->getClass())) {

$arguments[] = $request_params[$param->getPosition()]; // 对应的位置赋值对应的参数

// 参数不是类

} else {

$arguments[] = $this->make($param->getClass()->getName());

}

}

return new $abstract(...$arguments);

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值