conduit/gorilla-claw

WordPress 动作/过滤器钩子 API,具有猴子补丁功能。

0.0.7 2023-05-01 13:33 UTC

README

Build Coverage API Doc Packagist PHP Version Wordpress Compatibility

WordPress 动作/过滤器运行时工具库,具有猴子补丁功能。

  • 定位 - 通过过滤器、函数名、对象名和方法轻松查找钩子处理器
  • 替换 - 在一行内替换钩子回调,甚至可以通过魔法作用域绑定使用原始回调的 $this
  • 重新绑定 - 添加新的处理器,绑定到另一个的作用域(见 'rebind')
  • 注入 - 向现有处理器注入预处理和后处理程序,具有作用域绑定
  • 将钩子处理器作为集合管理
  • add_filters()add_actions() 可用于在单个命令中附加到多个钩子
  • 使用快速的闭包,而不是缓慢的反射。
  • [即将推出] 分析单个处理器,或整个钩子

本库旨在在生产环境中安全使用,具有最小的性能开销。但是,直到 1.0.0 版本发布,这仅应用于测试。但已经有 完整的测试覆盖率

安装

通过 composer

composer require conduit/gorilla-claw

这是什么?

GorillaClaw 是一个用于操作 WordPress 钩子(即动作和过滤器)的运行时工具库;也称为插件。它的目的是帮助最终用户开发者使插件相互协同工作,即使在发生冲突或行为异常的情况下。

它不应作为分布式 WordPress 插件中的组件使用,应使用常规的 WordPress 钩子 API 进行此操作。它也不是作为 '新的闪亮钩子包装器',因为没有人需要或想要。

GorillaClaw 的一个独特功能是,它通过猴子补丁作用域自动将您的钩子处理器绑定到可能隐藏在另一个插件代码中的现有(且以前无法控制的)对象。它甚至可以访问 privateprotected 属性,并且它还允许您调用任何方法,这可能会产生 灾难性的后果

⚠️ 下面开始警告。它们故意散布其中。

⚠️ 忽视它们将带来危险。

除了危险的功能外,该库还包含一些用于定位处理器以及安全地替换或删除它们的有用功能。

GorillaClaw 可以用于善良。它也可以用于邪恶。

定位

危险等级:🕶️ 安全

使用多种不同的查询查找处理器。轻松定位特定的处理器类别。

find_filters() 返回一个 Hook 集合,这是 GorillaClaw 中所有钩子操作的基础。

use function GorillaClaw\find_filters;
use function GorillaClaw\find_actions;

/* Find all handlers for `your_action` */
$hooks = find_filters('your_action');

/* Find all `function_name` handlers for `your_action` */
$hooks = find_filters('your_action', 'function_name');

/* Find all matching static handlers for `your_action` */
$hooks = find_filters('your_action', 'Namespace\ClassName::static_method');

/* Find all matching object / class handlers for `your_action` */
$hooks = find_filters('your_action', ['Namespace\ClassName', 'method_name']);

/* Find all object / class handlers for `your_action` matching any method */
$hooks = find_filters('your_action', ['Namespace\ClassName', false]);

/* You can also find using multiple hook names */
$hooks = find_filters('your_action another_action');
/* or */
$hooks = find_filters(['your_action', 'another_action']);


/* $hooks are a collection of handlers */
foreach($hooks as $hook) {
    
    /* Do something with individual handler */
    $hook->remove();
}

/* ... and array accessible */
$hooks[2]->remove();

删除

危险等级:🕶️ 安全

使用 find_*****s() 找到的处理器取消钩子/删除。安全且简单。

use function GorillaClaw\find_filters;

$hooks = find_filters('your_action');

/* Remove all matching handlers */
$hooks->remove();

/* Remove first handler */
$hooks[0]->remove();

替换

危险等级:🤔 小心

好吧,从这里开始就有点可疑了。我们可以用我们自己的闭包替换处理器,但神奇的是,$this 将被代理到原始对象。我们甚至可以读取 和写入 受保护的或私有的属性,并类似地调用方法。

use function GorillaClaw\find_filters;

/* Our dummy class for the examples below: */

class SomeClass {
    private $private_property;
    public $public_property;

    private function hello($name) {
        return "Hello " . $name;
    }

    public function public_method() {}
}

$hooks = find_filters('your_action', ['SomeClass', 'some_method']);

/* Replace a handler, magically binding to the original object */

$hooks->replace(function($input, $any, $other, $args) {
    /* $this is now the original object, and we've been monkey-patched into scope */

    $var = $this->public_method();
    $var = $this->public_property;

    /* Call a (!) private (!) method */
    return str_replace("Hello", "Goodbye", $this->hello());
});

