icanboogie/event

v4.1.0 2022-04-10 23:05 UTC

This package is auto-updated.

Last update: 2024-09-03 23:08:20 UTC


README

Release Code Quality Code Coverage Downloads

icanboogie/event 允许您提供钩子,其他开发者可以将其附加到应用程序中,当发生特定事件时,会收到通知并采取行动。

ICanBoogie 中,事件通常用于修改初始参数,在操作处理之前/之后或失败时采取行动,在请求分发之前/之后采取行动或捕获异常。

安装

composer require icanboogie/event

功能亮点

  • 易于实现。
  • 事件具有类型。
  • 事件通常具有目标对象,但也可以发出更简单的事件类型。
  • 事件钩子附加到类而不是对象上,并且它们可以继承。
  • 可以将事件钩子附加到在事件钩子链之后执行的 完成链
  • 可以停止事件链的执行。

观察者模式的一种变体

API使用的模式类似于 观察者模式,尽管事件钩子不是附加到对象上,而是附加到它们的类。当一个事件在一个目标对象上触发时,会使用其类的层次结构来过滤事件钩子。

考虑以下类层次结构

ICanBoogie\Operation
└─ ICanBoogie\Module\Operation\SaveOperation
    └─ Icybee\Modules\Node\Operation\SaveOperation
        └─ Icybee\Modules\Content\Operation\SaveOperation
            └─ Icybee\Modules\News\Operation\SaveOperation

当发出一个具有 …\News\Operation\SaveOperation 实例的 ProcessEvent 时,会调用所有附加到此事件类的类的钩子,从附加到实例类(…\News\Operation\SaveOperation)的事件钩子开始,一直到最后一个附加到其根类的钩子。

因此,当用 …\Node\Operation\SaveOperation 类实例触发 ProcessEvent 事件时,会调用附加到该类的钩子。可以考虑钩子是 继承 的。

入门指南

要发出事件,需要一个事件集合,其中包含事件钩子。因为当需要时为您创建了一个新的事件集合,所以您不需要自己设置它。仍然,如果您需要在创建事件集合时附加大量事件钩子,您可能想这样做。为此,您需要定义一个 提供者,该提供者将在需要时返回您的事件集合。

以下示例演示了如何设置一个提供者,该提供者使用由应用程序配置提供的事件钩子实例化事件集合

<?php

namespace ICanBoogie;

/* @var Application $app */

EventCollectionProvider::define(function() use ($app) {

	static $collection;

	return $collection ??= new EventCollection($app->configs['event']);

});

# Getting the event collection

$events = EventCollectionProvider::provide();
# or
$events = get_events();

类型化事件

所有事件都是 Event 类的实例,因为它是一个抽象类,所以需要扩展。

以下代码演示了如何定义一个 ProcessEvent

<?php

namespace ICanBoogie\Operation;

use ICanBoogie\Event;
use ICanBoogie\HTTP\Request;
use ICanBoogie\HTTP\Response;
use ICanBoogie\Operation;

class ProcessEvent extends Event
{
	/**
	 * Reference to the response result property.
	 */
	public mixed $result;

	public function __construct(
	    Operation $target,
	    public readonly Request $request,
	    public readonly Response $response,
	    mixed &$result
    ) {
		$this->result = &$result;

		parent::__construct($target);
	}
}

事件类型

如果事件有一个目标,则使用 for() 方法以及目标类或对象来获取事件。如果事件没有目标,则事件类型是事件类。

命名空间和命名

事件类应在仅针对其目标对象的唯一命名空间中定义。针对 ICanBoogie\Operation 实例的事件应在 ICanBoogie\Operation 命名空间中定义。

触发事件

使用 emit() 函数触发事件。

<?php

namespace ICanBoogie;

/* @var Event $event */

emit($event);

附加事件钩子

使用事件集合的 attach() 方法附加事件钩子。该 attach() 方法足够智能,可以从参数类型创建事件类型。这对于任何可调用对象都有效:闭包、可调用对象、静态类方法、函数。

以下示例演示了如何将闭包附加到 BeforeProcessEvent 事件。

