我们先来看下事件在 Yii2 中的使用,如下内容摘自 Yii2中文文档

事件可以将自定义代码“注入”到现有代码中的特定执行点。附加自定义代码到某个事件,当这个事件被触发时,这些代码就会自动执行。例如,邮件程序对象成功发出消息时可触发 messageSent 事件。如想追踪成功发送的消息,可以附加相应追踪代码到 messageSent 事件。

Yii 引入了名为 [[yii\base\Component]] 的基类以支持事件。如果一个类需要触发事件就应该继承 [[yii\base\Component]] 或其子类。

事件处理器(Event Handlers)

事件处理器是一个PHP 回调函数,当它所附加到的事件被触发时它就会执行。可以使用以下回调函数之一:

  • 字符串形式指定的 PHP 全局函数,如 'trim' ;
  • 对象名和方法名数组形式指定的对象方法,如 [$object, $method] ;
  • 类名和方法名数组形式指定的静态类方法,如 [$class, $method] ;
  • 匿名函数,如 function ($event) { ... } 。

事件处理器的格式是:

function ($event) {
// $event 是 yii\base\Event 或其子类的对象
}

通过 $event 参数,事件处理器就获得了以下有关事件的信息:

  • [[yii\base\Event::name|event name]]:事件名
  • [[yii\base\Event::sender|event sender]]:调用 trigger() 方法的对象
  • [[yii\base\Event::data|custom data]]:附加事件处理器时传入的数据,默认为空,后文详述

附加事件处理器

调用 [[yii\base\Component::on()]] 方法来附加处理器到事件上。如:

$foo = new Foo;

// 处理器是全局函数
$foo->on(Foo::EVENT_HELLO, 'function_name'); // 处理器是对象方法
$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']); // 处理器是静态类方法
$foo->on(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']); // 处理器是匿名函数
$foo->on(Foo::EVENT_HELLO, function ($event) {
//事件处理逻辑
});

附加事件处理器时可以提供额外数据作为 [[yii\base\Component::on()]] 方法的第三个参数。数据在事件被触发和处理器被调用时能被处理器使用。如:

// 当事件被触发时以下代码显示 "abc"
// 因为 $event->data 包括被传递到 "on" 方法的数据
$foo->on(Foo::EVENT_HELLO, function ($event) {
echo $event->data;
}, 'abc');

事件处理器顺序

可以附加一个或多个处理器到一个事件。当事件被触发,已附加的处理器将按附加次序依次调用。如果某个处理器需要停止其后的处理器调用,可以设置 $event 参数的 [yii\base\Event::handled]] 属性为真,如下:

$foo->on(Foo::EVENT_HELLO, function ($event) {
$event->handled = true;
});

默认新附加的事件处理器排在已存在处理器队列的最后。因此,这个处理器将在事件被触发时最后一个调用。在处理器队列最前面插入新处理器将使该处理器最先调用,可以传递第四个参数 $append 为假并调用 [[yii\base\Component::on()]] 方法实现:

$foo->on(Foo::EVENT_HELLO, function ($event) {
// 这个处理器将被插入到处理器队列的第一位...
}, $data, false);

触发事件

事件通过调用 [[yii\base\Component::trigger()]] 方法触发,此方法须传递事件名,还可以传递一个事件对象,用来传递参数到事件处理器。如:

namespace app\components;

use yii\base\Component;
use yii\base\Event; class Foo extends Component
{
const EVENT_HELLO = 'hello'; public function bar()
{
$this->trigger(self::EVENT_HELLO);
}
}

以上代码当调用 bar() ,它将触发名为 hello 的事件。

Tip: 推荐使用类常量来表示事件名。上例中,常量 EVENT_HELLO 用来表示 hello 。这有两个好处。第一,它可以防止拼写错误并支持 IDE 的自动完成。第二,只要简单检查常量声明就能了解一个类支持哪些事件。

有时想要在触发事件时同时传递一些额外信息到事件处理器。例如,邮件程序要传递消息信息到 messageSent 事件的处理器以便处理器了解哪些消息被发送了。为此,可以提供一个事件对象作为 [[yii\base\Component::trigger()]] 方法的第二个参数。这个事件对象必须是 [[yii\base\Event]] 类或其子类的实例。如:

namespace app\components;

