详解EventDispatcher事件分发组件


Posted in PHP onDecember 25, 2016

引言

考虑这样一个问题,现在你想给为你的项目提供一个插件系统,插件可以添加一些方法,或者在某些方法执行之前或者之后做些事情,而不干扰其他插件。要实现这个系统,简单的单继承不是个好办法,即使多继承在PHP中是可能的,他也有与生俱来的缺点(多继承不太了解,感觉挺操蛋的)。

Symfony EventDispatcher以一个简单有效的方式实现了中介者模式,事件分发器就是那个中介,让系统和插件不会耦合在一起,这让上面的插件系统成为可能,而且他会让你的项目可扩展性更好。

上面的话,翻译自Symfony官方文档片段

系统剖析

事件存储

详解EventDispatcher事件分发组件

上面这张图是分析Symfony EventDispatcher组件源码得出来的,可以看到事件在系统中是如何存储的

这里面将事件存储了两遍,用来加入优先级priority的概念,存如的时候放入上图中上面的结构中,取出时候从上图中下面的结构中拿出来,相同的事件名称可以有不同的优先级,优先级越高的事件优先触发,优先级相同的时候,先插入的事件优先触发。

排序事件(上图中下面的结构)在插入事件的时候不会构建,而是当取出事件的时候会生成排好序的事件,当相同的事件名中插入新的事件或删除某个事件的时候,会删除对应的排好序的事件名,后面用到的时候重新构建

执行事件的时候,会获取对应事件名排好序的linster列表,按照顺序依次执行。

事件执行

详解EventDispatcher事件分发组件

如上图所示,当触发某个时间的时候,该事件名下面如果监听了多个触发动作,他们会按照优先级、注册顺序依次触发,触发动作一般是一个可执行的“实例”(不管是类还是函数,必须可以通过call_user_func_array调用),可以传入三个参数,第一个参数(必须)是一个Event实例,第二个是触发的事件名,第三个是事件分发器实例。第一个参数会控制事件是否在该事件名下的所有触发动作之间继续传递,比如上面的linstener_2里面将Event.propagationStopped设置为true,执行完linstener_2后,事件就会停止传播,linstener_2后面的动作不会触发。

除此之外,Event实例中还可以保存其他必要的信息,以便linstener触发执行的时候,获取额外的信息。

事件订阅者

详解EventDispatcher事件分发组件

事件订阅者(Event subscriber),告诉dispathcer实例,他要订阅的所有事件,不用一个个通过dispathcer实例去注册。事件订阅者是一个PHP类,他可以告诉dispathcer他要订阅的具体的事件。

好处:

  • 关注的事件不用一个个去注册。
  • 取消关注的事件不用一个个去移除注册。

订阅者内部关注的事件是一个整体,要么全部关注要么全部不关注

实例

普通栗子

include "vendor/autoload.php";
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
class UserEvent extends Event
{
 public function name()
 {
 return "Cartman";
 }

 public function age()
 {
 return "24";
 }
}
$dispatcher = new EventDispatcher();
$dispatcher->addListener("user.name", function($event, $eventName, $dispatcher){
 echo "My name is Cartman\n";
});
$dispatcher->addListener("user.name", function($event, $eventName, $dispatcher){
 echo "My name is {$event->name()} from Event instance\n";
}, 10);
$dispatcher->addListener("user.age", function($event, $eventName, $dispatcher){
 echo "My age is 24\n";
}, 10);
$dispatcher->addListener("user.age", function($event, $eventName, $dispatcher){
 echo "My age is {$event->age()} from Event instance\n";
}, -10);
$dispatcher->dispatch("user.name", new UserEvent());
$dispatcher->dispatch("user.age", new UserEvent());

上面的例子输出

My name is Cartman from Event instance
My name is Cartman
My age is 24
My age is 24 from Event instance

事件订阅者栗子

通过Subscriber注册事件

include "vendor/autoload.php";
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class BookEvent extends Event
{
 public $name = self::class;
}
class BookSubscriber implements EventSubscriberInterface
{
 public static function getSubscribedEvents()
 {
 return [
  "chinese.name" => "chineseNameShow",
  "english.name" => [
  ["englishNameShow", -10],
  ["englishNameAFter", 10],
  ],
  "math.name" => ["mathNameShow", 100]
 ];
 }
 public function chineseNameShow(Event $event)
 {
 echo "我是汉语书籍\n";
 }
 public function englishNameShow(Event $event)
 {
 echo "我是英文书籍\n";
 }
 public function englishNameAFter(Event $event)
 {
 echo "我是展示之后的英文书籍[来自于Event实例{$event->name}]\n";
 }
 public function mathNameShow(Event $event)
 {
 echo "我是展示的数学书籍\n";
 }
}
$dispatcher = new EventDispatcher();
$subscriber = new BookSubscriber();
$dispatcher->addSubscriber($subscriber);
$dispatcher->dispatch("english.name", new BookEvent());
$dispatcher->dispatch("chinese.name");
$dispatcher->removeSubscriber($subscriber);
$dispatcher->dispatch("math.name");

