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 相关文章推荐
图象函数中的中文显示
Oct 09 PHP
php数组函数序列之array_values() 获取数组元素值的函数与方法
Oct 30 PHP
分享一个php 的异常处理程序
Jun 22 PHP
php验证码实现代码(3种)
Sep 07 PHP
在PHP中使用FastCGI解析漏洞及修复方案
Nov 10 PHP
PHP类的声明与实例化及构造方法与析构方法详解
Jan 26 PHP
thinkPHP商城公告功能开发问题分析
Dec 01 PHP
详解thinkphp实现excel数据的导入导出(附完整案例)
Dec 29 PHP
关于php几种字符串连接的效率比较(详解)
Feb 22 PHP
使用php自动备份数据库表的实现方法
Jul 28 PHP
PHP7.3.10编译安装教程
Oct 08 PHP
yii 框架实现按天,月,年,自定义时间段统计数据的方法分析
Apr 04 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/06/14 PHP
PHP合并数组+与array_merge的区别分析
2010/08/01 PHP
PHP捕获Fatal error错误的方法
2014/06/11 PHP
PHP实现获取图片颜色值的方法
2014/07/11 PHP
PHP list() 将数组中的值赋给变量的简单实例
2016/06/13 PHP
PHP经典实用正则表达式小结
2017/05/04 PHP
JQuery事件e参数的方法preventDefault()取消默认行为
2013/09/26 Javascript
JS关闭窗口或JS关闭页面的几种代码分享
2013/10/25 Javascript
javascript与cookie 的问题详解
2013/11/11 Javascript
node.js实现爬虫教程
2020/08/25 Javascript
使用jQuery制作基础的Web图片轮播效果
2016/04/22 Javascript
jQuery简单实现tab选项卡切换效果
2016/06/20 Javascript
js创建数组的简单方法
2016/07/27 Javascript
使用jquery给新生的th绑定hover事件的实例
2017/02/10 Javascript
详解vue中引入stylus及报错解决方法
2017/09/22 Javascript
node.js多个异步过程中判断执行是否完成的解决方案
2017/12/10 Javascript
js与jQuery实现获取table中的数据并拼成json字符串操作示例
2018/07/12 jQuery
vue-cli项目修改文件热重载失效的解决方法
2018/09/19 Javascript
详解IOS微信上Vue单页面应用JSSDK签名失败解决方案
2018/11/14 Javascript
vue 组件开发原理与实现方法详解
2019/11/29 Javascript
基于Vue CSR的微前端实现方案实践
2020/05/27 Javascript
[03:40]DOTA2亚洲邀请赛小组赛第二日 赛事回顾
2015/01/31 DOTA
[02:20]DOTA2亚洲邀请赛 IG战队出场宣传片
2015/02/07 DOTA
[01:01:52]完美世界DOTA2联赛PWL S2 GXR vs Magma 第二场 11.25
2020/11/26 DOTA
Python设置默认编码为utf8的方法
2016/07/01 Python
Python3 实现串口两进程同时读写
2019/06/12 Python
python实现数字炸弹游戏
2020/07/17 Python
浅析HTML5页面元素及属性
2021/01/20 HTML / CSS
俄罗斯便宜的在线服装商店:GroupPrice
2020/04/10 全球购物
护理自我鉴定范文
2013/10/06 职场文书
医院信息公开实施方案
2014/05/09 职场文书
爱护公物演讲稿
2014/09/09 职场文书
推普标语口号大全
2015/12/26 职场文书
致男子1500米运动员的广播稿
2019/11/08 职场文书
PHP连接MSSQL数据库案例,PHPWAMP多个PHP版本连接SQL Server数据库
2021/04/16 PHP
Mongo服务重启异常问题的处理方法
2021/07/01 MongoDB