Contextual 上下文绑定机制

上下文绑定在分析 Container 源码的时候是一个比较重要的部分,在了解上下文绑定之前,先解释下什么是上下文:

每一段程序都有很多外部变量。只有像 Add
这种简单的函数才是没有外部变量的。一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行。你为了使他们运行,就要给所有的外部变量一个一个写一些值进去。这些值的集合就叫上下文。
「编程中什么是「Context (上下文)」?」 - vczh 的回答。

简单说就是解析一个对象的时候,有些对象是需要外部的一些依赖的。那他在创建的时候就要用到上下文把依赖引入。

而上下文绑定的意思就是专门处理・实例化时候有依赖关系・情况的一种绑定

上下文绑定在 Laravel 5.6 文档中给出了相关示例:

use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('local');
          });

$this->app->when(VideoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('s3');
          });

这是项目中常会用到存储功能,得益于 Laravel 内置集成了 FlySystem 的 Filesystem 接口,我们很容易实现多种存储服务的项目。

示例中将用户头像存储到本地,将用户上传的小视频存储到云服务。那么这个时就需要区分这样不同的使用场景(即上下文或者说环境)。

当用户存储头像(PhotoController::class)需要使用存储服务(Filesystem::class)时,我们将本地存储驱动,作为实现给到 PhotoController::class:


function () {
    return Storage::disk('local');
}

而当用户上传视频 VideoController::class,需要使用存储服务(Filesystem::class)时,我们则将云服务驱动,作为实现给到 VideoController::class:

function () {
    return Storage::disk('s3');
}
源码:

我们来看下源码的实现。
illuminate\Container\Container.php

  1. 看下 when 方法,这个方法直接生成一个 ContextualBindingBuilder 对象,传入 container 对象和 $concrete。

$concrete 在这个例子中就是 PhotoController::class 和 VideoController::class
我们暂且用 PhotoController::class 为例

public function when($concrete)
{
    return new ContextualBindingBuilder($this, $this->getAlias($concrete));
}
  1. 然后进入这个 ContextualBindingBuilder 类看下。 这个类不大,提供了两个方法,needs 和 give

先看下 needs 方法,很简单就是把 $abstruct 存储起来。
这个例子中的 $abstruct 就是 Filesystem::class 类
然后返回当前对象,以致可以继续链式操作。

public function needs($abstract)
    {
        $this->needs = $abstract;

        return $this;
    }
  1. 再看下 give 方法,也很简单又重新调用了 $container 中的 addContextualBinding(), 这个就是添加上下文绑定的方法,分别传入的是:
    concrete:PhotoController::class 的别名(如果有的话)
    abstruct:Filesystem::class
    implemention:闭包 Storage::disk(‘local’);的返回值。
public function give($implementation)
{
    $this->container->addContextualBinding(
        $this->concrete, $this->needs, $implementation
    );
}
  1. 然后我们回到 Container 看看方法 addContextualBinding (). 也很简单,就是把这些参数存入 contextual 数组。

(这个特别重要)数组结构形式为:
contextual[PhotoController::class][Filesystem::class] = 闭包函数(也可以是一个类路径)

public function addContextualBinding($concrete, $abstract, $implementation)
    {
        $this->contextual[$concrete][$this->getAlias($abstract)] = $implementation;
    }

这就完成了一个上下文绑定。说到底和普通绑定雷同只不过是处理有依赖的对象。

总结

当一个类实例化需要一些外部依赖的时候,就要用到上下文绑定。把外部依赖通过 need 传递给他。还可以通过 give 存储 when 对应的实现(针对抽闲类或者接口甚至是子类)。存入到容器中那个负责上下文的那个数组中,这个数组将会在解析的时候 (就是取出某个对象的时候,他对应绑定的依赖也会被取出) 做判断。

必须指出,我们通过源码观察,(存储的结构:contextual[when][need] = implement)我们发现这个
implement 可以传入任何类型的值,理论上来说绑定的时候没有任何问题,但是当解析的时候,他必须是一个闭包或者是 need
的子类或实现类,不然它是无法解析的。在 build 章节中有分析。

实测用例

提供的类关系

//interface 接口money
interface Money
{
    public function getAmount();
}

//实现类 Cheque
class Cheque implements Money
{
    public function getAmount()
    {
        return 100000;
    }
}

//Boss类
class Boss
{
    private $money;
    public function __construct(Money $money)
    {
        $this->money = $money; // prints '100000'
    }

    public function getA(){
        return $this->money->getAmount();
    }
}

测试 1: 绑定 Boss 类,Boss 类依赖 Money 类的一个对象。最后绑定的实现是 Cheque 类。

public function testClosure(){
    $this->app->when(Boss::class)
        ->needs(Money::class)
        ->give(Cheque::class);

    $boss= app()->make(Boss::class);
    $output = $boss->getA();
    $this->assertEquals($output, 100000);
}

————————————————
原文作者:HarveyNorman
转自链接:https://learnku.com/articles/41092
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值