piggly/php-hooks

控制当特定TAG被调用时将执行的钩子。

1.0.0 2021-02-22 18:19 UTC

This package is auto-updated.

Last update: 2024-08-29 05:37:24 UTC


README

Latest Version on Packagist Software License

要阅读此文件的葡萄牙语版本,请点击此处

此库受到Wordpress核心中可用的函数do_actionapply_filter的启发。

钩子是在特定、预定义的位置运行某些代码的方式。此功能的主要优点是钩子可以累积在请求时才执行的一组函数。

与Wordpress建议相反,此库中有三种类型的钩子:过滤器、动作和调度器。所有这些都在标签上注册了回调,包括或不包括参数。然后,该标签可以执行。

如果您喜欢这个库并想支持这项工作,请随意向BTC钱包3DNssbspq7dURaVQH6yBoYwW3PhsNs8dnK捐赠任何金额 ❤。

安装

可以使用Composer通过以下命令安装此库:composer require piggly/php-hooks

注册回调

只要钩子标签没有被触发,就可以添加和/或删除注册到该标签的回调。对于每个注册的回调,都可以创建一个由以下内容组成的标签:标签名称、函数名称、必需参数和执行优先级

标签可以通过两种方式形成:使用Syntax::create()函数或使用以下标签语法编写字符串。

标签的语法由以下组成

  • {tagname}(必需)标签名称;
  • .{function_name}:函数名称;
  • ?{args}:要接收的参数数量,默认总是为1
  • ::{priority}:执行优先级,默认总是为10

给回调起的名字总是需要唯一的。如果函数名称已存在于标签中,它将引发NameAlreadyExistsException

标签语法正则表达式为/^(?:(?P[^\.\:\?]+))(?:\.(?P[^\:\?]+))?(?:\?(?P\d+))?(?:\:\:(?P\d+))?$。如果标签语法错误,将引发InvalidSyntaxException

例如

  • 要注册一个具有标签calculate和优先级1的回调,标签语法将是:calculate::1
  • 要注册一个具有标签calculate、名称pow和接收两个参数的回调,标签语法将是:calculate.pow?2

下面是另一个示例

// Register in tag calculate
$tag = 'calculate';

// Register in tag calculate ready to recieve two args
$tag = 'calculate?2';

// Register in tag calculate with priority 1
$tag = 'calculate::1';

// Register in tag calculate ready to recieve two args and priority 1
$tag = 'calculate?2::1';

// Register in tag calculate with name sum
$tag = 'calculate.sum';

// Register in tag calculate with name sum ready to recieve two args
$tag = 'calculate.pow?2';

// Register in tag calculate with name sum and priority 1
$tag = 'calculate.sum::1';

// Register in tag calculate with name sum, ready to recieve two args and priority 1
$tag = 'calculate.pow?2::1';

回调

除了需要标签外,任何注册的函数还需要一个回调。可以通过Hook::filter()Hook::action()Hook::dispatch()方法接收的回调有

  • Closure:闭包对象类型 Hook::filter( $tagSyntax, function ( $number ) { return $number + 15; } );
  • Function:一个指向函数名称的string Hook::filter( $tagSyntax, 'sum' );
  • Static Object:具有静态函数的class,指向类名和静态方法 Hook::filter( $tagSyntax, StaticClass::class, 'methodToCall' );
  • Object:一个对象的instance,指向要调用的对象方法 Hook::filter( $tagSyntax, $instance, 'methodToCall' );

附加参数

在引用了 callback 之后,最后几个参数将始终是该函数附加的附加参数。例如,Hook::dispatch( $tagSyntax, 'sum', 1 ); 将有一个附加参数为 1。附加参数仅在 Hook::dispatch() 方法中使用。

以下详细了解 Hook::filter()Hook::action()Hook::dispatch()

过滤器 x 动作 x 分发器

这三种类型的钩子的主要区别是

  • 过滤器在执行点接收信息,以某种方式修改它并返回它。换句话说:修改某些内容并返回以供下一个过滤器使用,依此类推;

