擴充套件機制也挺重要。先看下文件的解釋
extend 方法可以修改解析的服務。例如,當一個服務被解析後,你可以新增額外的程式碼去修飾或配置這個服務。 extend 方法接受一個閉包,閉包的唯一引數和返回值都是一個服務:
$this->app->extend(Service::class, function($service) {
return new DecoratedService($service);
});
不明白?我也不明白。我用我的話講一下。
extend主要的作用是在解析後,使用一個閉包函式產生的值(通常為當前實體類的子類)替換對應父實體類,從而對其產生擴充套件影響。這個閉包的引數和返回值都必須是物件。比如:
app()->extend('service', function ($service, $app) {
return new DecoratedService($service);
});
這裡DecoratedService 是 service 的子類。
更簡單來說,當我們從Container容器中取出一個例項後,用這個類的子類例項替換掉當前這個父類例項,達到擴充套件的作用。
當然這是一種用法。還有一些細節,我們去看下原始碼。
我們看下原始碼:
1.先獲取Container中這個id($abstract)的別名
2.檢視容器已有的例項陣列instance裡面有沒有對應的例項,如果有直接執行我們extend的這個閉包方法的返回值,存入這個陣列中。就是替換了原來的例項。
2.2.然後使用了rebound(),目的是看看有沒有附帶的回撥函式,觸發它,這個我們在回撥函式中會提。
這說明extend的時候會觸發回撥函式。
3.如果instance中沒有找到對應的例項,就把這個閉包函式存入extenders陣列,做個記錄,以後用。
並且如果這個id已經被resolved過了,還要觸發rebound函式,觸發一些對應的回撥函式。
public function extend($abstract, Closure $closure)
{
$abstract = $this->getAlias($abstract);
if (isset($this->instances[$abstract])) {
$this->instances[$abstract] = $closure($this->instances[$abstract], $this);
$this->rebound($abstract);
} else {
$this->extenders[$abstract][] = $closure;
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
}
現在就很清楚了。
總結
extend的機制就是如果instance中存在例項,就用extend中的閉包執行結果替換掉。如果instances例項列表中不存在就存起來以後備用。
某個適用場景可以是:用子類例項替換父類例項達到擴充套件的作用。
但不管怎麼樣,都會觸發當前存在的回撥函式一次。下一章講一下回撥函式。
實測例項
例項1:替換依賴的例項物件
本來的Boss例項的依賴Money是一個Cheque物件,最後解析的時候,被替換成了Dollar物件。
public function testClosure(){
app()->bind('boss', Boss::class);
$this->app->when(Boss::class)
->needs(Money::class)
->give(Cheque::class);
app()->extend(Money::class, function() {
return new Dollar();
});
$boss= app()->make('boss');
$output = $boss->getA();
$this->assertEquals($output, 1);
}
//
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();
}
}
例項2:子類替換父類,向下擴充
public function testClosure()
{
app()->extend(Money::class, function() {
return new Dollar();
});
$money= app()->make(Money::class);
$output = $money->getAmount();
$this->assertEquals($output, 1);
}
class Money
{
public function getAmount(){
return 100;
}
}
class Dollar extends Money
{
public function getAmount()
{
return 1;
}
}
class Cheque extends Money
{
public function getAmount()
{
return 100000;
}
}
例項3:向其他地方擴充, extend第二個引數閉包的第一個引數就是 extend第一個引數的例項,在這裡就是我們事先繫結的Money::class例項。
public function testClosure()
{
app()->bind('money', Money::class);
app()->extend('money', function($money) {
return new Currenty($money);
});
$boss= app()->make('money');
$output = $boss->getAmount();
$this->assertEquals($output, "harveynorman");
}
class Currenty{
protected $money;
public function __construct(Money $money)
{
$this->money = $money;
}
public function getAmount(){
return "harveynorman";
}
}
class Money
{
public function getAmount(){
return 100;
}
}
class Dollar extends Money
{
public function getAmount()
{
return 1;
}
}
class Cheque extends Money
{
public function getAmount()
{
return 100000;
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結