本文實例講述了Yii Framework框架中事件和行為的區(qū)別及應用。分享給大家供大家參考,具體如下:
個人覺得,在 Yii 里面,最難以明白的就是事件(Event)和行為(behavior)了。這不僅僅是因為它們的概念
比較難明,關鍵是它們的應用場景比較難明,不知道什么時候應該使用事件和行為來開發(fā)。
關于 Yii 的事件和行為的描述,可參考 http://www.yiiframework.com/doc/api/1.1/CComponent
本文參考的文章:
http://www.larryullman.com/2010/07/20/forcing-login-for-all-pages-in-yii/
http://www.yiiframework.com/wiki/44/behaviors-events/
事件
事件模型就是設計模式中的“觀察者模式”:當對象的狀態(tài)發(fā)生了變化,那么這個對象可以將該事件通知其它對象。
為了使用事件模型,需要實現(xiàn)這三個步驟:1、定義事件;2、注冊事件句柄;3、觸發(fā)事件。
為什么要做這三個步驟呢?因為對于 PHP 本身,它的執(zhí)行過程不是以進程化來運行的,
所以 Yii 的事件觸發(fā)機制不會像 ActionScript 3+ 那樣,直接將觸發(fā)事件。
有人說,Yii 的事件概念跟 js 中的事件概念差不多,因為 Yii 是將事件綁定到 Yii::app() 的執(zhí)行過程中。
由于本人對于 js 的事件沒有做過深入的了解,這里不敢貿(mào)然否定,或者肯定。
費話少說,先看這樣的應用場景:
我想在請求過來的時候,先將請求的 IP 的記錄到數(shù)據(jù)庫,然后才進行對應的相應的請求處理。
1. 通過編輯 components/Controller.php 的構造方法來處理。
如代碼:
1
2
3
4
5
6
7
8
|
class Controller extends CController { public function __construct() { parent::__construct(); //將請求的 IP 記錄到數(shù)據(jù)庫 } } |
2. 通過使用事件來處理。
我們來分析一個 framework/base/CApplication.php 的 run() 方法
1
2
3
4
5
6
7
8
|
public function run() { if ( $this ->hasEventHandler( 'onBeginRequest' )) $this ->onBeginRequest( new CEvent( $this )); $this ->processRequest(); if ( $this ->hasEventHandler( 'onEndRequest' )) $this ->onEndRequest( new CEvent( $this )); } |
從代碼可以看出來,在處理請求之前,Yii 首先會判斷一下當前有沒有處理 onBeginRequest 的函數(shù)或者類的方法綁定了,
如果有這樣的函數(shù)或者類的方法存在,則先執(zhí)行了它們,然后再處理請求。
那么,怎樣寫 onBeginRequest,或者怎樣去調用呢?
方法一:修改 index.php
一般來說,我們的 index.php 最后一句是:
1
|
Yii::createWebApplication( $config )->run(); |
我們在這里將它改造一下,改成:
1
2
3
4
5
6
7
8
9
|
$app = Yii::createWebApplication( $config ); Yii::app()->onBeginRequest= function ( $event ) { //將請求的 IP 記錄到數(shù)據(jù)庫 }; Yii::app()->onBeginRequest= function ( $event ) { //其它的你想要處理的內容,比如說,生成一個文件 //file_put_contents('onBeginRequest.txt', '阿媽,我得左啦!'); }; $app ->run(); |
方法二:在配置文件 main.php 里面注冊事件
如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/*************************************************** 在我們想要的內容的前后出現(xiàn)了這些代碼 只是為了說明,我們添加的內容是要放在 這個配置數(shù)據(jù)的一維里面。 'import'=>array( 'application.models.*', 'application.components.*', 'application.helpers.*', ), 'defaultController'=>'post', ***************************************************/ //其它代碼 'import' => array ( 'application.models.*' , 'application.components.*' , 'application.helpers.*' , ), /************** 這才是我們想要添加的代碼 **************/ 'onBeginRequest' => array ( 'MyEventHandler' , 'MyEventHandlerMethod' ), 'defaultController' => 'post' , //其它代碼 |
關于 onBeginRequest 的使用,它必須是一個有效的 PHP 回調。
即,一個指匿名函數(shù),全局函數(shù)名的字符串或一個數(shù)組。如果是數(shù)組,那么該數(shù)組包含兩個元素,第一個元素是一個對象,第二個元素是這個對象的方法。
由此可見,方法一和方法二還是有點區(qū)別的。使用方法二的時候,只能注冊一個 PHP 回調,而使用方法一,可以是不同的 PHP 回調。當然,這里說的方法二
只能注冊一個 PHP 回調是指,對整個請求處理過程中肯定會執(zhí)行的 PHP 回調,在其它地方需要的時候,也可以加上你想實現(xiàn)的功能。
3. 另一個例子,來說明自己是怎樣定義一個事件的。
打開 models/ContactForm.php,輸入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/** * 自己定義發(fā)送郵件事件 * @param unknown_type $event */ public function onSendMail( $event ) { $this ->raiseEvent( 'onSendMail' , $event ); } /** * 驗證成功,執(zhí)行 * @see CModel::afterValidate() */ public function afterValidate() { if ( $this ->hasEventHandler( 'onSendMail' )) $this ->onSendMail( new CEvent( $this )); } |
這里我們定義了一個 onSendMail 事件,并在 Validate 驗證后,觸發(fā)此事件。
打開 controllers/SiteController.php,將修改actionContact修改為以下內容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public function actionContact() { $model = new ContactForm; $model ->onSendMail= function ( $event ) { $headers = "From: {$event->sender->email}\r\nReply-To: {$event->sender->email}" ; mail(Yii::app()->params[ 'adminEmail' ], $event ->sender->subject, $event ->sender->body, $headers ); }; if (isset( $_POST [ 'ContactForm' ])) { $model ->attributes= $_POST [ 'ContactForm' ]; if ( $model ->validate()) { Yii::app()->user->setFlash( 'contact' , 'Thank you for contacting us. We will respond to you as soon as possible.' ); $this ->refresh(); } } $this ->render( 'contact' , array ( 'model' => $model )); } |
上面的 3 點,雖然通過綁定事件來做一些額外的處理,但同時已經(jīng)暴露了一個問題,就是協(xié)同開發(fā)的時候,我不一定知道,
其他開發(fā)人員寫了哪些事件的 PHP 回調,在處理過程中到底會調用哪些事件的 PHP 回調。或者說,這個 PHP 回調在什么時候創(chuàng)建的,
或者說你在為組件添加事件處理函數(shù)時,找不到合適的時候,如果添加早了,組件還沒創(chuàng)建,如果添加晚了,事件不被執(zhí)行,有可能組件已經(jīng)執(zhí)行完了。
我們需要一個類似于配置文件的東西,將存在的事件處理組織起來,統(tǒng)一管理。這個時候,行為可以用上了。
行為
這里先重新描述一下為什么要使用行為。
有兩種辦法可以對類添加特性:
1、直接修改這個類的代碼,添加一些成員函數(shù)和成員變量;
2、派生,通過子類來擴展。
很明顯第二種方法更加易維護、易擴展。但是如果需要對一個類添加多個特性(多人在不同時期),那么需要進行多級派生,這顯然加大了維護成本。
在 Yii 里面,通過行為類綁定,組件將一個或多個 CBehavior 類的成員方法和成員變量添加到自己身上,并且在不需要的時候載掉某些 CBehavior 類。
同時,可以通過重寫 CBehavior::events 的方法,來實現(xiàn)對目標類的多個事件綁定。這些事件將會在當前行為綁定到目標類的時候,一起被綁定上。
下面我們以代碼來具體看一下這個行為特性。
在 protected 創(chuàng)建目錄 behaviors,并在protected/behaviors目錄下創(chuàng)建ApplicationBehavior.php,輸入如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?php class ApplicationBehavior extends CBehavior { public function events() { return array_merge (parent::events(), array ( 'onBeginRequest' => 'beginRequest' )); } public function beginRequest( $event ) { echo "我已經(jīng)將 onBeginRequest 的事件處理通過行為綁定了" ; } } |
此行為文件,是要為 CApplication 服務,仔細查看這個行為文件,我們可以看到,events 方法定義了些行為可以處理的事件,
上面的類,可以處理 onBeginRequest 事件,當然如果你自己定義的組件也有一個叫做 onBeginRequest 方法,你也可以使用此行為
后面的 beginRequest 就是事件的處理函數(shù),這個處理函數(shù)必須要有行為類中定義。
跟上面的事件一樣,也有兩種方法將此行為類附加到 CApplication。
方法一:
打開 index.php,輸入下面代碼
1
2
3
4
5
6
7
8
9
10
|
$app = Yii::createWebApplication( $config ); Yii::app()->onBeginRequest= function ( $event ) { //將請求的 IP 記錄到數(shù)據(jù)庫 }; Yii::app()->onBeginRequest= function ( $event ) { //file_put_contents('onBeginRequest.txt', '阿媽,我又得左啦!'); }; /****** 這句才是我們想要的東東 *********/ $app ->attachBehavior( 'app' , 'application.behaviors.ApplicationBehavior' ); $app ->run(); |
刷新頁面,你將會在頭部看到一行 “我已經(jīng)將 onBeginRequest 的事件處理通過行為綁定了”
方法二:
如果對 Yii 的組件定義了解的話,應該知道每一個組件,都有一個behaviors方法,該方法中定義的相關行為,在組件初始化時,會自動附件,
下面我們就為 CApplication 定義 behaviors,由于 CApplication 是系統(tǒng)級類,我們可以擴展此類,并添加behaviors方法。這里補充一下,
CApplication 是會根據(jù) config/main.php 配置進行初始化,那么我們就可以將 behaviors 定義在 main.php。
打開 protected/config/main.php,加入如下代碼:
1
2
3
|
'behaviors' => array ( 'app' => 'application.behaviors.ApplicationBehavior' , ), |
刷新頁面,你也會在頭部看到一行 “我已經(jīng)將 onBeginRequest 的事件處理通過行為綁定了”
通過以上的例子,希望相關讀者對 Yii 的事件和行為有一定的了解。
希望本文所述對大家基于Yii框架的PHP程序設計有所幫助。
原文鏈接:https://www.cnblogs.com/davidhhuan/archive/2012/01/19/2326123.html