输出为内容:

我是展示之后的英文书籍[来自于Event实例BookEvent]
我是英文书籍
我是汉语书籍

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

PHP 相关文章推荐
PHP curl模拟浏览器采集阿里巴巴的实现代码
Apr 20 PHP
php数组函数序列之array_unique() - 去除数组中重复的元素值
Oct 29 PHP
深入php define()函数以及defined()函数的用法详解
Jun 05 PHP
php读取3389的脚本
May 06 PHP
php switch语句多个值匹配同一代码块应用示例
Jul 29 PHP
linux下编译安装memcached服务
Aug 03 PHP
php5.4以下版本json不支持不转义内容中文的解决方法
Jan 13 PHP
smarty模板引擎使用内建函数foreach循环取出所有数组值的方法
Jan 22 PHP
PHP输入输出流学习笔记
May 12 PHP
PHP微信开发之微信消息自动回复下所遇到的坑
May 09 PHP
PHP 中使用explode()函数切割字符串为数组的示例
May 06 PHP
PHP 实现人民币小写转换成大写的方法及大小写转换函数
Nov 17 PHP
php 反斜杠处理函数addslashes()和stripslashes()实例详解
Dec 25 #PHP
PHP实现的文件操作类及文件下载功能示例
Dec 24 #PHP
PHP文件与目录操作示例
Dec 24 #PHP
PHP数组操作实例分析【添加,删除,计算,反转,排序,查找等】
Dec 24 #PHP
PHP常见字符串处理函数用法示例【转换,转义,截取,比较,查找,反转,切割】
Dec 24 #PHP
PHP会话控制实例分析
Dec 24 #PHP
PHP面向对象程序设计方法实例详解
Dec 24 #PHP
You might like
常用的php ADODB使用方法集锦
2008/03/25 PHP
PHP计算2点经纬度之间的距离代码
2013/08/12 PHP
php像数组一样存取和修改字符串字符
2014/03/21 PHP
PHP中SimpleXML函数用法分析
2014/11/26 PHP
php动态生成缩略图并输出显示的方法
2015/04/20 PHP
基于php实现随机合并数组并排序(原排序)
2015/11/26 PHP
CI框架中数据库操作函数$this->db->where()相关用法总结
2016/05/17 PHP
php判断/计算闰年的方法小结【三种方法】
2019/07/06 PHP
JavaScript 判断判断某个对象是Object还是一个Array
2010/01/28 Javascript
jquery插件之easing 动态菜单
2010/08/21 Javascript
简单谈谈node.js 版本控制 nvm和 n
2015/10/15 Javascript
两款JS脚本判断手机浏览器类型跳转WAP手机网站
2015/10/16 Javascript
JS+CSS实现闪烁字体效果代码
2016/04/05 Javascript
jQuery选择器基础入门教程
2016/05/10 Javascript
ES6所改良的javascript“缺陷”问题
2016/08/23 Javascript
js自定义trim函数实现删除两端空格功能
2018/02/09 Javascript
Vue前后端不同端口的实现方法
2018/09/19 Javascript
vue框架下部署上线后刷新报404问题的解决方案(推荐)
2019/04/03 Javascript
小程序rich-text组件如何改变内部img图片样式的方法
2019/05/22 Javascript
layui 图片上传+表单提交+ Spring MVC的实例
2019/09/21 Javascript
微信小程序实现一张或多张图片上传(云开发)
2019/09/25 Javascript
JavaScript内置对象之Array的使用小结
2020/05/12 Javascript
微信小程序自定义顶部组件customHeader的示例代码
2020/06/03 Javascript
[53:10]Secret vs Pain 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
Python中的高级数据结构详解
2015/03/27 Python
python实现彩票系统
2020/06/28 Python
对PyQt5中树结构的实现方法详解
2019/06/17 Python
Python通过TensorFLow进行线性模型训练原理与实现方法详解
2020/01/15 Python
解决keras GAN训练是loss不发生变化,accuracy一直为0.5的问题
2020/07/02 Python
css3实现顶部社会化分享按钮示例
2014/05/06 HTML / CSS
HTML5 语音搜索(淘宝店语音搜素)
2013/01/03 HTML / CSS
禁毒宣传标语
2014/06/19 职场文书
2016年寒假生活小结
2015/10/10 职场文书
Python办公自动化之教你如何用Python将任意文件转为PDF格式
2021/06/28 Python
React Fragment介绍与使用详解
2021/11/11 Javascript
python 使用pandas读取csv文件的方法
2022/12/24 Python