CI架構原始碼閱讀筆記6 擴充鉤子 Hook.php

來源:互聯網
上載者:User

標籤:bar   file   傳遞   控制   params   渲染   enable   div   合法性   

CI架構同意你在不改動系統核心代碼的基礎上加入或者更改系統的核心功能(如重寫緩衝、輸出等)。

比如,在系統開啟hook的條件下(config.php中$config[‘enable_hooks‘] = TRUE;)。通過加入特定的鉤子,能夠讓系統在特定的時刻觸發特定的指令碼:

$hook[‘post_system‘] = array(    ‘class‘     => ‘frameLog‘,    ‘function‘  => ‘postLog‘,    ‘filename‘  => ‘post_system.php‘,    ‘filepath‘   => ‘hooks‘,);

上述鉤子定義了一個post_system的鉤子。用於在終於的頁面渲染之後的指令碼處理(參數的含義能夠參考後面或者手冊。這裡臨時不做很多其它解釋)。

那麼問題來了:

  1. 鉤子是什嗎?
  2. CI中支援的鉤子有哪些?
  3. CI中鉤子是怎樣實現的?

我們一步步來看。

1.  鉤子是什麼

  百度百科上對於鉤子的定義是:

鉤子實際上是一個處理訊息的程式段,通過系統調用。把它掛入系統。每當特定的訊息發出,在沒有到達目的表單前,鉤子程式就先捕獲該訊息,亦即鉤子函數先得到控制權。這時鉤子函數即能夠加工處理(改變)該訊息。也能夠不作處理而繼續傳遞該訊息。還能夠強制結束訊息的傳遞。

  從上述定義我們能夠看出幾點:

  1. 鉤子是一種事件驅動模式。它的核心自然是事件(CI中pre_system,pre_controller等都是特定的事件)。

  2. 既然是事件驅動,那麼必定要包括最重要的兩個步驟: (1)、事件注冊。對於Hook而言,就是指Hook鉤子的掛載。(2).事件觸發。

    在特定的時間點call特定的鉤子,運行對應的鉤子程式。

  3. 既然是事件驅動。那麼也應該支援統一掛鈎點的多個注冊事件。
  4. 啟動Hook鉤子之後,程式的流程可能會發生變化,且鉤子之間可能有相互調用的可能性,假設處理不當,會有死迴圈的可能性。同一時候,鉤子的啟用使得程式在一定程度上變得複雜,難以調試。

2.  CI中提前定義鉤子

CI中提供了7個可用的預設掛鈎點,各自是:

  pre_system:     指在系統載入前期的鉤子

  pre_controller:調用控制器之前的鉤子,路由與安全性檢查已經完成

  post_controller_constructor:控制器執行個體化之後,不論什麼方法調用之前

  post_controller:控制器全然執行之後

  display_override:重寫display

  cache_override :重寫緩衝

  post_system:終於的頁面發送到client之後

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(組態管理組件):

$CFG =& load_class(‘Config‘, ‘core‘);if ($CFG->item(‘enable_hooks‘) == FALSE){return;}

(2)     載入定義的hook列表

相同,你能夠設定不同的ENVIRONMENT啟用不同的hook,假設有的話,優先載入ENVRIONMENT下的hook:

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格式錯誤,則不作不論什麼處理,直接退出:

if ( ! isset($hook) OR ! is_array($hook)){return;}

經過initialize之後,Hook::hooks中儲存了已經定義的hook列表:

$this->hooks =& $hook;

2.  Call指定的鉤子

_call_hook是主程式中直接調用的介面。該介面基本的工作有:

(1).    檢查鉤子是否被啟用,以及call的鉤子是否被提前定義(假設未啟用或者call的鉤子不存在。則直接返回):

if ( ! $this->enabled OR ! isset($this->hooks[$which])){return FALSE;}

(2).    檢查同一個掛鈎點是否啟用了多個鉤子,假設有。則依次運行之:

if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0])){foreach ($this->hooks[$which] as $val){$this->_run_hook($val);}}

(3).    否則。僅僅有一個鉤子。運行它

else{$this->_run_hook($this->hooks[$which]);}

_run_hook是實際運行hook的函數。

3.  run運行特定的鉤子程式

_run_hook函數是hook的實際運行者。該函數接收一個提前定義的hook數組作為參數。實現例如以下:

(1).    假設傳遞的參數壓根就不是數組(自然也就不是有效hook),那麼直接返回:

if ( ! is_array($data)){return FALSE;}

(2).    檢查hook運行狀態。

in_progress用於標誌當前hook的運行狀態。這個參數的主要作用,是防止hook之間的相互調用而導致的死迴圈。

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程式,僅僅能直接返回:

