亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > PHP > 正文

深入解析PHP的Yii框架中的event事件機制

2024-05-04 23:44:04
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了PHP的Yii框架中的event事件機制,文中講解了Yii的事件處理器以及給組件對象綁定事件處理函數等重要知識,需要的朋友可以參考下
 

事件
事件可以將自定義代碼“注入”到現有代碼中的特定執行點。附加自定義代碼到某個事件,當這個事件被觸發時,這些代碼就會自動執行。例如,郵件程序對象成功發出消息時可觸發 messageSent 事件。如想追蹤成功發送的消息,可以附加相應追蹤代碼到messageSent 事件。
Yii 引入了名為 yii/base/Component 的基類以支持事件。如果一個類需要觸發事件就應該繼承 yii/base/Component 或其子類。

Yii的event機制
YII的事件機制,是其比較獨特之處,合理使用好事件機制,會使各個組件之間的耦合更為松散,利于團體協作開發。
何時需要使用事件,如何給事件綁定事件處理函數,以及如何觸發事件,與其它語言是有較大的差別的。例如Javascript中,可以使用

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

方式給DOM元素綁定處理函數,當DOM元素上發生指定的事件(如click)時,將自動執行設定的函數。 
但是PHP是服務器端的腳本語言,就不存在自動觸發事件之說,所以和Javascript對比,YII中的事件是需要手動觸發的。一般來說,要實現YII組件的事件機制,需要以下幾步:

定義事件名稱,其實就是級組件定義一個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))); }

事件處理器(Event Handlers)

