yii淺析(三)

整個應用是基于下面這句代碼運行起來的:

Yii::createWebApplication($env->configWeb)->run();

是的,就是這個run方法。我們在CWebApplication中找到run方法的實現:

public function run(){   
  //應用跑起來之前     
  if($this->hasEventHandler('onBeginRequest'))      
      $this->onBeginRequest(new CEvent($this));   
  //最后運行的方法   -- 1
  register_shutdown_function(array($this,'end'),0,false);   
  //處理請求
  $this->processRequest();   

  //處理請求結束之后 -- 2(2的運行順序在1之前)
  if($this->hasEventHandler('onEndRequest'))      
      $this->onEndRequest(new CEvent($this));
}

接下來我們應該來說明這句代碼:

$this->hasEventHandler('onBeginRequest')

不過在解釋它之前,我們需要理解一下yii的event機制。

Yii的event機制

YII的事件機制,是其比較獨特之處,合理使用好事件機制,會使各個組件之間的耦合更為松散,利于團體協作開發。

何時需要使用事件,如何給事件綁定事件處理函數,以及如何觸發事件,與其它語言是有較大的差別的。例如Javascript中,可以使用

$(‘#id’).on("click",function() {});

方式給DOM元素綁定處理函數,當DOM元素上發生指定的事件(如click)時,將自動執行設定的函數。

但是PHP是服務器端的腳本語言,就不存在自動觸發事件之說,所以和Javascript對比,YII中的事件是需要手動觸發的。一般來說,要實現YII組件的事件機制,需要以下幾步:

  1. 定義事件名稱,其實就是級組件定義一個on開頭的方法,其中的代碼是固定的,如:
  public function onBeginRequest($event){
  $this->raiseEvent('onBeginRequest',$event);
}

即函數名與事件名是一致的。此步的作用就是將綁定在此事件上的處理函數逐個執行。寫這一系列的播客,算是一個整理,所以我寫細一點,現在把raiseEvent方法的代碼貼出來。

/** * Raises an event. 
    * This method represents the happening of an event. It invokes 
    * all attached handlers for the event. 
    * @param string $name the event name 
    * @param CEvent $event the event parameter 
    * @throws CException if the event is undefined or an event handler is invalid. 
*/
    
    public function raiseEvent($name,$event){   
              $name=strtolower($name);   
              //_e這個數組用來存所有事件信息
              if(isset($this->_e[$name]))   {      
                    foreach($this->_e[$name] as $handler)  {         
                        if(is_string($handler)) 
                           call_user_func($handler,$event);             
                        elseif(is_callable($handler,true)){  
                                  if(is_array($handler)){               
                                      // an array: 0 - object, 1 - method name    
                                     list($object,$method)=$handler;     
                                     if(is_string($object)) // static method call  
                                        call_user_func($handler,$event);  
                                     elseif(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 // PHP 5.3: anonymous function  
                                         call_user_func($handler,$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}'=>gettype($handler))));         
                      // stop further handling if param.handled is set true  
                        if(($event instanceof CEvent) && $event->handled)  
                            return;     
                   }  
               }   elseif(YII_DEBUG && !$this->hasEvent($name))      
                    throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',         array('{class}'=>get_class($this), '{event}'=>$name)));
  }

2 . 給組件對象綁定事件處理函數

$component->attachEventHandler($name, $handler);
$component->onBeginRequest = $handler ;

yii支持一個事件綁定多個回調函數,上述的兩個方法都會在已有的事件上增加新的回調函數,而不會覆蓋已有回調函數。

$handler即是一個PHP回調函數,關于回調函數的形式,本文的最后會附帶說明。
如CLogRouter組件的init事件中,有以下代碼:

Yii::app()->attachEventHandler('onEndRequest',array($this,'processLogs'));

這就是給CApplication對象的onEndRequest綁定了CLogRouter::processLogs()回調函數。而CApplication組件確實存在名為onEndRequest的方法(即onEndRequest事件),它之中的代碼就是激活了相應的回調函數,即CLogRouter::processLogs()方法。所以從這里可以得出,日志的記錄其實是發生在CApplication組件的正常退出時。

  1. 在需要觸發事件的時候,直接激活組件的事件,即調用事件即可,如:
    比如CApplication組件的run方法中:
if($this->hasEventHandler('onBeginRequest'))
    $this->onBeginRequest(new CEvent($this));

這樣即觸發了事件處理函數。如果沒有第一行的判斷,那么在調試模式下(YII_DEBUG常量被定義為true),會拋出異常,而在非調試模式下(YII_DEBUG常量定義為false或沒有定義YII_DEBUG常量),則不會產生任何異常。

回調函數的形式:

  1. 普通全局函數(內置的或用戶自定義的)
call_user_func(‘print’, $str);
  1. 類的靜態方法,使用數組形式傳遞
call_user_func(array(‘className’, ‘print’),  $str );
  1. 對象方法,使用數組形式傳遞
$obj = new className();
call_user_func(array($obj, ‘print’),  $str );
  1. 匿名方法,類似javascript的匿名函數
call_user_func(function($i){echo $i++;},4);

或使用以下形式:

$s = function($i) {
    echo $i++;
};
call_user_func($s,4);

總結: 關于Yii的事件機制其實就是提供了一種用于解耦的方式,在需要調用event的地方之前,只要你提供了事件的實現并注冊在之后的地方需要的時候即可調用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,349評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,948評論 18 139
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,766評論 18 399
  • 朋友問我暗戀是什么感覺,下意識的回答:好像在商店看到喜歡的玩具,想買,錢不夠,努力存錢,回頭去看的時候發現漲價了,...
    WeiiiiiA閱讀 102評論 0 0
  • 18號早上5點鐘起床,天津去北京西路上,遇到北京地鐵的早高峰,真的是擠之又擠,只想說北京的人真的太多了。北京西到福...
    Lqruc閱讀 187評論 2 0