if ( ! isset($data[‘filepath‘]) OR ! isset($data[‘filename‘])){return FALSE;}$filepath = APPPATH.$data[‘filepath‘].‘/‘.$data[‘filename‘];if ( ! file_exists($filepath)){return FALSE;}

(4).   到這裡。已經基本確認鉤子程式的位置了,這裡有兩種情況:

a. 提前定義的hook中class參數為空白。表明使用的是過程式的調用方式。則直接運行hook檔案裡的function xxx 

b. class參數不為空白,提供的是物件導向的方式,則實際的鉤子程式是$class->$function .相同,假設既沒有設定class,也沒有設定function參數,則無法運行hook。直接返回:

$class= FALSE;$function= FALSE;$params= ‘‘;/* 擷取 hook class */if (isset($data[‘class‘]) AND $data[‘class‘] != ‘‘){$class = $data[‘class‘];}/* 擷取 hook function */if (isset($data[‘function‘])){$function = $data[‘function‘];}/* 擷取傳遞的 hook 參數 */if (isset($data[‘params‘])){$params = $data[‘params‘];}/* 假設class和function都不存在,則無法定位hook程式,直接返回 */if ($class === FALSE AND $function === FALSE){return FALSE;}

(5).   設定運行標誌in_progress。並運行上述兩種情況下的hook:

/* 物件導向的設定方式 */if ($class !== FALSE){if ( ! class_exists($class)){require($filepath);}$HOOK = new $class;$HOOK->$function($params);}else{if ( ! function_exists($function)){require($filepath);}$function($params);}

最後,別忘了在hook運行完之後,設定標識位in_progress為false,並返回運行成功的標誌:

$this->in_progress = FALSE;return TRUE;

Hook組件的完整原始碼:

<?

php if ( ! defined(‘BASEPATH‘)) exit(‘No direct script access allowed‘);class CI_Hooks {/** * Determines wether hooks are enabled * * @var bool */var $enabled= FALSE;/** * List of all hooks set in config/hooks.php * */var $hooks= array();/** * Determines wether hook is in progress, used to prevent infinte loops * */var $in_progress= FALSE;/** * Constructor */function __construct(){$this->_initialize();log_message(‘debug‘, "Hooks Class Initialized");}/** * Initialize the Hooks Preferences * * @accessprivate * @returnvoid */function _initialize(){$CFG =& load_class(‘Config‘, ‘core‘);// If hooks are not enabled in the config file// there is nothing else to doif ($CFG->item(‘enable_hooks‘) == FALSE){return;}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‘);}if ( ! isset($hook) OR ! is_array($hook)){return;}$this->hooks =& $hook;$this->enabled = TRUE;}/** * Call Hook * * Calls a particular hook * * @accessprivate * @paramstringthe hook name * @returnmixed */function _call_hook($which = ‘‘){if ( ! $this->enabled OR ! isset($this->hooks[$which])){return FALSE;}if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0])){foreach ($this->hooks[$which] as $val){$this->_run_hook($val);}}else{$this->_run_hook($this->hooks[$which]);}return TRUE;}/** * Run Hook * * Runs a particular hook * * @accessprivate * @paramarraythe hook details * @returnbool */function _run_hook($data){if ( ! is_array($data)){return FALSE;}// If the script being called happens to have the same hook call within it a loop can happenif ($this->in_progress == TRUE){return;}if ( ! isset($data[‘filepath‘]) OR ! isset($data[‘filename‘])){return FALSE;}$filepath = APPPATH.$data[‘filepath‘].‘/‘.$data[‘filename‘];if ( ! file_exists($filepath)){return FALSE;}$class= FALSE;$function= FALSE;$params= ‘‘;if (isset($data[‘class‘]) AND $data[‘class‘] != ‘‘){$class = $data[‘class‘];}if (isset($data[‘function‘])){$function = $data[‘function‘];}if (isset($data[‘params‘])){$params = $data[‘params‘];}if ($class === FALSE AND $function === FALSE){return FALSE;}$this->in_progress = TRUE;// Call the requested class and/or functionif ($class !== FALSE){if ( ! class_exists($class)){require($filepath);}$HOOK = new $class;$HOOK->$function($params);}else{if ( ! function_exists($function)){require($filepath);}$function($params);}$this->in_progress = FALSE;return TRUE;}}

參考文獻

1.  http://codeigniter.org.cn/user_guide/general/hooks.html   手冊

2.  http://itopic.org/codeigniter-hook.html  

3.  http://codeigniter.org.cn/forums/thread-4947-1-1.html  鉤子實現的Layout

CI架構原始碼閱讀筆記6 擴充鉤子 Hook.php

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.