当前位置:首页 > CMS教程 > 其它CMS > 列表

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

发布:smiling 来源: PHP粉丝网  添加日期:2022-02-26 08:27:10 浏览: 评论:0 

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

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

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

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

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

  1. public function onClicked($event
  2.   $this->raiseEvent('onClicked'$event); 

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

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

$component->onClicked=$callback;

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

事件句柄的结构如下:

  1. function methodName($event
  2.   ...... 

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

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

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

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

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

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

  1. /** 
  2.    * Raises an event. 
  3.    * This method represents the happening of an event. It invokes 
  4.    * all attached handlers for the event. 
  5.    * @param string the event name 
  6.    * @param CEvent the event parameter 
  7.    * @throws CException if the event is undefined or an event handler is invalid. 
  8.    */ 
  9.   public function raiseEvent($name,$event
  10.   //事件名称同一小写化处理 
  11.     $name=strtolower($name); 
  12.     //先查看成员变量是否有以此命名的事件 
  13.     if(isset($this->_e[$name])) 
  14.     { 
  15.       //如果有,这个成员保存的是每一个事件处理器 
  16.       //以数组的方式保存 
  17.       foreach($this->_e[$nameas $handler
  18.       { 
  19.         //如果事件处理器是一个字符串,那么就是一个全局函数 
  20.         if(is_string($handler)) 
  21.           call_user_func($handler,$event); 
  22.         //如果不是,那么有可能是一个数组,该数组包含一个对象和方法名 
  23.         //参考http://php.net/manual/en/function.is-callable.php 
  24.         else if(is_callable($handler,true)) 
  25.         { 
  26.           // an array: 0 - object, 1 - method name 
  27.           list($object,$method)=$handler
  28.           //如果对象是一个对象名 
  29.           if(is_string($object)) // static method call 
  30.             call_user_func($handler,$event); 
  31.           //判断对象是否有要调用的方法 
  32.           else if(method_exists($object,$method)) 
  33.             $object->$method($event); 
  34.           else 
  35.             throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler 
  36. "{handler}".', 
  37.               array('{class}'=>get_class($this), '{event}'=>$name'{handler}'=>$handler[1]))); 
  38.         } 
  39.         else 
  40.           throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler 
  41. "{handler}".', 
  42.             array('{class}'=>get_class($this), '{event}'=>$name'{handler}'=>gettype($handler)))); 
  43.         // stop further handling if param.handled is set true 
  44.         //如果想停止继续循环获取事件的handler 
  45. //那么需要设置event的handled为true 
  46.         if(($event instanceof CEvent) && $event->handled) 
  47.           return
  48.       } 
  49.     } 
  50.     else if(YII_DEBUG && !$this->hasEvent($name)) 
  51.       throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.'
  52.         array('{class}'=>get_class($this), '{event}'=>$name))); 
  53.     //如果_e中没有这个成员也没关系 
  54.   } 

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

  1. class CEvent extends CComponent 
  2.   /** 
  3.    * @var object the sender of this event 
  4.    */ 
  5.   public $sender
  6.   /** 
  7.    * @var boolean whether the event is handled. Defaults to false. 
  8.    * When a handler sets this true, the rest uninvoked handlers will not be invoked anymore. 
  9.    */ 
  10.   public $handled=false; 
  11.  
  12.   /** 
  13.    * Constructor. 
  14.    * @param mixed sender of the event 
  15.    */ 
  16.   public function __construct($sender=null) 
  17.   { 
  18.     $this->sender=$sender
  19.   } 

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

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

  1. /** 
  2.    * Attaches an event handler to an event. 
  3.    * 
  4.    * An event handler must be a valid PHP callback, i.e., a string referring to 
  5.    * a global function name, or an array containing two elements with 
  6.    * the first element being an object and the second element a method name 
  7.    * of the object. 
  8.    * 
  9.    * An event handler must be defined with the following signature, 
  10.    * <pre> 
  11.    * function handlerName($event) {} 
  12.    * </pre> 
  13.    * where $event includes parameters associated with the event. 
  14.    * 
  15.    * This is a convenient method of attaching a handler to an event. 
  16.    * It is equivalent to the following code: 
  17.    * <pre> 
  18.    * $component->getEventHandlers($eventName)->add($eventHandler); 
  19.    * </pre> 
  20.    * 
  21.    * Using {@link getEventHandlers}, one can also specify the excution order 
  22.    * of multiple handlers attaching to the same event. For example: 
  23.    * <pre> 
  24.    * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler); 
  25.    * </pre> 
  26.    * makes the handler to be invoked first. 
  27.    * 
  28.    * @param string the event name 
  29.    * @param callback the event handler 
  30.    * @throws CException if the event is not defined 
  31.    * @see detachEventHandler 
  32.    */ 
  33.   public function attachEventHandler($name,$handler
  34.   { 
  35.     $this->getEventHandlers($name)->add($handler); 
  36.   } 
  37.   /** 
  38.    * Returns the list of attached event handlers for an event. 
  39.    * @param string the event name 
  40.    * @return CList list of attached event handlers for the event 
  41.    * @throws CException if the event is not defined 
  42.    */ 
  43.   public function getEventHandlers($name
  44.   { 
  45.     if($this->hasEvent($name)) 
  46.     { 
  47.       $name=strtolower($name); 
  48.       if(!isset($this->_e[$name])) 
  49.         //新建一个CList保存事件的处理器 
  50.         $this->_e[$name]=new CList; 
  51.       return $this->_e[$name]; 
  52.     } 
  53.     else 
  54.       throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.'
  55.         array('{class}'=>get_class($this), '{event}'=>$name))); 

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

Tags: Yii框架组件 Yii事件机制

分享到: