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 相关文章推荐
浅析Mysql 数据回滚错误的解决方法
Aug 05 PHP
php实现给图片加灰色半透明效果的方法
Oct 20 PHP
php自定义类fsocket模拟post或get请求的方法
Jul 31 PHP
Yii中CGridView实现批量删除的方法
Dec 28 PHP
php阳历转农历优化版
Aug 08 PHP
PHP判断用户是否已经登录(跳转到不同页面或者执行不同动作)
Sep 22 PHP
php性能分析之php-fpm慢执行日志slow log用法浅析
Oct 17 PHP
PHP合并数组函数array_merge用法分析
Feb 17 PHP
PHP查询分页的实现代码
Jun 09 PHP
Laravel框架中队列和工作(Queues、Jobs)操作实例详解
Apr 06 PHP
php使用event扩展的io复用测试的示例
Oct 20 PHP
一文搞懂php的垃圾回收机制
Jun 18 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
php 文件上传系统手记
2009/10/26 PHP
浏览器预览PHP文件时顶部出现空白影响布局分析原因及解决办法
2013/01/11 PHP
基于thinkPHP实现的微信自定义分享功能示例
2016/09/23 PHP
extjs fckeditor集成代码
2009/05/10 Javascript
过虑特殊字符输入的js代码
2010/08/05 Javascript
js实现点击注册按钮开始读秒倒计时的小例子
2013/05/11 Javascript
在父页面调用子页面的JS方法
2013/09/29 Javascript
jquery中交替点击事件toggle方法的使用示例
2013/12/08 Javascript
Node.js实现简单聊天服务器
2014/06/20 Javascript
动态载入js提高网页打开速度的方法
2014/07/04 Javascript
js实现将json数组显示前台table中
2017/01/10 Javascript
Vue引用第三方datepicker插件无法监听datepicker输入框的值的解决
2018/01/27 Javascript
解决iView中时间控件选择的时间总是少一天的问题
2018/03/15 Javascript
结合Vue控制字符和字节的显示个数的示例
2018/05/17 Javascript
vue-cli+axios实现文件上传下载功能(下载接收后台返回文件流)
2019/05/10 Javascript
angular使用md5,CryptoJS des加密的方法
2019/06/03 Javascript
js实现适配移动端的拖动效果
2020/01/13 Javascript
webpack中的模式(mode)使用详解
2020/02/20 Javascript
JQuery实现折叠式菜单的详细代码
2020/06/03 jQuery
用webAPI实现图片放大镜效果
2020/11/23 Javascript
Python  __getattr__与__setattr__使用方法
2008/09/06 Python
python 随机数生成的代码的详细分析
2011/05/15 Python
Python2随机数列生成器简单实例
2017/09/04 Python
Python处理文本换行符实例代码
2018/02/03 Python
pycharm: 恢复(reset) 误删文件的方法
2018/10/22 Python
Python中的元组介绍
2019/01/28 Python
Python Pandas 箱线图的实现
2019/07/23 Python
win10系统下python3安装及pip换源和使用教程
2020/01/06 Python
tensorflow之自定义神经网络层实例
2020/02/07 Python
Python如何爬取b站热门视频并导入Excel
2020/08/10 Python
关于css中margin的值和垂直外边距重叠问题
2020/10/27 HTML / CSS
Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
2015/01/27 面试题
勤俭节约倡议书
2014/04/14 职场文书
幼师求职自荐信
2015/03/26 职场文书
公司市场部岗位职责
2015/04/15 职场文书
导游词之镇江-金山寺
2019/10/14 职场文书