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 相关文章推荐
ip签名探针
Oct 09 PHP
PHP版 汉字转码的实现详解
Jun 09 PHP
php使用curl模拟登录后采集页面的例子
Nov 04 PHP
简单实用的网站PHP缓存类实例
Jul 18 PHP
php获取YouTube视频信息的方法
Feb 11 PHP
php实现的IMEI限制的短信验证码发送类
May 05 PHP
PHP实现简单汉字验证码
Jul 28 PHP
PHP+HTML+JavaScript+Css实现简单爬虫开发
Mar 28 PHP
WordPress过滤垃圾评论的几种主要方法小结
Jul 11 PHP
一个简单安全的PHP验证码类、PHP验证码
Sep 24 PHP
Laravel 中创建 Zip 压缩文件并提供下载的实现方法
Apr 02 PHP
PHP 7.4 新语法之箭头函数实例详解
May 09 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
phpmyadmin导入(import)文件限制的解决办法
2009/12/11 PHP
硬盘浏览程序,保存成网页格式便可使用
2006/12/03 Javascript
javascript 表格排序和表头浮动效果(扩展SortTable)
2009/04/07 Javascript
使用jQuery的将桌面应用程序引入浏览器
2010/11/19 Javascript
jquery实现平滑的二级下拉菜单效果
2015/08/26 Javascript
Node.js 数据加密传输浅析
2016/11/16 Javascript
Bootstrap和Java分页实例第一篇
2016/12/23 Javascript
ECMAscript 变量作用域总结概括
2017/08/18 Javascript
JS计算输出100元钱买100只鸡问题的解决方法
2018/01/04 Javascript
js遍历详解(forEach, map, for, for...in, for...of)
2019/08/28 Javascript
JavaScript页面加载事件实例讲解
2019/09/01 Javascript
JS 数组和对象的深拷贝操作示例
2020/06/06 Javascript
nuxt引入组件和公共样式的操作
2020/11/05 Javascript
vue登录页实现使用cookie记住7天密码功能的方法
2021/02/18 Vue.js
各个系统下的Python解释器相关安装方法
2015/10/12 Python
python开发之IDEL(Python GUI)的使用方法图文详解
2015/11/12 Python
详解Python函数作用域的LEGB顺序
2016/05/14 Python
Flask框架学习笔记之模板操作实例详解
2019/08/15 Python
常用的10个Python实用小技巧
2020/08/10 Python
django数据模型中null和blank的区别说明
2020/09/02 Python
来自美国主售篮球鞋的零售商店:KICKSUSA
2017/11/28 全球购物
Herve Leger官网:标志性绷带连衣裙等
2018/12/26 全球购物
阿迪达斯英国官方网站:adidas英国
2019/08/13 全球购物
迪奥美国官网:Dior美国
2019/12/07 全球购物
在校生钳工实习自我鉴定
2013/09/19 职场文书
大型晚会策划方案
2014/02/06 职场文书
2014年机关植树节活动方案
2014/02/27 职场文书
大学应届生的自我评价
2014/03/06 职场文书
家长学校实施方案
2014/03/15 职场文书
兴趣小组活动总结
2014/05/05 职场文书
应聘教师自荐书
2014/06/16 职场文书
会计人员演讲稿
2014/09/11 职场文书
西游降魔篇观后感
2015/06/15 职场文书
2016圣诞节贺卡寄语
2015/12/07 职场文书
最新的离婚协议书范本!
2019/07/02 职场文书
私人贷款担保书该怎么写呢?
2019/07/02 职场文书