An event dispatching system with PSR-14.
event-dispatcher 是一个事件派发系统。它派发一个事件,并以优先级顺序调用预先定义的事件处理程序。
事件系统由以下5个概念构成:
- 事件 (Event): Event 是事件信息的载体,它往往围绕一个动作进行描述,例如 “用户被创建了”、“准备导出 excel 文件” 等等,Event 的内部需要包含当前事件的所有信息,以便后续的处理程序使用。
- 监听器 (Listener): Listener 是事件处理程序,负责在发生某一事件(Event)时执行特定的操作。
- Listener Provider: 它负责将事件(Event)与监听器(Listener)进行关联,在触发一个事件时,Listener Provider 需要提供绑定在该事件上的所有监听器。
- 派发器 (EventDispatcher): 负责通知某一事件发生了。我们所说的“向某一目标派发一个事件”,这里的“目标”指的是 Listener Provider,也就是说,EventDispatcher 向 Listener Provider 派发了 Event。
- 订阅器 (Subscriber): 订阅器是 Listener Provider 的扩展,它可以将不同的事件和订阅器里的方法进行自由绑定,这些操作都在订阅器内部进行,这样可以将同类事件的绑定与处理内聚,便于管理。
composer require constanze-standard/event-dispatcher
use ConstanzeStandard\EventDispatcher\Event;
$event = new Event();
echo $event->getName(); // \ConstanzeStandard\EventDispatcher\Event
\ConstanzeStandard\EventDispatcher\Event
基于 \ConstanzeStandard\EventDispatcher\Interfaces\EventInterface
,并实现了StoppableEventInterface
。
\ConstanzeStandard\EventDispatcher\Interfaces\EventInterface::getName
方法返回当前事件的唯一标识,默认为类名称。
定制的 CustomEvent
:
use ConstanzeStandard\EventDispatcher\Event;
class CustomEvent extends Event
{
private $id;
private $data;
public function __construct($id, $data)
{
$this->id = $id;
$this->data = $data;
}
public function getName()
{
return $id;
}
}
$event = new CustomEvent('user.create', ['id' => 1, 'name' => 'Alex']);
如果你的事件需要在网络间传输,你可能还需要实现 Serializable 接口,以便将有用的信息序列化:
use ConstanzeStandard\EventDispatcher\Event;
class CustomEvent extends Event implements \Serializable
{
...
public function serialize()
{
return serialize([
'name' => $this->name,
'data' => $this->data
]);
}
public function unserialize($serialized)
{
$array = unserialize($serialized);
$this->name = $array['name'];
$this->data = $array['data'];
}
}
$event = new CustomEvent('user.create', ['id' => 1, 'name' => 'Alex']);
$decompEvent = unserialize(serialize($event));
Listener 可以是任意可调用对象,或任意对象的方法,它可以接受一个 \ConstanzeStandard\EventDispatcher\Interfaces\EventInterface
的实例作为唯一参数,Listener 必须返回传入的 event 相同类型的实例对象:
use ConstanzeStandard\EventDispatcher\Interfaces\EventInterface;
// 可调用对象
function onSomeEvent(EventInterface $event) {
echo $event->getName();
return $event;
}
class Listener
{
// 普通的类方法
public function onSomeEvent(EventInterface $event)
{
echo $event->getName();
return $event;
}
}
\ConstanzeStandard\EventDispatcher\Event::withPropagationStopped
将返回一个与当前 event 对象相同,但携带了终止信号的 event,如果将它作为返回值,将终止后续的 event 派发。
use ConstanzeStandard\EventDispatcher\Interfaces\EventInterface;
function onSomeEvent(EventInterface $event) {
return $event->withPropagationStopped();
}
Listener Provider 负责确定哪些 listener 与当前派发的事件相关,并将相关的 listener 提供给 dispatcher. 添加一个 Listener:
use ConstanzeStandard\EventDispatcher\ListenerProvider;
$listenerProvider = new ListenerProvider();
$listenerProvider->addListener('user.create', onSomeEvent::class, 2);
$listenerProvider->addListener('user.create', [new Listener(), 'onSomeEvent'], 10);
如上所示,通过 ConstanzeStandard\EventDispatcher\ListenerProvider::addListener
方法绑定关系,第一个参数是事件 id;第二个参数是监听器对象,可以直接传入可调用对象,或形如 [对象, 方法名称] 的数组;第三个参数是优先级,必须为数字,数字越大优先级越高(越先调用),默认优先级为 0。
ConstanzeStandard\EventDispatcher\EventDispatcher
在初始化时绑定一个 Listener Provider 实例,然后可以调用 EventDispatcher::dispatch
方法将事件发送给 Listener Provider:
use ConstanzeStandard\EventDispatcher\EventDispatcher;
use Psr\EventDispatcher\ListenerProviderInterface;
/** @var ListenerProviderInterface $listenerProvider */
$dispatcher = new EventDispatcher($listenerProvider);
$dispatcher->dispatch($event);
上例会直接触发 $event
事件,并且按优先级调用 $listenerProvider
中绑定到该事件上的所有监听器。
我们之前利用 ListenerProvider::addListener
添加事件和监听器的关系,这种方式比较过程化,也无法体现出一组事件之间的关系,所以在实践中往往会提出“订阅器”的概念。
订阅器(Subscriber)实际上是对 ListenerProvider::addListener
的一种装饰。使用订阅器需要实现 ConstanzeStandard\EventDispatcher\Interfaces\SubscriberInterface
接口:
use ConstanzeStandard\EventDispatcher\Interfaces\EventInterface;
use ConstanzeStandard\EventDispatcher\Interfaces\SubscriberInterface;
class UserSubscriber implements SubscriberInterface
{
public function subscribe(Closure $subscriber)
{
$subscriber(
'user.signup',
'onSignup',
['onCreate', 1]
);
}
public function onSignup(EventInterface $event)
{
...
return $event;
}
public function onCreate(EventInterface $event)
{
...
return $event;
}
}
$listenerProvider->addSubscriber(new UserSubscriber());
ConstanzeStandard\EventDispatcher\Interfaces\SubscriberInterface::subscribe
方法会提供一个订阅器闭包函数,这个函数的第一个参数是事件 id,后续的参数都是当前类中方法的信息,你可以直接指定方法名称,或用一个数组提供方法名称和优先级。
在 ConstanzeStandard\EventDispatcher\ListenerProvider::addSubscriber
的过程中,$subscriber 函数会将当前类中的方法转化为普通的 Listener.