tap()
之前有提過,是 helpers.php 的方法之一:
function tap($value, $callback = null)
{
if (is_null($callback)) {
return new HigherOrderTapProxy($value);
}
$callback($value);
return $value;
}
先不管 HigherOrderTapProxy,來看剩下的原始碼:
function tap($value, $callback)
{
$callback($value);
return $value;
}
它需要傳入一個 $value,然後它會再回傳出來,因此可以知道下面這個寫法是可行的:
tap(new Collection(), $callback)->each->pay();
再來因為它中間有做 $callback($value)
,因此上面這個方法的全貌可能會是長這樣的:
$callback = function($collection) {
$collection->set(new Invoice);
};
tap(new Collection(), $callback)->each->pay();
反過來看,如果沒有 tap() 函式的話,我們可能需要這樣寫:
$collection = new Collection();
$collection->set(new Invoice);
$collection->each->pay();
咦,看起來似乎不用 tap() 寫起來比較乾淨。不是這樣的,是 Collection 並不適合用在這個地方。
最常看到的就是在物件初始化的時候,比方說分析 Session 提到了下面這段程式碼:
protected function startSession(Request $request)
{
return tap($this->getSession($request), function ($session) use ($request) {
$session->setRequestOnHandler($request);
$session->start();
});
}
使用 tap()
的好處之一是剛剛有提到的,這對要使用串聯方法是有利的;另一個好處則是:有時候我們會希望對某個實例做某些事,而做這些事會需要產生一些專用的暫時變數,這時 tap() 因為可以使用 Closure,所以可以把這些暫存變數「關」在裡面,外面就不會被這些暫時變數干擾到。
雖然是這樣,但每次要寫一堆 callable 就很煩,因此出現了另一個選擇:HigherOrderTapProxy,來看看它的原始碼:
// 前面只是建構的時候把 value 存到 target 而已,所以省略
public function __call($method, $parameters)
{
$this->target->{$method}(...$parameters);
return $this->target;
}
這很像 proxy pattern,唯一不同的地方在於,它固定會回傳 self。以 分析 Log 的例子來說,原本程式碼與改寫後的程式碼如下:
tap($this->createEmergencyLogger(), function ($logger) use ($e) {
$logger->emergency('Unable to create configured logger. Using emergency logger.', [
'exception' => $e,
]);
});
tap($this->createEmergencyLogger())
->emergency('Unable to create configured logger. Using emergency logger.', [
'exception' => $e,
]);
跟 Higher Order Messages 很像,可以省略掉一層 callback,但同時也有一樣的使用條件:以 callback 的寫法,只允許一行程式碼。