kuria / event
实现中介者和观察者模式变体的事件库
Requires
- php: >=7.1
Requires (Dev)
- kuria/dev-meta: ^0.1.0
README
实现中介者和观察者模式变体的事件库。
内容
特性
- 使用任意数量参数发射事件
- 管理特定或所有事件的监听器
- 按优先级排序监听器
- 停止事件传播
- 多种方式嵌入事件系统
需求
- PHP 7.1+
组件
事件发射器
EventEmitter
类维护一个监听器列表并将事件分发到它们。
它被设计为用作中介。
<?php use Kuria\Event\EventEmitter; $emitter = new EventEmitter();
EventEmitter
实现了 ObservableInterface
。
可观察者
抽象的 Observable
类使用内部事件发射器实现 ObservableInterface
。
它被设计为被子类扩展,这些子类将发射它们自己的事件
<?php use Kuria\Event\Observable; class MyComponent extends Observable { function doSomething() { $this->emit('something'); } }
或者,您可以使用 ObservableTrait
来实现相同的结果
<?php use Kuria\Event\EventEmitterPropInterface; use Kuria\Event\ObservableInterface; use Kuria\Event\ObservableTrait; class MyComponent implements ObservableInterface, EventEmitterPropInterface { use ObservableTrait; // ... }
用法
以下适用于两者事件发射器和可观察者,因为它们都实现了 ObservableInterface
。
监听事件
使用回调
要将回调注册为在特定事件发生时调用,请使用 on()
方法进行注册。任何事件参数都将直接传递给它。
<?php $observable->on('some.event', function ($arg1, $arg2) { // do something });
- 回调可以通过返回
FALSE
来停止事件传播 - 可以使用
on()
方法的第三个参数指定监听器优先级
要注销回调,请使用具有相同回调的 off()
方法(在闭包的情况下,这意味着相同的对象)
<?php $observable->off('some.event', $callback); // returns TRUE on success
使用事件监听器
要注册事件监听器,请使用 addListener()
方法
<?php use Kuria\Event\EventListener; $observable->addListener( new EventListener( 'some.event', function ($arg1, $arg2) {} ) );
- 可以使用
EventListener
构造函数的第三个参数指定监听器优先级 - 回调可以通过返回
FALSE
来停止事件传播
要注销监听器,请使用具有相同事件监听器对象的 removeListener()
方法
<?php $observable->removeListener($eventListener); // returns TRUE on success
使用事件订阅者
事件订阅者订阅一系列事件。每个事件通常映射到订阅者的一个方法。
可以使用方便的 listen()
方法创建监听器(如下面的示例所示)或手动创建 EventListener
实例。
- 任何回调或方法都可以通过返回
FALSE
来停止事件传播 - 可以使用
listen()
方法的第三个参数或EventListener
构造函数的第三个参数指定监听器优先级
<?php use Kuria\Event\EventSubscriber; class MySubscriber extends EventSubscriber { protected function getListeners(): array { return [ $this->listen('foo.bar', 'onFooBar'), $this->listen('lorem.ipsum', 'onLoremIpsum', 10), $this->listen('dolor.sit', 'onDolorSitA'), $this->listen('dolor.sit', 'onDolorSitB', 5), ]; } function onFooBar() { /* do something */ } function onLoremIpsum() { /* do something */ } function onDolorSitA() { /* do something */ } function onDolorSitB() { /* do something */ } } $subscriber = new MySubscriber();
注册事件订阅者
<?php $subscriber->subscribeTo($observable);
注销事件订阅者
<?php $subscriber->unsubscribeFrom($observable);
停止事件传播
任何监听器都可以通过返回 FALSE
来停止当前事件的进一步传播。
这可以防止调用任何其他监听器。
监听器优先级
监听器优先级决定了监听器调用的顺序
- 具有更高优先级的监听器将更早调用
- 具有较低优先级的监听器将较晚调用
- 如果优先级相等,调用顺序是未定义的
- 优先级可以是负数
- 默认优先级是
0
监听所有事件
要监听所有事件,请使用 ObservableInterface::ANY_EVENT
替代事件名称
<?php use Kuria\Event\EventListener; use Kuria\Event\ObservableInterface; $observable->on( ObservableInterface::ANY_EVENT, function ($event, $arg1, $arg2) {} ); $observable->addListener( new EventListener( ObservableInterface::ANY_EVENT, function ($event, $arg1, $arg2) {} ) );
- 全局监听器在特定事件的监听器之前调用
- 全局监听器在发射事件参数之前会得到一个额外的事件名称参数。
- 全局监听器还可以通过返回
FALSE
来停止事件传播,并且可能已经指定了 监听器优先级。
发射事件
事件是通过 emit()
方法发射的。
<?php $observable->emit('foo');
任何额外的参数都会传递给监听器。
<?php $observable->emit('foo', 'hello', 123);
注意
变量引用不能直接作为参数发射。如果您需要使用引用,请将它们包裹在对象或数组中。
事件文档
虽然事件库本身不需要这样做,但在某处显式定义可能的事件名称及其参数是一个好主意。
以下示例定义了一个 FieldEvents
类用于此目的。然后使用该类的常量代替事件名称,它们的注释作为文档。这也允许代码自动完成。
<?php use Kuria\Event\Observable; /** * @see Field */ abstract class FieldEvents { /** * Emitted when field value is about to be changed. * * @param Field $field * @param mixed $oldValue * @param mixed $newValue */ const CHANGE = 'change'; /** * Emitted when field value is about to be cleared. * * @param Field $field */ const CLEAR = 'clear'; } /** * @see FieldEvents */ class Field extends Observable { private $name; private $value; function __construct(string $name, $value = null) { $this->name = $name; $this->value = $value; } function getName(): string { return $this->name; } function getValue() { return $this->value; } function setValue($value): void { $this->emit(FieldEvents::CHANGE, $this, $this->value, $value); $this->value = $value; } function clear() { $this->emit(FieldEvents::CLEAR, $this); $this->value = null; } }
注意
在类常量上使用 @param
注释是非标准的,但集成开发环境(IDE)并不介意,一些文档生成器(如 Doxygen)甚至可以很好地显示它们。
使用示例
<?php $field = new Field('username'); $field->on(FieldEvents::CHANGE, function (Field $field, $oldValue, $newValue) { echo "Field '{$field->getName()}' has been changed from '{$oldValue}' to '{$newValue}'\n"; }); $field->on(FieldEvents::CLEAR, function (Field $field) { echo "Field '{$field->getName()}' has been cleared\n"; }); $field->setValue('john.smith'); $field->setValue('foo.bar123'); $field->clear();
输出
Field 'username' has been changed from '' to 'john.smith' Field 'username' has been changed from 'john.smith' to 'foo.bar123' Field 'username' has been cleared