use yii\base\Component;
use yii\base\Event; class MessageEvent extends Event
{
public $message;
} class Mailer extends Component
{
const EVENT_MESSAGE_SENT = 'messageSent'; public function send($message)
{
// ...发送 $message 的逻辑... $event = new MessageEvent;
$event->message = $message;
$this->trigger(self::EVENT_MESSAGE_SENT, $event);
}
}

当 [[yii\base\Component::trigger()]] 方法被调用时,它将调用所有附加到命名事件(trigger 方法第一个参数)的事件处理器。

移除事件处理器

从事件移除处理器,调用 [[yii\base\Component::off()]] 方法。如:

// 处理器是全局函数
$foo->off(Foo::EVENT_HELLO, 'function_name'); // 处理器是对象方法
$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']); // 处理器是静态类方法
$foo->off(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']); // 处理器是匿名函数
$foo->off(Foo::EVENT_HELLO, $anonymousFunction);

注意当匿名函数附加到事件后一般不要尝试移除匿名函数,除非你在某处存储了它。以上示例中,假设匿名函数存储为变量$anonymousFunction 。

移除事件的全部处理器,简单调用 [[yii\base\Component::off()]] 即可,不需要第二个参数:

$foo->off(Foo::EVENT_HELLO);

类级别的事件处理器

以上部分,我们叙述了在实例级别如何附加处理器到事件。有时想要一个类的所有实例而不是一个指定的实例都响应一个被触发的事件,并不是一个个附加事件处理器到每个实例,而是通过调用静态方法 [[yii\base\Event::on()]] 在类级别附加处理器。

例如,活动记录对象要在每次往数据库新增一条新记录时触发一个 [[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] 事件。要追踪每个活动记录对象的新增记录完成情况,应如下写代码:

use Yii;
use yii\base\Event;
use yii\db\ActiveRecord; Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::trace(get_class($event->sender) . ' is inserted');
});

每当 [[yii\db\BaseActiveRecord|ActiveRecord]] 或其子类的实例触发 [[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] 事件时,这个事件处理器都会执行。在这个处理器中,可以通过 $event->sender 获取触发事件的对象。

当对象触发事件时,它首先调用实例级别的处理器,然后才会调用类级别处理器。

可调用静态方法[[yii\base\Event::trigger()]]来触发一个类级别事件。类级别事件不与特定对象相关联。因此,它只会引起类级别事件处理器的调用。如:

use yii\base\Event;

Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) {
echo $event->sender; // 显示 "app\models\Foo"
}); Event::trigger(Foo::className(), Foo::EVENT_HELLO);

注意这种情况下 $event->sender 指向触发事件的类名而不是对象实例。

Note: 因为类级别的处理器响应类和其子类的所有实例触发的事件,必须谨慎使用,尤其是底层的基类,如 [[yii\base\Object]]。

移除类级别的事件处理器只需调用[[yii\base\Event::off()]],如:

// 移除 $handler
Event::off(Foo::className(), Foo::EVENT_HELLO, $handler); // 移除 Foo::EVENT_HELLO 事件的全部处理器
Event::off(Foo::className(), Foo::EVENT_HELLO);

全局事件

所谓全局事件实际上是一个基于以上叙述的事件机制的戏法。它需要一个全局可访问的单例,如应用实例。

事件触发者不调用其自身的 trigger() 方法,而是调用单例的 trigger() 方法来触发全局事件。类似地,事件处理器被附加到单例的事件。如:

use Yii;
use yii\base\Event;
use app\components\Foo; Yii::$app->on('bar', function ($event) {
echo get_class($event->sender); // 显示 "app\components\Foo"
}); Yii::$app->trigger('bar', new Event(['sender' => new Foo]));

全局事件的一个好处是当附加处理器到一个对象要触发的事件时,不需要产生该对象。相反,处理器附加和事件触发都通过单例(如应用实例)完成。

然而,因为全局事件的命名空间由各方共享,应合理命名全局事件,如引入一些命名空间(例:"frontend.mail.sent", "backend.mail.sent")。

