事件

October CMS Documentation Docs

事件

# 基本用法

Event 类提供了一个简单的观察者实现,允许您订阅和监听应用程序中的事件。 例如,您可以监听用户何时登录并更新他们的上次登录日期。

Event::listen('auth.login', function($user) {
    $user->last_login = new DateTime;
    $user->save();
});

这是通过 Event::fire 方法提供的事件,该方法作为用户登录逻辑的一部分调用,从而使逻辑可扩展。

Event::fire('auth.login', [$user]);

注意:有关 October CMS 本身中可用的所有事件的列表,请参阅 API 文档 (opens new window)

# 监听事件

Event::listen 方法主要用于监听事件,可以在应用程序代码中的任何位置完成。 第一个参数是事件名称。

Event::listen('acme.blog.myevent', ...);

第二个参数可以是一个闭包,它指定触发事件时应该发生什么。 闭包可以接受一些可选的参数,由 触发事件 提供。

Event::listen('acme.blog.myevent', function($arg1, $arg2) {
    // 做
});

您还可以传递对任何可调用对象或 专用事件类 的引用,并将使用它来代替。

Event::listen('auth.login', [$this, 'LoginHandler']);

注意:可调用方法可以选择指定全部、部分或不指定任何参数。 无论哪种方式,事件都不会引发任何错误,除非它指定太多。

# 在哪里注册监听器

最常见的地方是插件注册文件boot方法。

class Plugin extends PluginBase
{
    // ...

    public function boot()
    {
        Event::listen(...);
    }
}

或者,插件可以在插件目录中提供一个名为 init.php 的文件,您可以使用它来放置事件注册逻辑。 例如:

<?php

Event::listen(...);

由于这些方法都不是本质上"正确"的,因此请根据应用程序的大小选择您觉得合适的方法。

# 使用优先级监听

您还可以在监听事件时指定优先级作为第三个参数。 优先级较高的监听器将首先运行,而具有相同优先级的监听器将按监听顺序运行。

// 第一个运行
Event::listen('auth.login', function() { ... }, 10);

// 第二个运行
Event::listen('auth.login', function() { ... }, 5);

# 停止事件

有时您可能希望停止将事件传播给其他监听器。 您可以通过从监听器返回 false 来执行此操作:

Event::listen('auth.login', function($event) {
    // 处理事件

    return false;
});

# 通配符监听器

注册事件监听器时,您可以使用星号指定通配符监听器。 通配符监听器接收首先触发的事件名称,然后是作为数组传递给事件的参数。

以下监听器处理所有以 foo. 开头的事件。

Event::listen('foo.*', function($event, $params) {
    // 处理事件...
});

您可以使用 Event::firing 方法来准确确定触发了哪个事件。

Event::listen('foo.*', function($event, $params) {
    if (Event::firing() === 'foo.bar') {
        // ...
    }
});

# 触发事件

您可以在代码中的任何位置使用 Event::fire 方法来使逻辑可扩展。 这意味着其他开发人员,甚至您自己的内部代码,都可以"挂钩"到这一个代码并注入特定的逻辑。 第一个参数应该是事件名称。

Event::fire('myevent');

使用插件命名空间代码为事件名称添加前缀总是一个好主意,这将防止与其他插件发生冲突。

Event::fire('acme.blog.myevent');

第二个参数是一个值数组,将作为参数传递给监听它的 事件监听器

Event::fire('acme.blog.myevent', [$arg1, $arg2]);

第三个参数指定事件是否应该是 停止事件,这意味着如果返回"非 null"值,它应该停止。此参数默认设置为 false。

Event::fire('acme.blog.myevent', [...], true);

如果事件正在停止,则返回的第一个值被捕获。

// 单一结果,事件停止
$result = Event::fire('acme.blog.myevent', [...], true);

否则,它将以数组的形式返回来自所有事件的所有响应的集合。

// 多个结果,所有事件都被触发
$results = Event::fire('acme.blog.myevent', [...]);

# 通过引用传递参数

在处理或过滤传递给事件的值时,您可以在变量前面加上 & 以通过引用传递它。这允许多个监听器操纵结果并将其传递给下一个监听器。

Event::fire('cms.processContent', [&$content]);

监听事件时,参数也需要在闭包定义中用 & 符号声明。在下面的示例中,$content 变量将在结果中附加"AB"。

Event::listen('cms.processContent', function (&$content) {
    $content = $content . 'A';
});

Event::listen('cms.processContent', function (&$content) {
    $content = $content . 'B';
});

# 队列事件

触发事件可以在 与队列结合使用 中延迟。使用 Event::queue 方法将事件"排队"触发,但不立即触发。

Event::queue('foo', [$user]);

您可以使用 Event::flush 方法刷新所有排队的事件。

Event::flush('foo');

# 使用类作为监听器

在某些情况下,您可能希望使用类而不是闭包来处理事件。类事件监听器将从 Application IoC 容器 中解析出来,为您提供监听器依赖注入的全部功能。

# 监听单个方法

事件类可以像任何其他方法一样使用 Event::listen 方法注册,将类名作为字符串传递。

Event::listen('auth.login', 'LoginHandler');

默认情况下,会调用 LoginHandler 类的 handle 方法:

class LoginHandler
{
    public function handle($data)
    {
        // ...
    }
}

如果你不想使用默认的 handle 方法,你可以指定应该监听的方法名。

Event::listen('auth.login', 'LoginHandler@onLogin');

# 监听整个类

事件订阅者是可以从类本身监听多个事件的类。订阅者应该定义一个 subscribe 方法,该方法将被传递一个事件调度程序实例。

class UserEventHandler
{
    /**
     * 处理用户登录事件。
     */
    public function userLogin($event)
    {
        // ...
    }

    /**
     * 处理用户注销事件。
     */
    public function userLogout($event)
    {
        // ...
    }

    /**
     * 为订阅者注册监听器。
     *
     * @param  Illuminate\Events\Dispatcher  $events
     * @return array
     */
    public function subscribe($events)
    {
        $events->listen('auth.login', 'UserEventHandler@userLogin');

        $events->listen('auth.logout', 'UserEventHandler@userLogout');
    }
}

一旦定义了订阅者,就可以使用 Event::subscribe 方法注册它。

Event::subscribe(new UserEventHandler);

您也可以使用 Application IoC 容器 来解析您的订阅者。为此,只需将订阅者的名称传递给 subscribe 方法。

Event::subscribe('UserEventHandler');

# 事件派发器特征

有时您希望将事件绑定到对象的单个实例。您可以通过在类中实现 October\Rain\Support\Traits\Emitter 特征来使用替代事件系统。

class UserManager
{
    use \October\Rain\Support\Traits\Emitter;
}

此特征提供了一种使用 bindEvent 监听事件的方法。

$manager = new UserManager;
$manager->bindEvent('user.beforeRegister', function($user) {
    // 检查 $user 是否是垃圾邮件发送者
});

fireEvent 方法用于触发事件。

$manager = new UserManager;
$manager->fireEvent('user.beforeRegister', [$user]);

这些事件只会发生在本地对象上,而不是全局发生。