koriiit/eventdispatcher

基于PHP-DI的简单事件调度器

v2.0.0 2018-05-02 13:23 UTC

This package is auto-updated.

Last update: 2024-09-04 19:07:53 UTC


README

Build Status Coverage Status Scrutinizer Code Quality StyleCI SensioLabsInsight

Latest Stable Version License

基于PHP-DI的简单事件调度器。

这个库并不旨在成为一个通用库或覆盖所有可能的使用场景。这个库旨在成为那些使用PHP-DI并且喜欢使用PHP定义的人的完美选择。

目标是创建尽可能解耦的代码。使用调度器的代码可能不知道它的监听器,反之亦然,监听器甚至可能不知道它们实际上作为监听器被使用!

安装

EventDispatcher可以通过composer获取

composer require koriit/eventdispatcher

版本

这个库依赖于PHP-DI,因此会受到其兼容性中断的影响。

v2

与PHP-DI ^6.0进行了测试。

v1

与PHP-DI ^5.4进行了测试。

用法

建议您熟悉Koriit\EventDispatcher\EventDispatcherInterfaceKoriit\EventDispatcher\EventContextInterface,因为这两个接口是您使用此库所需的一切。

基本示例

// configure and build your container

$dispatcher = new EventDispatcher($container);

$listener = function (LoggerInterface $logger, Request $request) {
    $logger->info($request->getMethod().' '.$request->getPathInfo());
};

$dispatcher->addListener("init", $listener, 10);

$dispatcher->dispatch("init");

由于我们正在使用PHP-DI,因此我们会对Koriit\EventDispatcher\EventDispatcherInterface创建一个定义。

监听器可以是PHP-DI可以调用的任何内容

// MyClass.php
class MyClass
{
    public function logRequest(LoggerInterface $logger, Request $request)
    {
        $logger->info($request->getMethod().' '.$request->getPathInfo());
    }
}
// configure and build your container

$dispatcher = $container->get(EventDispatcherInterface::class);

$dispatcher->addListener(ApplicationLifecycle::INITIALIZING, [MyClass::class, 'logRequest'], 10);

$dispatcher->dispatch(ApplicationLifecycle::INITIALIZING);

甚至是接口

// MyInterface.php
interface MyInterface
{
    public function logRequest(LoggerInterface $logger, Request $request);
}
// MyClass.php
class MyClass implements MyInterface
{
    public function logRequest(LoggerInterface $logger, Request $request)
    {
        $logger->info($request->getMethod().' '.$request->getPathInfo());
    }
}
// configure and build your container

$dispatcher = $container->get(EventDispatcherInterface::class);

$dispatcher->addListener(InitializationEvent::class, [MyInterface::class, 'logRequest'], 10);

$dispatcher->dispatch(InitializationEvent::class, ["event" => new InitializationEvent()]);

为了使上述示例正常工作,您需要为MyInterface配置一个定义,当然。

警告
事件调度器与实现流畅接口或允许方法链的监听器不兼容。有关更多信息,请阅读以下关于停止调度链的内容。

添加监听器

有两种订阅监听器的方式。在两种情况下,您都必须指定事件名称和调用优先级。优先级值越高,监听器越晚被调用。具有相同优先级的监听器将按照它们被订阅的顺序调用。您可以完全省略优先级参数,因为它默认为0

addListener

首先,通过在Koriit\EventDispatcher\EventDispatcher对象上使用addListener方法。

interface EventDispatcherInterface
{
  // ..

  /**
   * Subscribes a listener to given event with specific priority.
   *
   * Listener must be invokable by PHP-DI.
   *
   * The higher the priority value the later the listener will be called.
   * Listeners with the same priority will be called in the order they have been subscribed.
   *
   * @param mixed  $eventName
   * @param mixed  $listener
   * @param int    $priority
   */
  public function addListener($eventName, $listener, $priority = 0);

  // ...
}

addListeners

其次,通过在Koriit\EventDispatcher\EventDispatcher对象上使用addListeners方法。

interface EventDispatcherInterface
{
  // ..

  /**
   * Subscribes listeners in bulk.
   * 
   * Listeners array is a simple structure of 3 levels:
   * - At first level it is associative array where keys are names of registered events
   * - At second level it is indexed array where keys are priority values
   * - At third level it is simple list containing listeners subscribed to given event with given priority
   * 
   * @param array $listeners
   * 
   * @return void
   */
  public function addListeners($listeners);

  // ...
}

监听器数组是一个三级结构

  • 在第一级中,它是一个关联数组,键是已注册事件的名称
  • 在第二级中,它是一个索引数组,键是优先级值
  • 在第三级中,它是一个简单的列表,包含已订阅给定事件和给定优先级的监听器

示例

// listners.php
// namespace and imports...

return [
    CLIApplicationLifecycle::INITIALIZING => [
        0 => [
            [ConfigServiceInterface::class, 'init'],
        ],
        1 => [
            [CommandsServiceInterface::class, 'load'],
        ],
    ],

    CLIApplicationLifecycle::FINALIZING => [
        100 => [
            function (LoggerInterface $logger, InputInterface $input, $exitCode) {
                $logger->info('Returning from command `'.$input.'` with exit code '.$exitCode);
            },
        ],
    ],
];
// ...

$dispatcher->addListeners(include 'listeners.php');

// ...

调度

调度是一个简单的调用已订阅到已调度事件的监听器的过程。

interface EventDispatcherInterface
{
    // ...

    /**
     * Dispatches an event with given name.
     *
     * @param mixed $eventName
     * @param array $parameters
     *
     * @return EventContextInterface
     */
    public function dispatch($eventName, $parameters = []);

    // ...
}
// ..

$dispatcher->dispatch(ApplicationLifecycle::INITIALIZING);

停止调度

如果调度链中的任何监听器返回一个可以评估为true的值,则调度将被停止,并且将跳过所有剩余的监听器。您也可以通过在事件上下文中调用stop来实现这一点。

虽然这种设计简化了过程,并且不需要将监听器与事件调度器连接,但它使得处理返回无法解释为成功或失败值的监听器变得有问题。这对于允许方法链或实现流畅接口的方法尤其如此。为了解决这个问题,您可以在事件上下文中使用stopignoreReturnValue方法,但这需要将您的监听器与事件上下文连接起来。毕竟,一切都是权衡,有人说过。

上下文

事件上下文是一个简单的数据对象,用于存储有关派遣过程的详细信息。有关更多信息,请参阅Koriit\EventDispatcher\EventContextInterface

参数

您可以通过按名称注入监听器参数来传递额外的参数以供调用者使用。

// ..

$dispatcher->dispatch(InitializationEvent::class, ["event" => new InitializationEvent()]);
function listener($event) {
  // $event is InitializationEvent object
}

eventName, eventContext, eventDispatcher

这里有3个参数是由事件调度器本身注入的

  1. eventName - 派遣事件的名称
  2. eventContext - 当前派遣的上下文对象的引用
  3. eventDispatcher - 事件调度器的引用;这允许嵌套派遣

您不能覆盖它们,因为在使用参数数组中的这些键时会导致异常。