概述
行為(Behavior)是ThinkPHP擴展機制中比較關鍵的一項擴展,行為既可以獨立調用,也可以綁定到某個標簽中進行偵聽,官方提出的CBD模式中行為也占了主要的地位,可見行為在ThinkPHP框架中意義非凡。
這里指的行為是一個比較抽象的概念,你可以想象成在應用執行過程中的一個動作或者處理,在框架的執行流程中,各個位置都可以有行為產生,例如路由檢測是一個行為,靜態緩存是一個行為,用戶權限檢測也是行為,大到業務邏輯,小到瀏覽器檢測、多語言檢測等等都可以當做是一個行為,甚至說你希望給你的網站用戶的第一次訪問彈出Hello,world!這些都可以看成是一種行為,行為的存在讓你無需改動框架和應用,而在外圍通過擴展或者配置來改變或者增加一些功能。
而不同的行為之間也具有位置共同性,比如,有些行為的作用位置都是在應用執行前,有些行為都是在模板輸出之后,我們把這些行為發生作用的位置稱之為標簽(位),當應用程序運行到這個標簽的時候,就會被攔截下來,統一執行相關的行為,類似于AOP編程中的“切面”的概念,給某一個切面綁定相關行為就成了一種類AOP編程的思想。
系統標簽位
系統核心提供的標簽位置包括下面幾個(按照執行順序排列):
app_init | 應用初始化標簽位 |
path_info | PATH_INFO檢測標簽位 |
route_check | 路由檢測標簽位 |
app_begin | 應用開始標簽位 |
action_name | 操作方法名標簽位 |
action_begin | 控制器開始標簽位 |
view_begin | 視圖輸出開始標簽位 |
view_template | 視圖模板解析標簽位 |
view_parse | 視圖解析標簽位 |
view_filter | 視圖輸出過濾標簽位 |
view_end | 視圖輸出結束標簽位 |
action_end | 控制器結束標簽位 |
app_end | 應用結束標簽位 |
在每個標簽位置,可以配置多個行為定義,行為的執行順序按照定義的順序依次執行。除非前面的行為里面中斷執行了(某些行為可能需要中斷執行,例如檢測機器人或者非法執行行為),否則會繼續下一個行為的執行。
除了這些系統內置標簽之外,開發人員還可以在應用中添加自己的應用標簽,例如我們給應用的公共Action類CommonAction添加一個action_init標簽位。
Class CommonAction extends Action{
Public function _initialize(){
tag('action_init'); // 添加action_init 標簽
}
}
注意:tag函數用于設置某個標簽位,可以傳入并且只接受一個參數,如果需要傳入多個參數,請使用數組,該參數為引用傳值,所以只能傳入變量。
核心行為
新版系統的很多核心功能也是采用行為擴展組裝的,雖然在開發過程中可能感覺不到這種變化,但正是由于這種架構設計的改變,讓新版變得更加靈活和易擴展,這是一個里程碑式的改變,對于滿足項目日益紛繁復雜的需求和定制底層框架提供了更多的方便和可能性。
框架核心內置的行為包括如下:
行為名稱 | 說明 | 對應標簽位置 |
checkRoute | 路由檢測行為,完成內置的路由功能 | route_check |
LocationTemplate | 模板定位行為,完成模板文件自動定位和輸出規則 | view_template |
ParseTemplate | 模板文件解析,并支持第三方模板引擎驅動 | view_parse |
ShowPageTrace | 頁面Trace功能行為,完成頁面Trace功能 | view_end |
ShowRuntime | 運行時間顯示行為,完成運行時間顯示 | view_filter |
TokenBuild | 令牌生成行為,完成表單令牌的自動生成 | view_filter |
ReadHtmlCache | 讀取靜態緩存行為 | app_init |
WriteHtmlCache | 生成靜態緩存行為 | view_filter |
行為定義
行為類的命名采用:行為名稱(駝峰法,首字母大寫)+Behavior 行為類的定義方式如下:
class TestBehavior extends Behavior {
// 行為參數定義
protected $options = array(
'test_param' => false, // 行為參數 會轉換成TEST_PARAM配置參數
);
// 行為擴展的執行入口必須是run
public function run(&$params){
if(C('TEST_PARAM')) {
echo 'RUNTEST BEHAVIOR '.$params;
}
}
}
行為類必須繼承Behavior類,并且必須定義執行入口方法run(&$params); 由于行為的調用機制影響,run方法不需要任何返回值,所有返回都通過引用返回。每個行為類可以定義options屬性,該屬性中的參數會自動轉換成單獨配置參數,也就是說該參數可以用全局的C方法直接讀取或者修改,同時也意味著行為中定義的options參數只是提供一個默認值,你完全可以在配置文件中或者使用C方法動態配置生效,而不需要手動傳入行為類。這是Behavior類提供的方便和全局配置對接的一個特性,當然你完全可以不依賴這個特性。
因此,上面的run方法實現也可以更改為:
public function run(&$params){
if($this->test_param) {
echo 'RUNTEST BEHAVIOR '.$params;
}
}
run方法的參數只有一個,但支持傳入數組。
行為類是支持自動加載的,其放置的位置可以包括如下位置,可以根據需要自行選擇:
位置 | 所在目錄 | 說明 |
核心行為目錄 | ThinkPHP/Lib/Behavior/ | 通常用于放置一些核心的內置行為 |
擴展行為目錄 | Extend/Behavior/ | 這是ThinkPHP擴展的行為擴展目錄,第三方的擴展大都在此 |
項目的行為目錄 | 項目目錄/Lib/Behavior/ | 用于項目自身的行為擴展 |
獨立分組的行為目錄 | 獨立分組目錄/Behavior/ | 如果采用了獨立分組的話,也可以把行為類放在這里 |
官方擴展包的行為擴展都位于Extend/Behavior/目錄下面。
行為綁定
行為定義完成后,就需要綁定到某個標簽位置才能生效,否則是不會執行的。
我們需要在項目的行為定義文件tags.php文件中進行行為和標簽的位置定義,格式如下:
return array(
'標簽名稱'=>array('行為名1','行為名2',...),
);
標簽名稱包括我們前面列出的系統標簽和應用中自己定義的標簽名稱,比如你需要在app_init標簽位置定義一個CheckLangBehavior行為類的話,可以使用:
return array(
'app_init'=>array('CheckLang'),
);
可以給一個標簽位定義多個行為,行為的執行順序就是定義的先后順序,例如:
return array(
'app_init'=>array('CheckLang','CronRun'),
);
默認情況下tags.php中定義的行為會并入系統行為一起執行,也就是說如果系統的行為定義中app_init標簽中已經定義了其他行為,則會首先執行系統行為擴展中定義的行為,然后再執行項目行為中定義的行為,如果你希望項目tags中定義的行為完全替換系統的行為擴展,可以使用:
return array(
'app_init'=>array('CheckLang','CronRun','_overlay'=>1),
);
應用行為的定義沒有限制,你可以把一個行為綁定到多個標簽位置執行,例如:
return array(
'app_begin'=>array('Test'), // 在app_begin 標簽位添加Test行為
'app_end'=>array('Test'), // 在app_end 標簽位添加Test行為
);
為了避免混淆,自定義的行為擴展盡量不要和核心的行為擴展重名。
除了定義tags行為配置文件之外,框架還提供了動態添加行為到標簽位的方法,例如我們可以使用下面的方式添加Test行為到app_end標簽位,而無需在tags文件中添加定義:
add_tag_behavior('app_end','Test');
表示把Test行為添加到app_end標簽位的最后,你可以把這個代碼放到項目的公共函數文件中甚至直接放到行為類的最后(如果你確定這個行為擴展只有你的項目會用到的話)。
單獨執行
有時候,行為的調用不一定要放到標簽才能調用,如果需要的話,我們可以在控制器中直接調用行為。例如,我們可以把用戶權限檢測封裝成一個行為類,例如:
class AuthCheckBehavior extends Behavior {
// 行為參數定義
protected $options = array(
'USER_AUTH_ON' =>false, // 是否開啟用戶認證
'USER_AUTH_ID' => 'user_id', // 定義用戶的id為權限認證字段
);
// 行為擴展的執行入口必須是run
public function run(&$return){
if(C('USER_AUTH_ON ')) {
// 進行權限認證邏輯 如果認證通過 $return = true;
// 否則用halt輸出錯誤信息
}
}
定義了AuthCheck行為后,然后在_initialize方法中直接用下面的方式調用:
B('AuthCheck');
注意:因為這種方式的行為調用需要在相關位置添加代碼,所以一般只有在應用代碼才直接使用B方法調用。總結
說了這么多,只是讓你了解ThinkPHP中的行為的概念和運行機制,至于如何用好行為,以及怎么封裝行為和定義標簽還需要多實踐了。相信很多熟悉ThinkPHP的開發者一定可以通過行為擴展來更方便的給框架增加功能了。