timatanga / events
timatanga Events 包
Requires
- php: ^8.0
- psr/event-dispatcher: ^1.0
Requires (Dev)
- phpunit/phpunit: 10.0.x-dev
- symfony/var-dumper: 6.0.x-dev
README
将各种应用包聚集在一起需要满足通信需求的基础。因此,事件在解耦包的同时,允许在状态变化时挂钩。
本包深受 Laravel 和 Symfony 的影响。对优秀代码的热爱和对社区的贡献是巨大的。请参阅以下文档以获取更多信息:https://symfony.com.cn/doc/current/components/event_dispatcher.html#events https://laravel.net.cn/docs/8.x/events
为什么不使用那里的组件并提供一个替代组件?我选择这条路径有各种原因
- Laravel 提供了一个启发灵感的、表达性强的、易于使用的接口。另一方面,对其他包的依赖关系链让我担忧。在构建一个网络应用程序时,我希望没有或至少有最小的依赖关系。也可能某些功能并不需要。
- Symfony EventDispatcher 组件提供了大量的文档和关于事件使用案例的出色解释。在最小依赖方面,它满足了基础包的预期。另一方面,如果您不经常使用它,界面需要一些脑力劳动。
所以为什么不将这些优势结合在一个简单、轻量级的事件包中。
安装
composer require timatanga/events
分发器
分发器类就像是事件通知系统的脉搏。事件和监听器 创建一个事件分发器实例非常简单
use timatanga\Events\Dispatcher;
$dispatcher = new Dispatcher();
事件分发器支持以下功能
// register one event (as string) or multiple events (as array of strings) for listener
$dispatcher->listen( $events, $listener );
// unregister event listeners
$dispatcher->unlisten( $events, $listener );
// subscribe subscriber implementing EventSubscriberInterface
$dispatcher->subscribe( $subscriber );
// unregister subscriber
$dispatcher->unsubscribe( $subscriber );
// dispatch event class (payload is then event itself) or event name as string with optional payload argument
$dispatcher->dispatch( $event, $payload );
// check if any listener is registered for event (or all listeners when argument left away )
$dispatcher->hasListeners( $event );
// get event listeners for event (or all listeners when argument left away )
$dispatcher->getListeners( $event );
事件
在应用包之间进行通信时,通常使用事件类进行事件分发。事件类充当相关数据集的容器。根据用例,为任何事件构建事件类可能是一种过度的开销。这两种情况的一个共同点,它们共享事件名称和事件数据。
事件命名
当事件被分发时,它通过一个唯一的名称被识别,可能有任意数量的监听器在监听。唯一的名称可以是任何字符串,但也可以遵循一些命名约定
- 只使用小写字母、数字、点(.)和下划线(_);
- 使用命名空间作为前缀,后跟一个点(例如:order.,user.);
- 以表示已采取动作的动词结束名称(例如:order.placed,order_placed)。
为了确保这些命名约定,事件分发器包括一些检查和转换
- 对于具有 'name' 属性的事件类,'name' 属性被转换为蛇形写法
- 对于没有 'name' 属性的事件类,事件名称通过类名(不带命名空间)构建,并转换为蛇形写法,例如:事件类 'User\UserProfileCreated' 转换为事件名称 'user_profile_created'
- 对于作为字符串注册和分发的事件,应用蛇形写法转换,例如:事件名称 'userProfileCreated' 转换为事件名称 'user_profile_created'
事件有效载荷
无论是作为事件类分发还是以轻量级模式分发,事件都具有传输有效载荷的能力。
事件类分发的有效载荷
// dispatching event class
$dispatcher->dispatch(new Event($subject, [..]))
// event name: 'event'
// event payload: event class itself
请参阅事件类章节以获取有关通用事件类的更多详细信息
轻量级模式的有效载荷
// dispatching simple event
$dispatcher->dispatch('event', [...])
// event name: 'event'
// event payload: [...]
事件类
基本的事件 timatanga\Events\Event 类方便那些希望在应用程序中仅使用一个事件对象的人使用。它可以直接用于大多数目的,因为它遵循标准的观察者模式,其中事件对象封装了一个事件‘主题’,但还增加了可选的额外参数。
__construct($subject, $arguments): Constructor takes the event subject and any arguments;
getSubject(): Get the subject;
setArgument(): Sets an argument by key;
setArguments(): Sets arguments array;
getArgument(): Gets an argument by key;
getArguments(): Getter for all arguments;
hasArgument(): Returns true if the argument key exists;
Event 类实现了 ArrayAccess 和 IteratorAggregate,这使得在事件主题方面传递额外参数变得非常方便。
Event 类实现了 StoppableEventInterface。由于监听器可以访问事件本身,它可以通过使用 $e->stopPropagation() 来停止进一步传播。任何等待处理该事件名称的监听器将不会被事件分发器通知。
监听器
为了利用现有事件,您需要将监听器连接到分发器,以便在事件被分发时进行通知。调用分发器的 listen() 方法将任何有效的 PHP 可调用对象关联到事件。listen() 方法接受最多两个参数
- 监听器想要监听的事件名称(字符串);
- 当指定的事件被分发时将被执行的 PHP 可调用对象;
监听器可调用对象
PHP 可调用对象是一个 PHP 变量,它可以由 call_user_func() 函数使用,并在传递给 is_callable() 函数时返回 true。它可以有以下几种情况
-
\Closure 实例;
$dispatcher->listen('foo', function () use ($a) { ... do something ... }; ); -
实现 invoke() 方法的对象;
$dispatcher->listen('foo', new CallableClass() { public function __invoke(...$args) { ... do something ... } } ); -
表示函数的字符串或表示对象方法或类方法的数组。
$dispatcher->listen('foo', [$listener, $method] );
通配符监听器
此包提供的事件分发器支持通配符事件,以便更通用的监听器。例如。
- 注册为 order.created 的监听器仅对此特定事件起作用
- 注册为 order.* 的监听器将对所有匹配点前缀表示法的事件起作用,如 order.processed、order.closed
订阅者
监听事件最常见的方式是将事件监听器注册到分发器。此监听器可以监听一个或多个事件,并在这些事件被分发时被通知。
另一种监听事件的方式是通过事件订阅者。事件订阅者是一个 PHP 类,能够告诉分发器它应该订阅哪些事件。它实现了 timatanga\Events\Contracts\EventSubscriberInterface 接口,该接口要求一个名为 getSubscribedEvents() 的静态方法。根据订阅者类,分发器解析并注册所有应执行的事件。
class mySubscriber implements EventSubscriberInterface
{
/*
* Only method required by the EventSubscriberInterface
*/
public static function getSubscribedEvents()
{
return [
order.created => [ onOrderCreated, onOrderCreatedPost ],
order.closed => [],
];
}
public function onOrderCreated( $event )
{
// ...
}
public function onOrderCreatedPostfix( $event, $dispatcher )
{
// ...
}
分发器在事件被分发时调用注册的方法。分发器允许两个监听器参数
- 负载:事件类本身或通过 listen 方法注册事件时提供的负载
- 分发器:分发器实例,以便进行高级用例
自动发现
此包提供的事件发现功能,而不是逐个程序化注册所有事件监听器和事件订阅者。在 config/events.php 文件中,可以添加包含监听器和订阅者的目录以进行自动发现。
return [
'listeners' => [
'timatanga/events/tests/Data'
],
'subscribers' => [
'timatanga/events/tests/Data'
],
];
上述配置将搜索 timatanga/events/tests/Data 目录中的事件监听器和订阅者。
事件监听器必须实现 timatanga\Events\Contracts\EventListenerInterface,它强制执行静态 handle 方法以在事件上执行。事件订阅者必须实现 timatanga\Events\Contracts\EventSubscriberInterface,如前几章所述。
在识别实现相应接口的类之后,监听器和订阅者将被注册到事件分发器。如果您想避免自动发现,请创建分发器实例
$dispatcher = new Dispatcher(['autoDiscovery' => false]);