一个过滤器的例子是过滤器 Hook::apply('comment', $comment),它通过删除非法单词、删除链接等来过滤评论。

  • 动作在执行点接收信息,对其进行操作并返回空值。换句话说:执行某些操作,然后以开始、中间和结束的方式结束你的代码。

一个动作的例子是创建新票据时的通知 Hook::run('new_ticket', $ticket)。该动作将接收 $ticket 并相应地发送通知。

  • 分发器类似于动作,但它不是在执行点接收信息,而是在注册点接收信息。换句话说:它执行与执行点无关的操作,但会干扰它。

一个分发器的例子是在 HTML 标签中包含 CSS 文件 Hook::run('head')。在这里,分发器将在注册时接收 CSS $name 文件,即 Hook::dispatch('head', 'importCss', 'home.css') 时。

Hook::filter() 过滤器

过滤器 使钩子能够在执行过程中操纵数据。过滤器函数将接收一个变量,修改它并返回它。注册在过滤器中的函数必须独立工作,并且永远不应该有副作用。过滤器始终期望从它们那里返回某些内容。

要将新回调注册到过滤器,应使用以下参数调用 Hook::filter() 方法

  • $tagSyntax:函数将注册的标签语法;
  • $callback, ?$method:要执行的回调。

要应用过滤器,应使用以下参数调用 Hook::apply() 方法

  • $tag:将要执行的标签名称;
  • $value:要过滤的初始值;
  • ...$params:要过滤的附加参数(Hook::filter() 需要能够在标签语法中接收这些参数,以 ?{args} 的形式,默认情况下始终为 1);

此外,还有两种 Hook::apply() 方法的变体

  • Hook::applyByName() 仅执行特定标签的一个函数;
  • Hook::applyOnce() 仅执行标签一次,之后,它将无法再使用;

还可以使用 Hook::removeFilter() 方法在执行之前删除过滤器。

提示:当过滤器标签在应用程序生命周期中仅使用一次时,请使用 Hook::applyOnce() 方法,它将在运行时释放内存空间。

以下是一个使用过滤器的实际示例

function fsum ( $number ) { return $number+15; }
function fsub ( $number ) { return $number - 10; }
function fmul ( $number ) { return $number * 3; }
function fdiv ( $number ) { return $number / 2; }
function fpow ( $number, $exp ) { return is_numeric($exp) ? pow($number, $exp) : $number; }

Hook::filter('calculate.sum', 'fsum');
Hook::filter('calculate.sub', 'fsub');
Hook::filter('calculate.mul', 'fmul');
Hook::filter('calculate.div', 'fdiv');

// -> Apply => Expects (((10+15)-10)*3)/2 = 22.5
$number = Hook::apply('calculate', 10);
echo sprintf("Number: %s\n", $number);

完整示例可在 此处 查看。

Hook::action() 动作

动作 允许您修改应用程序的行为。动作的函数可以写入一些输出,将数据插入数据库,发送通知等。注册在动作中的函数必须始终执行某种类型的任务,因此不会发生任何类型的返回。

要注册一个新回调到动作,应使用以下参数调用方法 Hook::action()

  • $tagSyntax:函数将注册的标签语法;
  • $callback, ?$method:要执行的回调。

要运行一个动作,应使用以下参数调用方法 Hook::run()

  • $tag:将要执行的标签名称;
  • ...$params:传递给动作的附加参数(Hook::action() 需要能够以 ?{args} 语法接收这些参数,默认情况下总是 1);

此外,还有两种 Hook::run() 方法变体,它们是:

  • Hook::runByName() 只执行标签的特定功能;
  • Hook::runOnce() 只执行一次标签,之后将无法再使用;

还可以使用方法 Hook::removeAction() 在执行前删除动作。

提示:当动作标签在应用程序的生命周期中只使用一次时,请使用 Hook::runOnce() 方法,它将在运行时释放内存空间。

以下是一个使用动作的实际示例

function line () { echo "I am line 02\n"; }
function message ( $message ) { echo sprintf("Message: %s\n", $message); }

Hook::action('sentences.line', 'line');
Hook::action('sentences.message', 'message');

Hook::run('sentences', 'Peace and Love');

完整示例请访问 这里

