热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

yii2Event实例及源码分析

2019独角兽企业重金招聘Python工程师标准yii2可以方便的使用Event组件基类来实现注册事件和监听触发机制,每个事件都有自身的事件队列首先要知道&#x

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

yii2 可以方便的使用 Event 组件基类 来实现 注册事件和监听触发 机制,每个事件都有自身的事件队列

首先要知道,yii2的事件一共有三类:对象级,类级,全局 \Yii::$app 级

对象和 全局是通过 yii\base\Component 管理的

类级 是通过 yii\base\Event 管理的

yii\base\Object |--yii\base\Behavior|--yii\base\Event|--yii\base\Component|--yii\base\Application|--yii\base\Controller..............

而且要注意的一点是:对象的事件队列触发完毕后会隐式的检测对象所属的类是否也存在此事件的队列,若存在,则会使用 Event::trigger($this, $event_name, $event = null) 进行触发,$this 传递进去会被提取类名,进而使得管理类级别的 Event 也可以执行事件,看下源码就知道了

yii\base\Event 定义和实现了event的一些基础属性和类级别的方法。虽然在对象级的事件中处理时并没有直接调用 Event 中的类级别的事件方法,但传递给 handler 方法的 event 都是 Event 的实例。

在 yii\base\Component 中重写了事件的 on/trigger 将其转变为对象级别的方法。要注意,yii2 会始终检测此对象的类是否有注册相同的事件,若注册则同样会联动触发,此时虽然是触发类级别的事件,但触发事件的上下文依然是对象级别的,默认的 event->sender 依然是当前对象

//触发的事件名称public $name;//触发的传递者 如果是对象级的事件则sender默认为此对象$this 如果是类级别的则默认为 nullpublic $sender;//是否终端事件队列的执行//因为事件注册的 handler 方法可以接收 $event 对象 所以如果在内部将 $event->handled设为 true 后//事件队列将会终端不再向下执行public $handled = false;//用于传递的数据public $data;

以上为 Event 基类的属性,我们可以继承它来扩展我们自己想要的 $event 回调参数的字段


yii\base\Component 的 on 方法

我们常用的组件:控制器、模型等组件的事件都是继承于 Component 的

通过 on 方法向自身的 _events 队列数组注册事件,默认挂载到队列尾部,根据时序性执行,但你也可以传参 append 为 false 将此事件从队列头压入,会优先于当前已注册的事件执行

$name   事件名称

$hander 负责处理方法 此方法会接受 Event 的对象 我们可以将数据填充给此对象做到传参的目的

$handled 标注事件队列是否处理完成,true的话讲不会再执行队列中的剩余事件

$data 这里的data 最终会赋值给 $event->data 属性,可以传递一些参数,但注意他对标量并不会延迟绑定,比如你注册时传递 $this->name,则是以 name 属性的当前值存储的,后续 name 改变,触发时这个改变也无法传递给 data,这里传递 $this 对象本身比较靠谱,可以适当的提高灵活性

/** * @param string $name the event name* @param callable $handler the event handler* @param mixed $data the data to be passed to the event handler when the event is triggered.* When the event handler is invoked, this data can be accessed via [[Event::data]].* @param boolean $append whether to append new event handler to the end of the existing* handler list. If false, the new handler will be inserted at the beginning of the existing* handler list.* @see off()*/public function on($name, $handler, $data = null, $append = true){$this->ensureBehaviors();if ($append || empty($this->_events[$name])) {$this->_events[$name][] = [$handler, $data];} else {array_unshift($this->_events[$name], [$handler, $data]);}}

yii\base\Component 的 trigger 方法

trigger 可以不传递 Event 参数,内部默认会给你 new 一个 yii\base\Event 对象,并默认将 sender 属性默认为当前调用触发的对象$this,当然我们也可以传递一个 Event 的实例来自定义设定。

