solventt/ event-dispatcher
PSR-14 兼容的事件调度器
Requires
- php: ^8.0
- psr/event-dispatcher: ^1.0.0
Requires (Dev)
- php-di/php-di: ^6.3
- phpunit/phpunit: ^9.5
- squizlabs/php_codesniffer: ^3.6
- vimeo/psalm: ^4.10
Provides
This package is auto-updated.
Last update: 2024-08-31 00:42:41 UTC
README
目录
这是一个 PSR-14 兼容的事件调度器,它提供了多种确定“事件监听器”定义的方法。
注意:以下所有示例,包括 DI 容器,都是使用 php-di 库编写的。但是您可以在代码中使用任何 DI 容器。
安装
// php 7.4+
composer require solventt/event-dispatcher ^0.1
// php 8.0+
composer require solventt/event-dispatcher ^1.0
注意:如果您要使用容器监听提供者,则必须安装任何 PSR-11 兼容的 DI 容器。
容器监听提供者
此提供者仅支持监听器的 字符串 定义。监听器必须实现 __invoke
方法。它允许在调度事件时“延迟”创建可调用监听器。
记住
- 监听器的执行顺序与您在 DI 容器定义中指定的顺序相同;
- 此提供者不支持使用
ON/OFF
或ADD/REMOVE
方法动态绑定/解绑监听器到/从事件
DI 容器中的“事件监听器”定义,
第一种格式
return [ 'eventsToListeners' => [ FirstEvent::class => [ InvokableListenerOne::class, InvokableListenerTwo::class, ], SecondEvent::class => [ InvokableListenerThree::class, InvokableListenerFour::class, ], ] ];
第二种格式(不指定事件,监听器事件将自动解决)
return [ 'eventsToListeners' => [ InvokableListenerOne::class, InvokableListenerTwo::class, InvokableListenerThree::class, InvokableListenerFour::class, ] ];
第三种混合格式(数组和字符串)
return [ 'eventsToListeners' => [ FirstEvent::class => [ InvokableListenerOne::class, InvokableListenerTwo::class, ], InvokableListenerThree::class, InvokableListenerFour::class, ] ];
DI 容器的事件调度器定义
return [ EventDispatcherInterface::class => function (ContainerInterface $container) { $provider = new ContainerListenerProvider($container); return new EventDispatcher($provider); } ];
在代码的某个地方
public function __construct(private EventDispatcherInterface $dispatcher) {} ... $this->dispatcher->dispatch(new FirstEvent());
'eventsToListeners'
- 默认情况下,由容器提供者识别的定义名称。如果您想使用不同的名称,您必须将其作为第二个参数传递给 ContainerListenerProvider
构造函数
return [ 'yourCustomName' => [ FirstEvent::class => [ InvokableListenerOne::class InvokableListenerTwo::class ], ], EventDispatcherInterface::class => function (ContainerInterface $container) { $provider = new ContainerListenerProvider($container, 'yourCustomName'); return new EventDispatcher($provider); } ];
经典监听提供者
此提供者仅支持监听器的可调用定义。您可以使用以下方式将监听器分配给事件
- 提供者构造函数
- 和/或
ON
方法(OFF
方法解绑监听器)
使用
$provider = new ClassicListenerProvider([ FirstEvent::class => [ new InvokableClass(), [ArrayCallable::class, 'test'], [new ArrayCallable(), 'test2'] ], SecondEvent::class => [ 'usualFunc', $someClosure ] ]); $dispatcher = new EventDispatcher($provider); $dispatcher->dispatch(new FirstEvent());
如果您使用 DI 容器,您的定义可能如下所示
return [ EventDispatcherInterface::class => function (ContainerInterface $container) { $someClosure = function (FirstEvent $event): void {}; $provider = new ClassicListenerProvider([ SomeEvent::class => [ new InvokableClass(), [ArrayCallable::class, 'test'], [new ArrayCallable(), 'test2'], 'usualFunc', $someClosure ], ]); return new EventDispatcher($provider); } ];
此外,您还可以指定监听器执行顺序(整数参数)。值越高,调用越早
... [ SomeEvent::class => [ [new InvokableClass(), 3], [[ArrayCallable::class, 'test'], 6], [[new ArrayCallable(), 'test2'], 10], ['usualFunc', 1], [$someClosure, 12] ], ] ...
在代码的某个地方使用 ON
方法定义“事件监听器”
public function __construct(private EventDispatcherInterface $dispatcher) {} ... $this->dispatcher->on(FirstEvent::class, new InvokableClass()); $this->dispatcher->on(SecondEvent::class, [ArrayCallable::class, 'test']); $this->dispatcher->on(ThirdEvent::class, function (ThirdEvent $event): void {});
以及优先级
$this->dispatcher->on(FirstEvent::class, new InvokableClass(), 4);
要从一个事件中解绑监听器,请使用 OFF
方法
$this->dispatcher->off(FirstEvent::class, new InvokableClass()); $this->dispatcher->off(SecondEvent::class, [ArrayCallable::class, 'test']);
反射监听提供者
此提供者仅接受没有指定事件的可调用定义的监听器(它将自动解决)。
您可以使用以下方式将监听器分配给事件
- 提供者构造函数
- 和/或
ADD
方法(REMOVE
方法解绑监听器)
使用
$provider = new ReflectionListenerProvider([ new InvokableClass(), [ArrayCallable::class, 'test'], [new ArrayCallable(), 'test2'], 'usualFunc', $someClosure ]); $dispatcher = new EventDispatcher($provider); $dispatcher->dispatch(new FirstEvent());
以及监听器执行顺序
... [ [new InvokableClass(), 0], [[ArrayCallable::class, 'test'], 2], [new ArrayCallable(), 'test2'], ['usualFunc', 3], // will be executed the first $someClosure // 0 - a default priority, if it isn't specified explicitly ] ...
在代码的某个地方使用 ADD
方法定义“事件监听器”
public function __construct(private EventDispatcherInterface $dispatcher) {} ... $this->dispatcher->add(new InvokableClass()); $this->dispatcher->add([ArrayCallable::class, 'test']); $this->dispatcher->add(function (ThirdEvent $event): void {});
以及优先级
$this->dispatcher->add(new InvokableClass(), 4);
要从一个事件中解绑监听器,请使用 REMOVE
方法
$this->dispatcher->remove(new InvokableClass()); $this->dispatcher->remove([ArrayCallable::class, 'test']);
延迟事件
在某些情况下,您可能需要使用延迟事件。
public function __construct(private EventDispatcherInterface $dispatcher) {} ... $this->dispatcher->defer(new FirstEvent()); // no listeners are called; $this->dispatcher->defer(new SecondEvent()); // no listeners are called; $this->dispatcher->dispatchDeferredEvents(); // FirstEvent and SecondEvent are dispatched
根据 PSR-14 的监听器约束
PSR-14 的引用
- "监听器必须有一个且仅有一个参数,即它响应的事件";
- "监听器应有一个空的返回值,并且应显式地使用类型提示返回值"。
您可以说,SHOULD 不等于 MUST。但是我想不出可能需要从监听器返回值并使用它们的使用场景。无论如何,这个调度器没有实现它。
因此,在这个包中,您不能指定
- 空监听器签名;
- 监听器签名中的参数多于一个;
- 除了
void
之外的任何返回类型。省略返回类型也是不允许的
您必须提供监听器参数的类型提示 - 它可以是现有的类或object
类型。不接受其他类型提示。
在这些情况下,将抛出异常
// Listeners callbacks public function noParameters(): void {} public function moreThanOneParameter(FirstEvent $event, string $name): void {} public function undefinedParameterType($event): void {} public function noReturnType(object $event) {} public function wrongReturnType(FirstEvent $event): string {}
但是,您可以关闭这些监听器约束。在某个监听器提供者的构造函数中传递false
是必要的
$provider = new ContainerListenerProvider($container, 'someDefinitionName', false); ... $provider = new ClassicListenerProvider([...definition...], false);
然而,未提供相应事件的监听器会忽略此设置,因为它需要类型提示的参数来解析事件
return [ 'eventsToListeners' => [ FirstEvent::class => [InvokableListenerOne::class], // supports switching off the listener constraints InvokableListenerTwo::class, // doesn't support switching off the listener constraints ] ];
此外,ReflectionListenerProvider
不支持关闭监听器约束。
停止传播事件
您的事件可以实现PSR-14的StoppableEventInterface
(来自PSR-14),以对监听器执行有更多的控制。例如,我们有一个以下的事件类
class FirstEvent implements StoppableEventInterface { public string $result = ''; public function isPropagationStopped(): bool { return (bool) preg_match('/stop/', $this->result); } }
并且有三个监听器
public function __invoke(FirstEvent $event): void { $event->result = 'First'; } ... public function __invoke(FirstEvent $event): void { $event->result .= '-stop'; } ... public function __invoke(FirstEvent $event): void { $event->result .= '-Test'; }
监听器定义
return [ 'eventsToListeners' => [ FirstEvent::class => [ InvokableListenerOne::class, InvokableListenerTwo::class, // adds the '-stop' string in the $result property of FirstEvent InvokableListenerThree::class, // will not be executed ], ], EventDispatcherInterface::class => function (ContainerInterface $container) { $provider = new ContainerListenerProvider($container); return new EventDispatcher($provider); } ];
第三个监听器将不会执行,因为FirstEvent
的isPropagationStopped()
方法在第二个监听器执行后返回true
。
使用迭代器而不是数组
提供者也会在它们的构造函数中收到迭代器。但迭代器必须实现ArrayAccess
接口。
示例1
$definition = [ FirstEvent::class => [ new InvokableClass(), [ArrayCallable::class, 'test'], ] ]; $provider = new ClassicListenerProvider(new ArrayIterator($definition));
示例2
class ListenerIterator extends ArrayIterator { public function __construct() { parent::__construct($this->getListeners()); } private function getListeners(): array { return [ FirstEvent::class => [ new InvokableClass(), [[ArrayCallable::class, 'test'], 2], ], SecondEvent::class => [ 'usualFunc', [new ArrayCallable(), 'test2'] ] ]; } }
在代码的某个地方
$provider = new ClassicListenerProvider(new ListenerIterator());
创建监听器提供者
您需要实现PSR-14的ListenerProviderInterface
。如果您的提供者想要支持ON/OFF
方法,您还必须实现SubscribingInterface
。