分发器 Hook::dispatch()

分发器 具有与动作相同的行为。它们执行没有返回值的代码片段,并通过 Hook::dispatch() 方法注册。然而,它们接收附加参数的方式与动作不同。

假设您的应用程序中有一个 head 钩子,该钩子会在应用程序的 <head> 标签中添加 HTML 标签。

<html>
    <head>
        <?php Hook::run('head'); ?>
    </head>
    <body>
    </body>
</html>

假设您有一个 Controller,它包含 home()services()about() 方法(每个页面一个),以及 home.cssservices.cssabout.css 文件。要将这些文件包含在 head 中,需要为每个动作创建一个回调。请参阅

class Controller
{
    // ...

    public function home()
    {
        Hook::action('head', $this, 'homeAction');
        // ...
    }

    public function services()
    {
        Hook::action('head', $this, 'servicesAction');
        // ...
    }

    public function about()
    {
        Hook::action('head', $this, 'aboutAction');
        // ...
    }

    public function homeAction ()
    { /** Add css file... **/ }

    public function servicesAction ()
    { /** Add css file... **/ }

    public function aboutAction ()
    { /** Add css file... **/ }

    // ...
}

这不是很无聊吗?这是因为方法 Hook::run('head') 不会为动作传递参数,并且动作不会接收参数。这是因为我们需要一个回调来处理每个动作。此外,无法在执行点 Hook::run('head')home()services()about() 所需的参数传递给它们。

分发器就是为了解决这个问题而创建的。在这种情况下,假设我们有一个 importCss($name) 函数。该函数使用 $name 作为 CSS 文件名,在页面中生成 <link> 标签。现在,我们只需要将此函数的分发器与参数 $name 注册。请参阅

class Controller
{
    // ...

    public function home()
    {
        Hook::dispatch('head', 'importCss', 'home.css');
        // ...
    }

    public function services()
    {
        Hook::dispatch('head', 'importCss', 'services.css');
        // ...
    }

    public function about()
    {
        Hook::dispatch('head', 'importCss', 'about.css');
        // ...
    }

    // ...
}

如您所见,我们简化了代码,并且使用了更高效的钩子。使用触发器,我们可以更智能地将函数包含在代码中,而无需依赖与创建的动作等效的函数(因为动作不允许在 Hook::run() 执行点之外传递附加参数)。

非常重要的一点是要理解,钩子 Hook::run('new_ticket', $ticket) 会发送 $ticket 参数,并且该参数将以 Hook::action('new_ticket.notification', 'notification'); 的形式传递给任何动作,但它不会通过 Hook::dispatch('new_ticket.notification', 'notification', $ticket); 传递,后者需要在注册时包含 $ticket

当需要继承 Hook::run() 执行参数时,必须使用动作;而当我们想在钩子内包含具有自己参数的函数时,必须使用分发器。

还可以使用方法 Hook::removeDispatcher() 在执行前删除分发器。

以下是一个使用分发器的实际示例

function name ( $message ) { echo sprintf("Your name: %s\n", $message); }
function prog ( $message ) { echo sprintf("Progamming Language: %s\n", $message); }

Hook::dispatch('sentences.name::1', 'name', 'Alpha');
Hook::dispatch('sentences.prog::1', 'prog', 'JS');

Hook::run('sentences', 'Peace and Love');

完整示例请访问 这里

变更日志

请查看CHANGELOG文件,了解所有代码更改的信息。

代码测试

此库使用PHPUnit。我们对本应用程序的所有主要类进行了测试。

vendor/bin/phpunit

贡献

在提交贡献前,请参阅CONTRIBUTING文件获取信息。

安全

如果您发现与安全相关的问题,请发送电子邮件至dev@piggly.com.br,而不是使用GitHub的问题跟踪器。

致谢

支持项目

Piggly Studio是一家位于巴西里约热内卢的代理机构。如果您喜欢这个库并希望支持这项工作,请自由地捐赠任意金额至BTC钱包3DNssbspq7dURaVQH6yBoYwW3PhsNs8dnK ❤。

许可证

MIT许可证(MIT)。请参阅LICENSE