Yii2的深入学习--事件Event的更多相关文章

  1. Yii2基本概念之——事件(Event)

    说起事件(event),我们可是一点都不陌生.现实生活当中的事件无处不在,比如你发了一条微博,触发了一条事件,导致关注你的人收到了一条消息,看到你发的内容:比如你通过支付宝买东西,付了款,触发一个事件 ...

  2. 学习笔记---Javascript事件Event、IE浏览器下的拖拽效果

    学习笔记---Javascript事件Event.IE浏览器下的拖拽效果     1. 关于event常用属性有returnValue(是否允许事件处理继续进行, false为停止继续操作).srcE ...

  3. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  4. Lite OS学习之事件EVENT

    1. Lite OS的事件EVENT,就是一个任务向另外一个任务通知事件的,不能数据传输.看下有的函数,实际比较复杂 2. 具体还是看编程,先全局结构体整个事件变量 /*事件控制结构体*/ EVENT ...

  5. libevent源码学习(9):事件event

    目录在event之前需要知道的event_baseevent结构体创建/注册一个event向event_base中添加一个event设置event的优先级激活一个event删除一个event获取指定e ...

  6. 经典线程同步 事件Event

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...

  7. jQuery学习-事件之绑定事件(四)

    今天我们来学习jQuery.Event对象.jQuery为了添加自己的处理机制,及可以传递用户自定义数据,于是Event对象就出世了.   1 jQuery.Event = function( src ...

  8. jQuery学习-事件之绑定事件(三)

    在上一篇<jQuery学习-事件之绑定事件(二)>我们了解了jQuery的dispatch方法,今天我们来学习下handlers 方法: handlers: function( event ...

  9. jQuery学习-事件之绑定事件(二)

    在上一篇<jQuery学习-事件之绑定事件(一)>我们了解了jQuery的add方法,今天我们来学习下dispatch方法: dispatch: function( event ) {   ...

随机推荐

  1. 【Beta】Daily Scrum Meeting第二次

    1.任务进度 学号 已完成 接下去要做 502 系负责人及所负责专业的表 写出PHP测试的demo:将okHttp的请求放在非UI线程中执行 509 PHP更该用户信息:更新系负责人所负责系:删除任务 ...

  2. Daily Scrum02 12.14

    大家已经被各种作业折磨得体无完肤了,但是大家还挤出时间完成每天的软件工作啊…… 坚持就是胜利! Member 任务进度 下一步工作 吴文会 调试QuerySetting类函数 调试QuerySetti ...

  3. Android ANR 分析解决方法

    一:什么是ANR ANR:Application Not Responding,即应用无响应 二:ANR的类型 ANR一般有三种类型: 1. KeyDispatchTimeout(5 seconds) ...

  4. gulp es7配置文件

    http://sanwen.net/a/ybsfcoo.html /** * Created by udi on 2016/11/24. */ var gulp = require('gulp'); ...

  5. 高灵活度,高适用性,高性能,轻量级的 ORM 实现

    ORM(Object-Relational Mapping 对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,目的是提供易于理解的模型化数据的方法. ORM虽然有诸多好处,但是在实 ...

  6. [转]各种移动GPU压缩纹理的使用方法

    介绍了各种移动设备所使用的GPU,以及各个GPU所支持的压缩纹理的格式和使用方法.1. 移动GPU大全 目前移动市场的GPU主要有四大厂商系列:1)Imagination Technologies的P ...

  7. ASP.NET MVC 控制器激活(二)

    ASP.NET MVC 控制器激活(二) 前言 在之前的篇幅中,用文字和图像来表示了控制器的激活过程,描述的角度都是从框架默认实现的角度去进行描述的,这样也使得大家都可以清楚的知道激活的过程以及其中涉 ...

  8. kpvalidate开辟验证组件,通用Java Web请求服务器端数据验证组件

    小菜利用工作之余编写了一款Java小插件,主要是用来验证Web请求的数据,是在服务器端进行验证,不是简单的浏览器端验证. 小菜编写的仅仅是一款非常初级的组件而已,但小菜为它写了详细的说明文档. 简单介 ...

  9. 七天学会ASP.NET MVC (三)——ASP.Net MVC 数据处理

    第三天我们将学习Asp.Net中数据处理功能,了解数据访问层,EF,以及EF中常用的代码实现方式,创建数据访问层和数据入口,处理Post数据,以及数据验证等功能. 系列文章 七天学会ASP.NET M ...

  10. Azure Blob Storage 基本用法 -- Azure Storage 之 Blob

    Azure Storage 是微软 Azure 云提供的云端存储解决方案,当前支持的存储类型有 Blob.Queue.File 和 Table. 笔者在<Azure Table storage ...