kuria/event

实现中介者和观察者模式变体的事件库

v2.0.1 2018-03-13 21:47 UTC

This package is auto-updated.

Last update: 2024-09-22 17:44:41 UTC


README

实现中介者和观察者模式变体的事件库。

https://www.travis-ci.cn/kuria/event.svg?branch=master

内容

特性

  • 使用任意数量参数发射事件
  • 管理特定或所有事件的监听器
  • 按优先级排序监听器
  • 停止事件传播
  • 多种方式嵌入事件系统

需求

  • 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