⚠️ 属性是可写的,即使 protectedprivatefinal class

⚠️ 被替换的处理程序调用的方法可能会更改对象的当前状态,这将对后续调用此操作或其他依赖于原始对象的操作产生影响。 这在大多数情况下可能会导致不可预测的行为。

如果您以某种方式更改类的状态,很可能会遇到很多麻烦。虽然极端情况下这是所需要的。 如果可能的话,避免这样做。

实现这一机制的实际机制 相当不寻常,使用了 Closure、作用域绑定以及按引用传递。没有使用慢速 Reflection

重新绑定

危险等级:⚠️ 可能是一个糟糕的主意

重新绑定类似于替换,但原始的过滤器处理程序仍然处于激活状态。这允许我们“接入”现有过滤器处理程序的 $this 和作用域。

use function GorillaClaw\find_filters;

$hooks = find_filters('your_action');

$hooks[0]->rebind('another_action', function() {
    
    /* 
     * $this now points to the 'your_action' handler's $this,
     * but we are currently running on 'another_action'
     */
    
    return $this->foo;
}, 10);

/* Re-binding a collection of hooks throws an exception */

$hooks->rebind('another_action', function() {});
//||\\ <-- Note: '$hooks', not '$hook[0]' 

⚠️ 重新绑定处理程序调用的方法可能会更改对象的当前状态,这将对后续调用此操作或其他依赖于原始对象的操作产生影响。 这在大多数情况下可能会导致不可预测的行为。

⚠️ 重新绑定不能保证重新绑定函数和原始执行的顺序。 这完全取决于应用程序逻辑。由于对象之间存在链接,两个处理程序都可以相互影响。

🤔 使用 注入 来设置和撤销重新绑定处理程序对对象进行的修改可以减轻上述一些风险。

注入

危险等级:⚠️ 可能是一个糟糕的主意

注入允许您添加在单个过滤器处理程序之前和之后执行的功能。您可以访问 $this,甚至修改私有变量。

大多数时候,如果我们想在一个现有的处理程序之前运行一个函数,我们只需要以“较低”的优先级添加它,以“较高”的优先级运行。 这是正确的做法。 然而,有时我们只想操作单个处理程序,比如...呃...调用改变对象状态的方法,所以让它 修改 -> 运行原始的 -> 取消修改,这样未来的对象交互就不会受到影响。

前后回调都是可选的,并且像通常的链式 WordPress 一样传递它们的参数和返回值。

use function GorillaClaw\find_filters;

$hooks = find_filters('your_action');

$hooks->inject(function($input) {
    return $input . '-run-before';
}, function($input) {
    return $input . '-run-after';
});

⚠️ 注入处理程序调用的方法可能会更改对象的当前状态,这将对后续调用此操作或其他依赖于原始对象的操作产生影响。 记住撤销修改以最大限度地减少这种情况发生的可能性。

⚠️ 重新绑定不能保证重新绑定函数和原始执行的顺序。 这完全取决于应用程序逻辑。由于对象之间存在链接,两个处理程序都可以相互影响。

添加

还有围绕 add_filter()add_action() 的简单包装器 - 多数版本允许在单行中将处理程序添加到多个操作/过滤器。这只是语法糖。

use function GorillaClaw\add_filters;
use function GorillaClaw\add_actions;

add_filters('filter_1 filter_2', 'some_function', 10, 2);
add_filters(['filter_1', 'filter_2'], 'some_function', 10, 2);

add_actions('action_1 action_2', 'some_function', 10, 2);
add_actions(['action_1', 'action_2'], 'some_function', 10, 2);

您不需要这个

理想情况下,您永远不应该“需要”这个库。

然而,随着插件开发者越来越多地使用对象(好事!),并且并不总是意识到作用域限制(坏事),有时您可能需要修补到插件对象的范围内,并处理私有变量/方法。这包括设置 Gutenberg 块的插件和使用依赖注入的插件等。

如果您可以通过这个库以外的其他方式实现目标,那么请那样做。

如果操作不当,运行时猴子补丁会导致大量调试麻烦,所以如果您使用 replacerebindinject 修改对象属性,请务必非常小心。

贡献

欢迎提交拉取请求。对于重大更改,请先提交一个问题来讨论您想更改的内容。

请确保适当更新测试。

许可证

MIT

关闭你的代码检查器,丢弃你的测试套件,加密你的代码库!是时候打破一些东西了...