事件處理器是一個PHP 回調函數,當它所附加到的事件被觸發時它就會執行??梢允褂靡韵禄卣{函數之一:

  • 字符串形式指定的 PHP 全局函數,如 'trim' ;
  • 對象名和方法名數組形式指定的對象方法,如 [$object, $method] ;
  • 類名和方法名數組形式指定的靜態類方法,如 [$class, $method] ;
  • 匿名函數,如 function ($event) { ... } 。

事件處理器的格式是:

function ($event) {  // $event 是 yii/base/Event 或其子類的對象}

通過 $event 參數,事件處理器就獲得了以下有關事件的信息:

  • yii/base/Event::name:事件名
  • yii/base/Event::sender:調用 trigger() 方法的對象
  • yii/base/Event::data:附加事件處理器時傳入的數據,默認為空,后文詳述

附加事件處理器

調用 yii/base/Component::on() 方法來附加處理器到事件上。如:

$foo = new Foo;// 處理器是全局函數$foo->on(Foo::EVENT_HELLO, 'function_name');// 處理器是對象方法$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);// 處理器是靜態類方法$foo->on(Foo::EVENT_HELLO, ['app/components/Bar', 'methodName']);// 處理器是匿名函數$foo->on(Foo::EVENT_HELLO, function ($event) {  //事件處理邏輯});附加事件處理器時可以提供額外數據作為 yii/base/Component::on() 方法的第三個參數。數據在事件被觸發和處理器被調用時能被處理器使用。如:// 當事件被觸發時以下代碼顯示 "abc"// 因為 $event->data 包括被傳遞到 "on" 方法的數據$foo->on(Foo::EVENT_HELLO, function ($event) {  echo $event->data;}, 'abc');

事件處理器順序

可以附加一個或多個處理器到一個事件。當事件被觸發,已附加的處理器將按附加次序依次調用。如果某個處理器需要停止其后的處理器調用,可以設置 $event 參數的 [yii/base/Event::handled]] 屬性為真,如下:

$foo->on(Foo::EVENT_HELLO, function ($event) {  $event->handled = true;});

默認新附加的事件處理器排在已存在處理器隊列的最后。因此,這個處理器將在事件被觸發時最后一個調用。在處理器隊列最前面插入新處理器將使該處理器最先調用,可以傳遞第四個參數 $append 為假并調用 yii/base/Component::on() 方法實現:

$foo->on(Foo::EVENT_HELLO, function ($event) {  // 這個處理器將被插入到處理器隊列的第一位...}, $data, false);


觸發事件

事件通過調用 yii/base/Component::trigger() 方法觸發,此方法須傳遞事件名,還可以傳遞一個事件對象,用來傳遞參數到事件處理器。如:

namespace app/components;use yii/base/Component;use yii/base/Event;class Foo extends Component{  const EVENT_HELLO = 'hello';  public function bar()  {    $this->trigger(self::EVENT_HELLO);  }}

以上代碼當調用 bar() ,它將觸發名為 hello 的事件。

提示:推薦使用類常量來表示事件名。上例中,常量 EVENT_HELLO 用來表示 hello 。這有兩個好處。第一,它可以防止拼寫錯誤并支持 IDE 的自動完成。第二,只要簡單檢查常量聲明就能了解一個類支持哪些事件。
有時想要在觸發事件時同時傳遞一些額外信息到事件處理器。例如,郵件程序要傳遞消息信息到 messageSent 事件的處理器以便處理器了解哪些消息被發送了。為此,可以提供一個事件對象作為 yii/base/Component::trigger() 方法的第二個參數。這個事件對象必須是 yii/base/Event 類或其子類的實例。如:

namespace app/components;use yii/base/Component;use yii/base/Event;class MessageEvent extends Event{  public $message;}class Mailer extends Component{  const EVENT_MESSAGE_SENT = 'messageSent';  public function send($message)  {    // ...發送 $message 的邏輯...    $event = new MessageEvent;    $event->message = $message;    $this->trigger(self::EVENT_MESSAGE_SENT, $event);  }}

當 yii/base/Component::trigger() 方法被調用時,它將調用所有附加到命名事件(trigger 方法第一個參數)的事件處理器。

移除事件處理器

從事件移除處理器,調用 yii/base/Component::off() 方法。如:

// 處理器是全局函數$foo->off(Foo::EVENT_HELLO, 'function_name');// 處理器是對象方法$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);// 處理器是靜態類方法$foo->off(Foo::EVENT_HELLO, ['app/components/Bar', 'methodName']);// 處理器是匿名函數$foo->off(Foo::EVENT_HELLO, $anonymousFunction);

注意當匿名函數附加到事件后一般不要嘗試移除匿名函數,除非你在某處存儲了它。以上示例中,假設匿名函數存儲為變量$anonymousFunction 。

移除事件的全部處理器,簡單調用 yii/base/Component::off() 即可,不需要第二個參數:

$foo->off(Foo::EVENT_HELLO);

類級別的事件處理器

以上部分,我們敘述了在實例級別如何附加處理器到事件。有時想要一個類的所有實例而不是一個指定的實例都響應一個被觸發的事件,并不是一個個附加事件處理器到每個實例,而是通過調用靜態方法 yii/base/Event::on() 在類級別附加處理器。

例如,活動記錄對象要在每次往數據庫新增一條新記錄時觸發一個 yii/db/BaseActiveRecord::EVENT_AFTER_INSERT 事件。要追蹤每個活動記錄對象的新增記錄完成情況,應如下寫代碼:

use Yii;use yii/base/Event;use yii/db/ActiveRecord;Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {  Yii::trace(get_class($event->sender) . ' is inserted');});

每當 yii/db/BaseActiveRecord 或其子類的實例觸發 yii/db/BaseActiveRecord::EVENT_AFTER_INSERT 事件時,這個事件處理器都會執行。在這個處理器中,可以通過 $event->sender 獲取觸發事件的對象。

當對象觸發事件時,它首先調用實例級別的處理器,然后才會調用類級別處理器。

可調用靜態方法yii/base/Event::trigger()來觸發一個類級別事件。類級別事件不與特定對象相關聯。因此,它只會引起類級別事件處理器的調用。如:

use yii/base/Event;Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) {  echo $event->sender; // 顯示 "app/models/Foo"});Event::trigger(Foo::className(), Foo::EVENT_HELLO);

注意這種情況下 $event->sender 指向觸發事件的類名而不是對象實例。

注意:因為類級別的處理器響應類和其子類的所有實例觸發的事件,必須謹慎使用,尤其是底層的基類,如 yii/base/Object。
移除類級別的事件處理器只需調用yii/base/Event::off(),如:

// 移除 $handlerEvent::off(Foo::className(), Foo::EVENT_HELLO, $handler);// 移除 Foo::EVENT_HELLO 事件的全部處理器Event::off(Foo::className(), Foo::EVENT_HELLO);

全局事件

所謂全局事件實際上是一個基于以上敘述的事件機制的戲法。它需要一個全局可訪問的單例,如應用實例。

事件觸發者不調用其自身的 trigger() 方法,而是調用單例的 trigger() 方法來觸發全局事件。類似地,事件處理器被附加到單例的事件。如:

use Yii;use yii/base/Event;use app/components/Foo;Yii::$app->on('bar', function ($event) {  echo get_class($event->sender); // 顯示 "app/components/Foo"});Yii::$app->trigger('bar', new Event(['sender' => new Foo]));

全局事件的一個好處是當附加處理器到一個對象要觸發的事件時,不需要產生該對象。相反,處理器附加和事件觸發都通過單例(如應用實例)完成。

然而,因為全局事件的命名空間由各方共享,應合理命名全局事件,如引入一些命名空間(例:"frontend.mail.sent", "backend.mail.sent")。

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

$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組件的正常退出時。

在需要觸發事件的時候,直接激活組件的事件,即調用事件即可,如:比如CApplication組件的run方法中:

if($this->hasEventHandler('onBeginRequest'))  $this->onBeginRequest(new CEvent($this));

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

普通全局函數(內置的或用戶自定義的)

call_user_func(‘print', $str);

類的靜態方法,使用數組形式傳遞

call_user_func(array(‘className', ‘print'), $str );

對象方法,使用數組形式傳遞

$obj = new className();call_user_func(array($obj, ‘print'), $str );

匿名方法,類似javascript的匿名函數

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

或使用以下形式:

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

總結

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



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲视频777| 欧美亚洲一区在线| 日韩在线视频观看正片免费网站| 国产精品自产拍在线观看中文| 亚洲第一精品久久忘忧草社区| 国产精品免费福利| 亚洲国产精久久久久久久| 蜜臀久久99精品久久久久久宅男| 日本成熟性欧美| 欧美激情一区二区三区高清视频| 色中色综合影院手机版在线观看| 狠狠躁夜夜躁久久躁别揉| 国产精品福利片| 91在线免费视频| 国产九九精品视频| 欧美性videos高清精品| 另类少妇人与禽zozz0性伦| 丝袜亚洲另类欧美重口| 韩国国内大量揄拍精品视频| 国外成人免费在线播放| 亚洲欧美日韩直播| 精品自拍视频在线观看| 欧美精品aaa| 精品国产乱码久久久久久虫虫漫画| 亚洲国产欧美一区二区丝袜黑人| 久久免费视频在线观看| 欧美视频精品一区| 成人美女免费网站视频| 欧美性生交xxxxx久久久| 国产美女久久精品| 亚洲天天在线日亚洲洲精| 日本精品中文字幕| 欧美黑人极品猛少妇色xxxxx| 国产亚洲精品美女久久久久| 欧美午夜精品久久久久久人妖| 91在线免费网站| 欧美一区二粉嫩精品国产一线天| 久久视频在线看| 亚洲天堂av综合网| 久久久人成影片一区二区三区观看| 中文字幕亚洲无线码a| 亚洲免费视频观看| 欧美猛交ⅹxxx乱大交视频| 91沈先生作品| 国产精品一区二区女厕厕| 日韩福利视频在线观看| 91精品国产高清久久久久久| 国产欧美精品一区二区三区介绍| 久久久成人精品| 国产精品美女www爽爽爽视频| 热99久久精品| 国精产品一区一区三区有限在线| 欧美精品一区二区三区国产精品| 亚洲精品国产精品国自产在线| 亚洲精品国精品久久99热一| 日韩欧美在线中文字幕| 成人精品视频久久久久| 在线视频欧美日韩| 亚洲一区中文字幕在线观看| 国产精品美女久久久久久免费| 亚洲精品一区二区网址| 美日韩精品免费观看视频| 一区二区三区视频在线| 久久精品男人天堂| 久久夜色精品国产亚洲aⅴ| 亚洲精品国偷自产在线99热| 日韩av色综合| 日韩欧美一区视频| 色狠狠久久aa北条麻妃| 亚洲精品一区二区在线| 国产精品免费视频久久久| 超碰日本道色综合久久综合| 国模叶桐国产精品一区| 搡老女人一区二区三区视频tv| 国产精自产拍久久久久久| 国产香蕉精品视频一区二区三区| 中文字幕精品影院| 国产成人免费91av在线| 国产精品入口日韩视频大尺度| 亚洲精品成人久久电影| 国产精品jizz在线观看麻豆| 一区二区在线视频播放| 国产999精品视频| 成人在线视频网| 欧美性极品少妇精品网站| 久久天天躁日日躁| 久久久精品一区| 亚洲视频电影图片偷拍一区| 亚洲3p在线观看| 一区二区日韩精品| 国产亚洲美女久久| 亚洲香蕉av在线一区二区三区| 日韩精品www| 国产精品视频成人| 国产精品久久久久av| 欧美插天视频在线播放| 欧美极品少妇xxxxⅹ喷水| 国产精品丝袜久久久久久不卡| 久久亚洲精品国产亚洲老地址| 国产主播精品在线| 欧美激情一区二区久久久| 97久久超碰福利国产精品…| 欧美夜福利tv在线| 日产日韩在线亚洲欧美| 日韩高清av一区二区三区| 亚洲自拍小视频免费观看| 亚洲国产成人av在线| 操91在线视频| 欧美黄色小视频| 日韩欧美精品中文字幕| 影音先锋欧美精品| 美女性感视频久久久| 91国产高清在线| 日韩在线视频一区| 国产精品va在线播放我和闺蜜| 日韩中文在线不卡| 欧美另类高清videos| 久久亚洲精品毛片| 日本sm极度另类视频| 国产精品自产拍在线观看中文| 色偷偷88888欧美精品久久久| 亚洲欧美日韩中文在线制服| www.久久撸.com| 热草久综合在线| 91精品国产高清| 91精品国产综合久久久久久蜜臀| 欧美成人剧情片在线观看| 日本国产一区二区三区| 亚洲国产精品成人va在线观看| 欧美激情一区二区久久久| 这里只有精品在线观看| 欧美日韩国产成人| 国产亚洲在线播放| 色婷婷**av毛片一区| 日韩欧美精品网站| 成人激情av在线| 这里只有精品在线观看| 另类图片亚洲另类| 国产日韩精品在线播放| 久久久国产一区二区| 97色伦亚洲国产| 亚洲人成电影网站色xx| 久久69精品久久久久久久电影好| 欧美亚洲另类制服自拍| 日韩一级裸体免费视频| 狠狠躁天天躁日日躁欧美| 国产精品一区电影| 国产成人aa精品一区在线播放| 久久久人成影片一区二区三区| 亚洲国产婷婷香蕉久久久久久| 欧美高清第一页| 亚洲国产成人在线视频| 国产v综合v亚洲欧美久久| 一级做a爰片久久毛片美女图片| 91亚洲国产成人久久精品网站| 亚洲网在线观看| 中文字幕欧美在线| 日韩av免费看网站| 国产丝袜一区二区| 欧美电影免费观看| 欧美电影免费观看大全| 国产精品爽爽爽| 美日韩精品视频免费看| 久久久爽爽爽美女图片|