Yii框架组件的事件机制原理与用法分析


Posted in PHP onApril 07, 2020

本文实例讲述了Yii框架组件的事件机制原理与用法。分享给大家供大家参考,具体如下:

在深入分析 Yii 的运行之前,我们先来看一下 Yii 框架中一个很重要的机制 - 事件。

Yii 官方参考文档关于组件事件的解释:

=======================================================================

组件事件是一些特殊的属性,它们使用一些称作 事件句柄 ( event handlers )的方法作为其值。 附加 ( 分配 ) 一个方法到一个事件将会引起方法在事件被唤起处自动被调用。因此, 一个组件的行为可能会被一种在部件开发过程中不可预见的方式修改。

组件事件以 on 开头的命名方式定义。和属性通过 getter/setter 方法来定义的命名方式一样, 事件的名称是大小写不敏感的。以下代码定义了一个 onClicked 事件 :

public function onClicked($event)
{
  $this->raiseEvent('onClicked', $event);
}

这里作为事件参数的 $event 是 CEvent 或其子类的实例。

我们可以附加一个方法到此 event ,如下所示 :

$component->onClicked=$callback;

这里的 $callback 指向了一个有效的 PHP 回调。它可以是一个全局函数也可以是类中的一个方法。 如果是后者,它必须以一个数组的方式提供 : array($object,'methodName').

事件句柄的结构如下:

function methodName($event)
{
  ......
}

这里的 $event 即描述事件的参数(它来源于 raiseEvent() 调用)。 $event 参数是 CEvent 或其子类的实例。 至少,它包含了关于谁触发了此事件的信息。

从版本 1.0.10 开始,事件句柄也可以是一个 PHP 5.3 以后支持的匿名函数。例如,

$component->onClicked=function($event) {
  ......
}

如果我们现在调用 onClicked() , onClicked 事件将被触发(在 onClicked() 中), 附属的事件句柄将被自动调用。

一个事件可以绑定多个句柄。当事件触发时, 这些句柄将被按照它们绑定到事件时的顺序依次执行。如果句柄决定组织后续句柄被执行,它可以设置 $event->handled 为 true 。

=======================================================================

从这一句开始”我们可以附加一个方法到此 event “,读者可能 就不知道是什么意思了,于是看一下 CComponent 的源码:

/**
   * Raises an event.
   * This method represents the happening of an event. It invokes
   * all attached handlers for the event.
   * @param string the event name
   * @param CEvent the event parameter
   * @throws CException if the event is undefined or an event handler is invalid.
   */
  public function raiseEvent($name,$event)
{
  //事件名称同一小写化处理
    $name=strtolower($name);
    //先查看成员变量是否有以此命名的事件
    if(isset($this->_e[$name]))
    {
      //如果有,这个成员保存的是每一个事件处理器
      //以数组的方式保存
      foreach($this->_e[$name] as $handler)
      {
        //如果事件处理器是一个字符串,那么就是一个全局函数
        if(is_string($handler))
          call_user_func($handler,$event);
        //如果不是,那么有可能是一个数组,该数组包含一个对象和方法名
        //参考http://php.net/manual/en/function.is-callable.php
        else if(is_callable($handler,true))
        {
          // an array: 0 - object, 1 - method name
          list($object,$method)=$handler;
          //如果对象是一个对象名
          if(is_string($object)) // static method call
            call_user_func($handler,$event);
          //判断对象是否有要调用的方法
          else if(method_exists($object,$method))
            $object->$method($event);
          else
            throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler
"{handler}".',
              array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1])));
        }
        else
          throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler
"{handler}".',
            array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>gettype($handler))));
        // stop further handling if param.handled is set true
        //如果想停止继续循环获取事件的handler
//那么需要设置event的handled为true
        if(($event instanceof CEvent) && $event->handled)
          return;
      }
    }
    else if(YII_DEBUG && !$this->hasEvent($name))
      throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',
        array('{class}'=>get_class($this), '{event}'=>$name)));
    //如果_e中没有这个成员也没关系
  }

我们再看一下 CEvent 的代码( CComponent.php ):

class CEvent extends CComponent
{
  /**
   * @var object the sender of this event
   */
  public $sender;
  /**
   * @var boolean whether the event is handled. Defaults to false.
   * When a handler sets this true, the rest uninvoked handlers will not be invoked anymore.
   */
  public $handled=false;

  /**
   * Constructor.
   * @param mixed sender of the event
   */
  public function __construct($sender=null)
  {
    $this->sender=$sender;
  }
}

CEvent 只包含两个变量 $sender 记录事件触发者, $handled 表示事件是否已经被“解决”。

接着我们再看一下如何给一个组件注册一个事件处理器:

/**
   * Attaches an event handler to an event.
   *
   * An event handler must be a valid PHP callback, i.e., a string referring to
   * a global function name, or an array containing two elements with
   * the first element being an object and the second element a method name
   * of the object.
   *
   * An event handler must be defined with the following signature,
   * <pre>
   * function handlerName($event) {}
   * </pre>
   * where $event includes parameters associated with the event.
   *
   * This is a convenient method of attaching a handler to an event.
   * It is equivalent to the following code:
   * <pre>
   * $component->getEventHandlers($eventName)->add($eventHandler);
   * </pre>
   *
   * Using {@link getEventHandlers}, one can also specify the excution order
   * of multiple handlers attaching to the same event. For example:
   * <pre>
   * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler);
   * </pre>
   * makes the handler to be invoked first.
   *
   * @param string the event name
   * @param callback the event handler
   * @throws CException if the event is not defined
   * @see detachEventHandler
   */
  public function attachEventHandler($name,$handler)
  {
    $this->getEventHandlers($name)->add($handler);
  }
  /**
   * Returns the list of attached event handlers for an event.
   * @param string the event name
   * @return CList list of attached event handlers for the event
   * @throws CException if the event is not defined
   */
  public function getEventHandlers($name)
  {
    if($this->hasEvent($name))
    {
      $name=strtolower($name);
      if(!isset($this->_e[$name]))
        //新建一个CList保存事件的处理器
        $this->_e[$name]=new CList;
      return $this->_e[$name];
    }
    else
      throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',
        array('{class}'=>get_class($this), '{event}'=>$name)));
}

