Laravel helper tap 助手函数完全解析

tap 函数的起源
tap 函数是一个高阶函数 higher-order function (HOF),在 Larvel 5.3 当中被引入

// 接收两个参数,`$value` 为需要处理的数据,`$callback` 为闭包 Closure
function tap($value, $callback)
{
   $callback($value); // $callback 对 $value 进行处理
   return $value; // 返回处理完毕的 $value
}

这里解释了一种用法

public function findArticleAndIncreaseView($id)
{
    $article = Article::findOrFail($id);
    $article->incrementViews();
    
    return $article;
}
public function findArticleAndIncreaseView($id)
{
    return tap(Article::findOrFail($id), function (Article $article) {
        $article->incrementViews();
    });
}

仔细分析一下原始代码步骤:

  1. 原始代码中表明这是一个会产生副作用的函数,直接对 $article 中的状态进行了修改,用例子中的话来说就是执行了一个 增加文章的阅读量的操作
  2. 声明了一个函数作用域内的变量 $article

使用 tap 之后的改进:

  1. 没有声明额外变量
  2. 引入 tap 函数,将三条语句包裹为一个代码体
  3. fn(v);return v; 的过程进行了抽象

注意⚠️ tap 中的 $value 必须是一个引用类型,因为副作用只能作用于引用类型,也就是 PHP 中的 \stdClass

咋一看没什么鸟用,好像还搞复杂了?
如果看到这里你以为就这么简单那就错了,让我们重新回到源代码 tap v10.x

if (! function_exists('tap')) {
    /**
     * Call the given Closure with the given value then return the value.
     *
     * @param  mixed  $value
     * @param  callable|null  $callback
     * @return mixed
     */
    function tap($value, $callback = null)
    {
        if (is_null($callback)) {
            return new HigherOrderTapProxy($value);
        }

        $callback($value);

        return $value;
    }
}

可以看到,代码中对 $callback 进行了 is_null 检查,意思为:如果闭包函数为 null,也就是没有传递 $callback,那么返回一个 new HigherOrderTapProxy($value); 这里叫它 tap 高阶函数代理
那么什么是 HigherOrderTapProxyHigherOrderTapProxy v10.x

namespace Illuminate\Support;

class HigherOrderTapProxy
{
    /**
     * The target being tapped.
     *
     * @var mixed
     */
    public $target;

    /**
     * Create a new tap proxy instance.
     *
     * @param  mixed  $target
     * @return void
     */
    public function __construct($target)
    {
        $this->target = $target;
    }

    /**
     * Dynamically pass method calls to the target.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        $this->target->{$method}(...$parameters);

        return $this->target;
    }
}

参考代码

// tap 拥有两种作用,一个是返回 $model
tap($model, $callback)
// 另一个是在忽略 callback 的时候,返回一个 `HigherOrderTapProxy` 实例
tap($model)
  ->foobar();

在我们不传递 $callbacktap 中的时候,$model 传递到了 HigherOrderTapProxy 这个类实例中的 $target,也就是“被代理了”
HigherOrderTapProxy 中的 public __call(string $name, array $arguments): mixed 是 PHP 的类魔术方法,意思是当类实例调用一个不存在的实例方法的时候,会自动触发此方法,方法第一个参数接收调用的函数名,第二个参数接收调用的函数参数,如上函数名为 foobar,参数为 空数组
这行代码 $this->target->{$method}(...$parameters); 对函数执行原有预期操作,没什么特别的
特别的地方在于 return $this->target;我们返回的是被代理的 $model 而不是 $this->target->{$method}(...$parameters);
因为我们不需要 $model->foobar() 的返回值(这个返回值可能会返回布尔,或者返回数组,返回 Collection 等等非 $model 的值),我们要的只是 $model

比如

<?php
return $post->update([
	'title' => $title,
    'description' => $description,
]); // boolean

// 我们不要返回的 boolean,要 `$post`
return tap($post) // 先返回一个 HigherOrderTapProxy 实例
	->update([
	    'title' => $title,
	    'description' => $description,
	]); // 调用 HigherOrderTapProxy 实例不存在的方法 update,触发 __call 函数调用,最后返回委托对象 $post

PHP Magic Methods
https://freek.dev/694-laravels-tap-helper-function-explained
https://medium.com/@tanmaymishu/the-anatomy-of-laravels-tap-function-ea239c9846ab

posted @ 2024-01-26 00:30  我听不见  阅读(25)  评论(0编辑  收藏  举报