上下文繫結在分析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));
}
2.然後進入這個ContextualBindingBuilder
類看下。
這個類不大,提供了兩個方法,needs和give
先看下needs方法,很簡單就是把$abstruct
儲存起來。
這個例子中的$abstruct
就是Filesystem::class類
然後返回當前物件,以致可以繼續鏈式操作。
public function needs($abstract)
{
$this->needs = $abstract;
return $this;
}
3.再看下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
);
}
4.然後我們回到Container看看方法addContextualBinding().
也很簡單,就是把這些引數存入contextual陣列。
(這個特別重要)陣列結構形式為:contextual[PhotoController::class][Filesystem::class] = 閉包函式(也可以是一個類路徑)
public function addContextualBinding($concrete, $abstract, $implementation)
{
$this->contextual[$concrete][$this->getAlias($abstract)] = $implementation;
}
這就完成了一個上下文繫結。說到底和普通繫結雷同只不過是處理有依賴的物件。
總結
當一個類需要一些外部依賴的時候,要用到上下文繫結。把外部依賴傳遞給他。
實現方式就是存入到容器中那個負責上下文的那個陣列中,這個陣列將會在解析的時候(就是取出某個物件的時候,他對應繫結的依賴也會被取出)做判斷。
實測用例
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);
}
//interface
interface Money
{
public function getAmount();
}
class Dollar implements Money
{
public function getAmount()
{
return 1;
}
}
class Cheque implements Money
{
public function getAmount()
{
return 100000;
}
}
class Worker
{
private $money;
public function __construct(Money $money)
{
$this->money = $money; // prints '1'
}
public function getA(){
return $this->money->getAmount();
}
}
class Boss
{
private $money;
public function __construct(Money $money)
{
$this->money = $money; // prints '100000'
}
public function getA(){
return $this->money->getAmount();
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結