PHP Hooks 库中实现多过滤器链式调用并收集全部返回值的正确方法

php-hooks 库的 `apply_filters()` 默认仅返回最后一个回调的执行结果;若需收集所有已注册同名钩子的返回值,必须手动遍历调用或改用支持“过滤器链聚合”的设计模式。

在 PHP-Hooks(bainternet/PHP-Hooks)中,add_filter() 允许为同一钩子名(如 'my_filter')注册多个回调函数,但其核心机制是链式传递:每个回调接收前一个回调的返回值作为输入,并最终只返回最后一个回调的结果。这与 WordPress 的 apply_filters() 行为一致——它不是“广播式”调用,而是值流式处理

因此,你原始代码中:

app_hooks()->add_filter('my_filter', fn($v) => { echo $v; return "x"; });
app_hooks()->add_filter('my_filter', fn($v) => { echo $v; return "y"; });
$my_tabs[] = app_hooks()->apply_filters('my_filter', 'some value to be passed');

实际执行流程为:

  1. 第一个回调接收 'some value to be passed',输出该字符串,返回 "x";
  2. 第二个回调接收 "x"(而非原始字符串),输出 "x",返回 "y";
  3. apply_filters() 最终只返回 "y" → $my_tabs = ['y']。

正确解法:避免复用同一钩子名进行“并行收集”,而应显式分发调用

推荐采用以下结构,清晰、可控且符合库设计意图:

// 为每个逻辑独立的处理器分配唯一钩子名
app_hooks()->add_filter('my_filter_x', function ($value) {
    echo $value . "\n"; // 输出: some value to be passed
    return "x";
});

app_hooks()->add_filter('my_filter_y', function ($value) {
    echo $value . "\n"; // 输出: some value to be passed
    return "y";
});

// 手动批量触发不同钩子,收集全部返回值
$hook_names = ['my_filter_x', 'my_filter_y'];
$my_tabs = [];

foreach ($hook_names as $hook) {
    $result = app_hooks()->apply_filters($hook, 'some value to be passed');
    $my_tabs[] = $result;
}

print_r($my_tabs);
// 输出:
// Array
// (
//     [0] => x
//     [1] => y
// )

⚠️ 注意事项:

  • 不要依赖 apply_filters() 自动聚合多个同名回调的返回值——该库不提供类似 apply_filters_array() 的原生方法;
  • 若业务逻辑强依赖“同一语义钩子下多处理器并行执行+结果合并”,建议自行封装工具函数,例如:
    function apply_filters_all($hook, $value) {
        $hooks = app_hooks()->get_filters($hook); // 注意:需确认库是否暴露 get_filters()
        $results = [];
        foreach ($hooks as $callback) {
            $results[] = call_user_func($callback, $value);
        }
        return $results;
    }

    (注:PHP-Hooks 默认未公开 get_filters() 方法,如需此能力,需扩展类或修改源码)

? 总结:PHP-Hooks 是轻量级钩子实现,强调链式数据流而非事件广播。追求“多回调并行返回”时,请主动管理钩子命名与调用逻辑——这是设计使然,而非使用错误。