由此可以看出,首先获取事件处理器对象,如果没有则使用 CList ( Yii 实现的一个链表)创建,然后将事件处理器 add 进这个对象中,这样就可以在 raiseEvent 时遍历所有的事件处理器进行处理了,有点儿类似 jQuery 中注册了多个 click 事件处理器之后,当 click 事件触发时,会按顺序调用之前注册的事件处理器。

希望本文所述对大家基于Yii框架的PHP程序设计有所帮助。

PHP 相关文章推荐
聊天室php&amp;mysql(三)
Oct 09 PHP
PHP array_flip() 删除重复数组元素专用函数
May 16 PHP
PHP递归返回值时出现的问题解决办法
Feb 19 PHP
php自定义的格式化时间示例代码
Dec 05 PHP
PHP获取数组长度或某个值出现次数的方法
Feb 11 PHP
PHP中SESSION的注销与清除
Apr 16 PHP
php实现爬取和分析知乎用户数据
Jan 26 PHP
PHP页面跳转操作实例分析(header方法)
Sep 28 PHP
简单谈谈PHP中的Reload操作
Dec 12 PHP
Yii2实现UploadedFile上传文件示例
Feb 15 PHP
PHP命令空间namespace及use的用法小结
Nov 27 PHP
PHP生成短网址的思路以及实现方法的详解
Mar 25 PHP
Yii框架多语言站点配置方法分析【中文/英文切换站点】
Apr 07 #PHP
php设计模式之适配器模式实例分析【星际争霸游戏案例】
Apr 07 #PHP
php设计模式之迭代器模式实例分析【星际争霸游戏案例】
Apr 07 #PHP
解决Laravel5.x的php artisan migrate数据库迁移创建操作报错SQLSTATE[42000]
Apr 06 #PHP
4种Windows系统下Laravel框架的开发环境安装及部署方法详解
Apr 06 #PHP
Laravel5.5+ 使用API Resources快速输出自定义JSON方法详解
Apr 06 #PHP
Laravel 5+ .env环境配置文件详解
Apr 06 #PHP
You might like
cache_lite试用
2007/02/14 PHP
Yii控制器中filter过滤器用法分析
2016/07/15 PHP
示例详解Laravel的注册重构
2016/08/14 PHP
PHP7匿名类的用法示例
2019/04/05 PHP
设定php简写功能的方法
2019/11/28 PHP
本地图片预览(支持IE6/IE7/IE8/Firefox3)经验总结
2013/03/25 Javascript
js/html光标定位的实现代码
2013/09/23 Javascript
使用jQuery时Form表单元素ID和name命名大忌
2014/03/06 Javascript
js调试系列 断点与动态调试[基础篇]
2014/06/18 Javascript
JAVA四种基本排序方法实例总结
2015/07/24 Javascript
js实现将选中值累加到文本框的方法
2015/08/12 Javascript
不得不分享的JavaScript常用方法函数集(下)
2015/12/25 Javascript
总结Javascript中数组各种去重的方法
2016/10/04 Javascript
谈谈因Vue.js引发关于getter和setter的思考
2016/12/02 Javascript
简单实现js进度条加载效果
2020/03/25 Javascript
jQuery获取复选框选中的当前行的某个字段的值
2017/09/15 jQuery
jQuery结合jQuery.cookie.js插件实现换肤功能示例
2017/10/14 jQuery
Vue组件之自定义事件的功能图解
2018/02/01 Javascript
JS实现的base64加密解密操作示例
2018/04/18 Javascript
JS实现的倒计时恢复按钮点击功能【可用于协议阅读倒计时】
2018/04/19 Javascript
微信小程序云开发 搭建一个管理小程序
2019/05/17 Javascript
微信小程序rich-text富文本用法实例分析
2019/05/20 Javascript
vue实现购物车选择功能
2020/01/10 Javascript
简单的抓取淘宝图片的Python爬虫
2014/12/25 Python
Python OpenCV处理图像之图像直方图和反向投影
2018/07/10 Python
python爬取基于m3u8协议的ts文件并合并
2019/04/26 Python
解决python 执行shell命令无法获取返回值的问题
2020/12/05 Python
python 三种方法提取pdf中的图片
2021/02/07 Python
HTML5组件Canvas实现图像灰度化(步骤+实例效果)
2013/04/22 HTML / CSS
美国波道夫·古德曼百货官网:Bergdorf Goodman
2017/11/07 全球购物
Groupon比利时官方网站:特卖和网上购物高达-70%
2019/08/09 全球购物
厂区绿化方案
2014/05/08 职场文书
师德标兵先进事迹材料
2014/12/19 职场文书
goland 设置project gopath的操作
2021/05/06 Golang
MySQL的全局锁和表级锁的具体使用
2021/08/23 MySQL
js前端图片加载异常兜底方案
2022/06/21 Javascript