CI框架原始碼閱讀筆記6 擴充套件鉤子 Hook.php
CI框架允許你在不修改系統核心程式碼的基礎上新增或者更改系統的核心功能(如重寫快取、輸出等)。例如,在系統開啟hook的條件下(config.php中
‘class’ => ‘frameLog’,
‘function’ => ‘postLog’,
‘filename’ => ‘post_system.php’,
‘filepath’ => ‘hooks’,
);
上述鉤子定義了一個post_system的鉤子,用於在最終的頁面渲染之後的指令碼處理(引數的含義可以參考後面或者手冊,這裡暫時不做更多解釋)。
那麼問題來了:
鉤子是什麼?
CI中支援的鉤子有哪些?
CI中鉤子是如何實現的?
我們一步步來看。
1. 鉤子是什麼
百度百科上對於鉤子的定義是:
鉤子實際上是一個處理訊息的程式段,通過系統呼叫,把它掛入系統。每當特定的訊息發出,在沒有到達目的視窗前,鉤子程式就先捕獲該訊息,亦即鉤子函式先得到控制權。這時鉤子函式即可以加工處理(改變)該訊息,也可以不作處理而繼續傳遞該訊息,還可以強制結束訊息的傳遞。
從上述定義我們可以看出幾點:
鉤子是一種事件驅動模式,它的核心自然是事件(CI中pre_system,pre_controller等都是特定的事件)。
既然是事件驅動,那麼必然要包含最重要的兩個步驟: (1)、事件註冊。對於Hook而言,就是指Hook鉤子的掛載。(2).事件觸發。在特定的時間點call特定的鉤子,執行相應的鉤子程式。
既然是事件驅動,那麼也應該支援統一掛鉤點的多個註冊事件。
啟動Hook鉤子之後,程式的流程可能會發生變化,且鉤子之間可能有相互呼叫的可能性,如果處理不當,會有死迴圈的可能性。同時,鉤子的啟用使得程式在一定程度上變得複雜,難以除錯。
2. CI中預定義鉤子
CI中提供了7個可用的預設掛鉤點,分別是:
pre_system: 指在系統載入前期的鉤子
pre_controller:呼叫控制器之前的鉤子,路由與安全性檢查已經完畢
post_controller_constructor:控制器例項化之後,任何方法呼叫之前
post_controller:控制器完全執行之後
display_override:重寫display
cache_override :重寫快取
post_system:最終的頁面傳送到客戶端之後
3. CI中鉤子的實現
CI中鉤子的核心功能是由Hook元件完成的,先看該元件的類圖:
其中:
enabled: 鉤子功能是否開啟的標誌。
hooks :儲存系統中啟用的鉤子列表
in_progress:之後我們會看到,這個標誌位用於防止鉤子之間的互相呼叫而導致的死迴圈。
_construct是Hook元件的建構函式,這其中呼叫了_initialize來完成初始化的工作
_call_hook: 呼叫_run_hook來call指定的鉤子程式。之前CodeIgniter.PHP中我們已經看到,_call_hook是實際提供給外部呼叫的介面。
_run_hook: 實際執行鉤子程式的函式
在開始之前,我們先貼出預定義鉤子的結構。這個結構可能會貫穿在原始碼的始終,因而我們有必要知道該結構的引數含義。
$hook[‘xx’] = array(
‘class’ => ‘xx’, //鉤子呼叫的類名,可以為空
‘function’ => ‘xx’,//鉤子呼叫的函式名
‘filename’ => ‘xx’,//該鉤子的檔名
‘filepath’ => ‘xx’,//鉤子的目錄
‘params’ => ‘xx’//傳遞給鉤子的引數
);
1). 鉤子元件初始化
_initialize函式用於鉤子元件的初始化,該函式主要完成的工作有:
(1) 檢查配置檔案中hook功能是否被啟用,這需要載入Config(配置管理元件):
[php] view plain copy print?
$CFG =& load_class(‘Config’, ‘core’);
if ($CFG->item(‘enable_hooks’) == FALSE)
{
return;
}
(2) 載入定義的hook列表
同樣,你可以設定不同的ENVIRONMENT啟用不同的hook,如果有的話,優先載入ENVRIONMENT下的hook:
[php] view plain copy print?
if (defined(‘ENVIRONMENT’) AND is_file(APPPATH.’config/’.ENVIRONMENT.’/hooks.php’))
{
include(APPPATH.’config/’.ENVIRONMENT.’/hooks.php’);
}
elseif (is_file(APPPATH.’config/hooks.php’))
{
include(APPPATH.’config/hooks.php’);
}
(3) Hook的檢查。如果未設定任何hook,或者設定的hook格式錯誤,則不作任何處理,直接退出:
[php] view plain copy print?
if ( ! isset(
{
return;
}
經過initialize之後,Hook::hooks中儲存了已經定義的hook列表:
[php] view plain copy print?
this->hooks =&
2. Call指定的鉤子
_call_hook是主程式中直接呼叫的介面。該介面主要的工作有:
(1). 檢查鉤子是否被啟用,以及call的鉤子是否被預定義(如果未啟用或者call的鉤子不存在,則直接返回):
[php] view plain copy print?
if ( !
{
return FALSE;
}
(2). 檢查同一個掛鉤點是否啟用了多個鉤子,如果有,則依次執行之:
[php] view plain copy print?
if (isset(
{
foreach (
{
}
}
(3). 否則,只有一個鉤子,執行它
[php] view plain copy print?
else
{
}
_run_hook是實際執行hook的函式。
3. run執行特定的鉤子程式
_run_hook函式是hook的實際執行者,該函式接收一個預定義的hook陣列作為引數,實現如下:
(1). 如果傳遞的引數壓根就不是陣列(自然也就不是有效的hook),那麼直接返回:
[php] view plain copy print?
if ( ! is_array($data))
{
return FALSE;
}
(2). 檢查hook執行狀態。
in_progress用於標誌當前hook的執行狀態。這個引數的主要作用,是防止hook之間的相互呼叫而導致的死迴圈。
[php] view plain copy print?
if ($this->in_progress == TRUE)
{
return;
}
(3). Hook的合法性檢查。
為了方便講述,我們再次提出一個預定義的hook需要的引數:
$hook[‘xx’] = array(
‘class’ => ‘xx’, //鉤子呼叫的類名,可以為空
‘function’ => ‘xx’,//鉤子呼叫的函式名
‘filename’ => ‘xx’,//該鉤子的檔名
‘filepath’ => ‘xx’,//鉤子的目錄
‘params’ => ‘xx’//傳遞給鉤子的引數
);
其中class和params是可選引數,其他3個引數為必選引數,如果不提供,則由於無法準確定位到hook程式,只能直接返回:
[php] view plain copy print?
if ( ! isset(
{
return FALSE;
}
if ( ! file_exists($filepath))
{
return FALSE;
}
(4). 到這裡,已經基本確認鉤子程式的位置了,這裡有兩種情況:
a. 預定義的hook中class引數為空,表明使用的是過程式的呼叫方式,則直接執行hook檔案中的function xxx
b. class引數不為空,提供的是物件導向的方式,則實際的鉤子程式是
[php] view plain copy print?
$params = ”;
/* 獲取 hook class */
if (isset(
{
}
/* 獲取 hook function */
if (isset(data[‘function’]))
{
}
/* 獲取傳遞的 hook 引數 */
if (isset(data[‘params’]))
{
}
/* 如果class和function都不存在,則無法定位hook程式,直接返回 */
if (
{
return FALSE;
}
(5). 設定執行標誌in_progress,並執行上述兩種情況下的hook:
[php] view plain copy print?
/* 物件導向的設定方式 */
if (class !== FALSE)
{
if ( ! class_exists(
{
require($filepath);
}
$HOOK = new $class;
$HOOK->$function($params);
}
else
{
if ( ! function_exists(function))
{
require(
}
$function($params);
}
最後,別忘了在hook執行完之後,設定標識位in_progress為false,並返回執行成功的標誌:
[php] view plain copy print?
$this->in_progress = FALSE;
return TRUE;
Hook元件的完整原始碼:
[php] view plain copy print?
相關文章
- Koa 原始碼閱讀筆記原始碼筆記
- CopyOnWriteArrayList原始碼閱讀筆記原始碼筆記
- ArrayList原始碼閱讀筆記原始碼筆記
- LinkedList原始碼閱讀筆記原始碼筆記
- python原始碼閱讀筆記Python原始碼筆記
- guavacache原始碼閱讀筆記Guava原始碼筆記
- LongAdder原始碼閱讀筆記原始碼筆記
- JDK原始碼閱讀:String類閱讀筆記JDK原始碼筆記
- JDK原始碼閱讀:Object類閱讀筆記JDK原始碼Object筆記
- Thinkphp實戰利用鉤子使用行為擴充套件 (Hook)PHP套件Hook
- JDK原始碼閱讀(7):ConcurrentHashMap類閱讀筆記JDK原始碼HashMap筆記
- JDK原始碼閱讀(5):HashTable類閱讀筆記JDK原始碼筆記
- JDK原始碼閱讀(4):HashMap類閱讀筆記JDK原始碼HashMap筆記
- Express Session 原始碼閱讀筆記ExpressSession原始碼筆記
- 【iOS印象】GLPubSub 原始碼閱讀筆記iOS原始碼筆記
- Flask 原始碼閱讀筆記 開篇Flask原始碼筆記
- Redux 學習筆記 – 原始碼閱讀Redux筆記原始碼
- goroutine排程原始碼閱讀筆記Go原始碼筆記
- 如何閱讀框架原始碼框架原始碼
- ES6學習筆記4--字串的擴充套件筆記字串套件
- JDK原始碼閱讀(3):AbstractStringBuilder、StringBuffer、StringBuilder類閱讀筆記JDK原始碼UI筆記
- 【初學】Spring原始碼筆記之零:閱讀原始碼Spring原始碼筆記
- Laravel 訊息通知原始碼閱讀筆記Laravel原始碼筆記
- tomcat8.5.57原始碼閱讀筆記1Tomcat原始碼筆記
- 擴充套件包 Laravel-compass 使用筆記套件Laravel筆記
- PostgreSQL 原始碼解讀(248)- HTAB動態擴充套件圖解#2SQL原始碼套件圖解
- PostgreSQL 原始碼解讀(247)- HTAB動態擴充套件圖解#1SQL原始碼套件圖解
- Swift標準庫原始碼閱讀筆記 - DictionarySwift原始碼筆記
- Django 原始碼閱讀筆記(基礎檢視)Django原始碼筆記
- Vue2.0原始碼閱讀筆記(四):nextTickVue原始碼筆記
- 《人月神話》閱讀筆記6筆記
- 聊聊Dubbo(五):核心原始碼-SPI擴充套件原始碼套件
- C#學習筆記(補充)——擴充套件方法、事件C#筆記套件事件
- es6-字串擴充套件字串套件
- es6-物件擴充套件物件套件
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- PostgreSQL 原始碼解讀(216)- 實現簡單的擴充套件函式SQL原始碼套件函式
- Spring Boot Transactional註解原始碼閱讀筆記(一)Spring Boot原始碼筆記
- Spring Boot Transactional註解原始碼閱讀筆記(二)Spring Boot原始碼筆記