public function trigger($name, Event $event = null){$this->ensureBehaviors();if (!empty($this->_events[$name])) {if ($event === null) {$event = new Event;}if ($event->sender === null) {$event->sender = $this;}$event->handled = false;$event->name = $name;foreach ($this->_events[$name] as $handler) {$event->data = $handler[1];call_user_func($handler[0], $event);// stop further handling if the event is handledif ($event->handled) {return;}}}// invoke class-level attached handlersEvent::trigger($this, $name, $event);}

注意:最后的 Event::trigger($this, $name, $event),即我们之前说的此对象的类如果也有注册相同事件,则也会被触发,同时要注意这里是对象级别隐式调用事件,传递的 $class 为 $this,如果传递的 $event->sender属性为 null 的话在下面的执行中会被默认为 $this,嗯,也就像我们之前说的 对象触发默认 sender 为本对象,类触发默认 sender 为 null


yii\base\Event::on 方法

与 yii\base\Component::on 没有太大区别,都是管理一个事件的队列,这里只不过需要给出类名而已

yii\base\Event::trigger 方法

public static function trigger($class, $name, $event = null){if (empty(self::$_events[$name])) {return;}if ($event === null) {$event = new static;}$event->handled = false;$event->name = $name;//这里的判断主要是针对对象隐式调用自身类的事件if (is_object($class)) {if ($event->sender === null) {$event->sender = $class;}$class = get_class($class);} else {$class = ltrim($class, '\\');}$classes = array_merge([$class],class_parents($class, true),class_implements($class, true));foreach ($classes as $class) {if (!empty(self::$_events[$name][$class])) {foreach (self::$_events[$name][$class] as $handler) {$event->data = $handler[1];call_user_func($handler[0], $event);if ($event->handled) {return;}}}}}

Event::trigger 的 $class 如果是对象时则会被提取类名


使用方法

对象级别

yii\base\Component::on($event_name, $handler_method, $event_data = null,$is_append = true)

//注册 handler是匿名方法
$this->on("my_first_event", function($event) {// 事件名称:my_first_eventecho $event->name;// 触发者:当前对象,我这里拿到它的类名echo $event->sender->className();// 传递的数据echo $event->data;// 如果传递了 stop 的信号if ($event->data == "stop") {// 本事件的队列将会终止 后续注册的事件也不会被触发执行了$event->handled = true;}
}, "data you can get with event->data");// handler 是对象方法
$this->on("my_first_event", [$this, 'eventHandler'], "you can get with event->data");// handler 是类方法
$this->on("my_first_event", ["app\handlers", 'eventHandler'], "you can get with event->data");// append 设为false,直接挂载到事件队列头优先执行,默认 true 是挂载到事件队列尾步的
$this->on("my_first_event", function($event) {echo "though i am the last one, but i can execute firstly!"
}, "this is event data you can get with event->data", false);//触发 默认第二个参数Event的实例为空
$this->trigger("my_first_event");//自己定义一个 event
//要注意这里传递的 event 的 name data 参数是无法改变的,会在trigger 方法里自动配置
//handled 参数也不可能手动设定,handled 是在你的 handler 方法中来决定的
$this->trigger("my_first_event", new Event(["sender" => "default is $this now i change it"]));//传递的 event 必须是 Event 的实例,所以我们也可以通过 Event 的子类来扩展数据
$this->trigger("my_first_event", new MyEvent(["desc" => "sub class of Event and expand property","title" => "i can pass some other data","id" => "33"
]));/** MyEvent
|class MyEvent extends \yii\base\Event
|{
| public $desc;
| public $title;
| public $id;
|}
*/

类级别

yii\base\Event::on($class, $event_name, $handler_method, $event_data = null,$is_append = true)

其实和对象也没太大区别,只是将事件定义和触发提升到类的层次,需要手动指定是哪个类的事件而已

Event::on(__CLASS__, "my_first_event", [__CLASS__, 'classEventHandler'], "event->data");
//要注意这里如果不传递 event 的话,默认的 event->sender 为 null 对象级别的默认的是当前对象 $this
Event::trigger(__CLASS__, "my_first_event");
//给他一个sender
Event::trigger(__CLASS__, "my_first_event", new Event(['sender' => "this is a class"]));public static funtion classEventHandler($event)
{echo $event->name . $event-> sender . $event->data;// if set handled is true the event queue will end and return$event->handled= true;
}

全局应用级别

\Yii::$app 是全局应用的句柄

\Yii::$app->on("can_be_trigger_anywhere", function($event) {echo "全局应用级别事件,可以在应用的任何地方注册或触发"
})
\Yii::$app->trigger("can_be_trigger_anywhere");

可以注册全局事件,可以在全局项目进行注册和触发


小结

 1、on 方法注册事件,可以附加数据,通过 handler 方法的 $event->data 接受,同时可以设定append来绝对事件是否被优先执行

2、trigger 方法传递的 event 默认为空时,如果为类级别的触发则 $event->sender 为 null,如果为对象级别的触发则 $event->sender 为当前对象 $this

3、每个事件都有一个自己的事件队列,而且,我们可以通过 设定 $event->handled 的状态来绝对本事件的处理队列是否继续下去


转:https://my.oschina.net/sallency/blog/682805



推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • 热血合击脚本辅助工具及随机数生成器源码分享
    本文分享了一个热血合击脚本辅助工具及随机数生成器源码。游戏脚本能够实现类似真实玩家的操作,但信息量有限且操作不可控。热血合击脚本辅助工具可以帮助玩家自动刷图、换图拉怪等操作,并提供了雷电云手机的扩展服务。此外,还介绍了使用mt_rand函数作为随机数生成器的代码示例。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
author-avatar
最佳牛牛1
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有