<?php

namespace ICanBoogie

$detach = $events->attach(function(Operation\BeforeProcessEvent $event, Operation $target) {

	// …

});

# or, if the event doesn't have a target

$detach = $events->attach(function(Operation\BeforeProcessEvent $event) {

	// …

});

$detach(); // You can detach if you no longer want to listen.

以下示例演示了如何将可调用对象附加到相同的事件类型。

<?php

namespace ICanBoogie

class ValidateOperation
{
	private $rules;

	public function __construct(array $rules)
	{
		$this->rules = $rules;
	}

	public function __invoke(Operation\BeforeProcessEvent $event, Operation $target)
	{
		// …
	}
}

// …

/* @var $events EventCollection */
/* @var $rules array<string, mixed> */

$events->attach(new ValidateOperation($rules));

将事件钩子附加到特定目标

使用 attach_to() 方法,可以将事件钩子附加到特定目标,并且仅对该目标进行调用。

<?php

namespace ICanBoogie;

use ICanBoogie\Routing\Controller;

// …

/* @var $events EventCollection */

$detach = $events->attach_to($controller, function(Controller\ActionEvent $event, Controller $target) {

	echo "invoked!";

});

$controller_clone = clone $controller;

emit(new Controller\ActionEvent($controller_clone, …));   // nothing happens, it's a clone
emit(new Controller\ActionEvent($controller, …));         // echo "invoked!"

// …

$detach(); // You can detach if you no longer want to listen.

附加 一次性 事件钩子

once() 方法附加的事件钩子在使用后自动断开。

<?php

namespace ICanBoogie;

/* @var $events EventCollection */

$n = 0;

$events->once(MyEvent $event, function() use(&$n) {

	$n++;

});

emit(new MyEvent());
emit(new MyEvent());
emit(new MyEvent());

echo $n;   // 1

使用 events 配置附加事件钩子

当通过 ICanBoogieicanboogie/bind-event 绑定包时,可以从 events 配置中附加事件钩子。有关更多详细信息,请参阅 icanboogie/bind-event 包。

将事件钩子附加到 完成链

完成链 在遍历事件链而未停止的情况下执行。

以下示例演示了如何将事件钩子附加到 count 事件的 完成链 以获得字符串 "0123"。如果第三个事件钩子与其他钩子定义相同,我们将获得 "0312"。

<?php

namespace ICanBoogie;

class CountEvent extends Event
{
	public function __construct(
	    public string $count = "0"
    ) {
		parent::__construct();
	}
}

/* @var $events EventCollection */

$events->attach(function(CountEvent $event): void {

	$event->count .= "2";

});

$events->attach(function(CountEvent $event): void {

	$event->count .= "1";

});

$events->attach('count', function(CountEvent $event): void {

	$event->chain(function(CountEvent $event) {

		$event->count .= "3";

	});
});

$event = emit(new CountEvent(0));

echo $event->count; // 0123

断开事件钩子链

可以使用 stop() 方法的事件钩子来中断事件钩子链的处理。

<?php

use ICanBoogie\Operation;

function on_event(Operation\ProcessEvent $event, Operation $operation): void
{
	$event->rc = true;
	$event->stop();
}

事件分析

使用 EventProfiler 类来收集有关未使用事件和事件钩子调用的计时信息。所有时间信息都以浮点微时间计量。

<?php

use ICanBoogie\EventProfiler;

foreach (EventProfiler::$unused as list($time, $type))
{
	// …
}

foreach (EventProfiler::$calls as list($time, $type, $hook, $started_at))
{
	// …
}

辅助工具

  • get_events():返回当前事件集合。如果不存在,则创建一个新的集合。
  • emit():发出指定事件。

持续集成

该项目通过 GitHub actions 进行持续测试。

Tests Static Analysis Code Style

行为准则

本项目遵循 贡献者行为准则。通过参与本项目及其社区,您应遵守此准则。

贡献

有关详细信息,请参阅 CONTRIBUTING

许可

icanboogie/eventBSD-3-Clause 许可证发布。