1. 程式人生 > 程式設計 >Yii框架元件的事件機制原理與用法分析

Yii框架元件的事件機制原理與用法分析

本文例項講述了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','{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.','{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}'=>$name)));
}

由此可以看出,首先獲取事件處理器物件,如果沒有則使用 CList ( Yii 實現的一個連結串列)建立,然後將事件處理器 add 進這個物件中,這樣就可以在 raiseEvent 時遍歷所有的事件處理器進行處理了,有點兒類似 jQuery 中註冊了多個 click 事件處理器之後,當 click 事件觸發時,會按順序呼叫之前註冊的事件處理器。

更多關於Yii相關內容感興趣的讀者可檢視本站專題:《Yii框架入門及常用技巧總結》、《php優秀開發框架總結》、《smarty模板入門基礎教程》、《php面向物件程式設計入門教程》、《php字串(string)用法總結》、《php+mysql資料庫操作入門教程》及《php常見資料庫操作技巧彙總》

希望本文所述對大家基於Yii框架的PHP程式設計有所幫助。