CodeIgniter源碼分析[3]——擴(kuò)展框架核心:鉤子類Hooks.php

CI框架提供了鉤子的方法來修改框架的內(nèi)部運(yùn)作流程夯缺,而無需修改核心文件。CodeIgniter 的運(yùn)行遵循著一個(gè)特定的流程踊兜,但通過Hook特性你可能希望在 執(zhí)行流程中的某些階段添加一些動作,可以簡單理解為主流程執(zhí)行過程中預(yù)留了交給開發(fā)者實(shí)現(xiàn)的API。鉤子的實(shí)現(xiàn)在文件Hooks.php文件中毁葱,類名為CI_Hooks贰剥。

用戶配置

啟用鉤子
鉤子特性需要在配置文件中進(jìn)行設(shè)置,啟用鉤子特性需要在application/config/config.php文件中設(shè)置參數(shù)為:
$config['enable_hooks'] = TRUE
掛鉤點(diǎn)列表
在上一篇文章啟動器CodeIgniter.php中鸠澈,我們介紹了CI框架的執(zhí)行過程,應(yīng)該已經(jīng)了解了CI框架中定義的掛鉤點(diǎn)列表际度。下面我們進(jìn)一步解釋每個(gè)掛鉤點(diǎn)的作用涵妥。

  • pre_system 在系統(tǒng)執(zhí)行的早期調(diào)用,這個(gè)時(shí)候只有基準(zhǔn)測試類和鉤子類被加載了蓬网,還沒有執(zhí)行到路由或其他的流程。
  • pre_controller 在你的控制器調(diào)用之前執(zhí)行帆锋,所有的基礎(chǔ)類都已加載,路由和安全檢查也已經(jīng)完成皮官。
  • post_controller_constructor 在你的控制器實(shí)例化之后立即執(zhí)行实辑,控制器的任何方法都還尚未調(diào)用。
  • post_controller 在你的控制器完全運(yùn)行結(jié)束時(shí)執(zhí)行剪撬。
  • display_override 覆蓋 _display() 方法,該方法用于在系統(tǒng)執(zhí)行結(jié)束時(shí)向?yàn)g覽器發(fā)送最終的頁面結(jié)果馍佑。 這可以讓你有自己的顯示頁面的方法萍摊。注意你可能需要使用$this->CI =& get_instance()方法來獲取CI超級對象如叼,以及使用$this->CI->output->get_output()方法來 獲取最終的顯示數(shù)據(jù)冰木。
  • cache_override 使用你自己的方法來替代 輸出類 中的_display_cache()方法踊沸,這讓你有自己的緩存顯示機(jī)制。
  • post_system 在最終的頁面發(fā)送到瀏覽器之后评凝、在系統(tǒng)的最后期被調(diào)用腺律。

定義鉤子特性
定義鉤子特性需要在application/config/hooks.php文件中定義
設(shè)置的格式如:

$hook['pre_controller'] = array(
    'class' => 'ClassName',
    'function' => 'FunctionName',
    'filename' => 'FileName',
    'filepath' => 'FilePath',
    'params' => 'Array('argv1','argv2')'
);

如果需要在同一個(gè)掛鉤點(diǎn)添加多個(gè)腳本,則需要將鉤子數(shù)組定義為二維數(shù)組即可

至此匀钧,我們已經(jīng)清楚了鉤子的開啟和配置方法,下面我們開始分析CI_hooks類的代碼日杈。

屬性概覽

屬性名稱 注釋
public $enabled = FALSE 鉤子特性是否開啟的標(biāo)志
public $hooks = array() 獲取并保存定義的鉤子特性
protected $_objects = array() 保存執(zhí)行過的鉤子類的實(shí)例
protected $_in_progess = FALSE 確定鉤子程序的正常執(zhí)行防止死循環(huán)

方法概覽

方法名稱 注釋
__construct() 鉤子類的構(gòu)造函數(shù)
call_hook($which='') 調(diào)用制定名稱的鉤子類
_run_hook($data) 鉤子的實(shí)際執(zhí)行方法莉擒,被call_hook方法調(diào)用

構(gòu)造函數(shù)__construct
構(gòu)造函數(shù)主要是根據(jù)用戶配置確定是否開啟鉤子特性瘫絮,并在配置文件中尋找用戶配置的鉤子特性,如果沒有定義任何鉤子則返回麦萤,否則將更新屬性$hooks的值和將鉤子啟動標(biāo)志位$enabled置為True。

public function __construct()
{
   $CFG =& load_class('Config', 'core');
   log_message('info', 'Hooks Class Initialized');
   //如果用戶配置中沒有開啟鉤子特性栓辜,直接返回
   if ($CFG->item('enable_hooks') === FALSE)
   {
      return;
   }
   //尋找并獲取用戶配置文件中定義的鉤子特性$hook
   if (file_exists(APPPATH.'config/hooks.php'))
   {
      include(APPPATH.'config/hooks.php');
   }
   if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
   {
      include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
   }
   //如果用戶沒有定義任何鉤子特性垛孔,直接返回
   if ( ! isset($hook) OR ! is_array($hook))
   {
      return;
   }
   //設(shè)置類屬性$hooks和$enabled的值
   $this->hooks =& $hook;
   $this->enabled = TRUE;
}

調(diào)用鉤子函數(shù)call_hook()
call_hook方法就是在CI主流程中調(diào)用鉤子的方法,主要判斷是否在一個(gè)掛鉤點(diǎn)定義了多個(gè)鉤子狭莱,然后分別處理即可,實(shí)際鉤子的執(zhí)行方法是_run_hook方法

public function call_hook($which = '')
{
    //沒有開啟鉤子特性或鉤子未定義直接返回FALSE
   if ( ! $this->enabled OR ! isset($this->hooks[$which]))
   {
      return FALSE;
   }
   //判斷是否在這個(gè)掛鉤點(diǎn)定義了多個(gè)鉤子腋妙,并分別進(jìn)行處理
   if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))
   {
      foreach ($this->hooks[$which] as $val)
      {
         $this->_run_hook($val);
      }
   }
   else
   {
      $this->_run_hook($this->hooks[$which]);
   }
   return TRUE;
}

運(yùn)行鉤子函數(shù)_run_hook()
實(shí)際運(yùn)行鉤子函數(shù)里比較關(guān)鍵的是利用in_progess屬性保證鉤子的執(zhí)行遇到循環(huán)調(diào)用或錯誤調(diào)用時(shí)能夠跳出函數(shù)骤素,并且對定義類和方法以及只定義方法的情況都嘗試進(jìn)行調(diào)用匙睹,定義類和方法時(shí)會將類保存在屬性_objects[]數(shù)組中痕檬。

protected function _run_hook($data)
{
   //判斷鉤子是否可調(diào)用送浊,如無誤根據(jù)參數(shù)位置直接執(zhí)行Object->Function
   if (is_callable($data))
   {
      is_array($data)
         ? $data[0]->{$data[1]}()
         : $data();
      return TRUE;
   }
   elseif ( ! is_array($data))
   {
      return FALSE;
   }
   // -----------------------------------
   // 安全處理:防止鉤子循環(huán)調(diào)用或錯誤調(diào)用
   // -----------------------------------

   // 如果鉤子內(nèi)部調(diào)用的自己那就形成循環(huán)調(diào)用,其他錯誤調(diào)用也需要跳出
   if ($this->_in_progress === TRUE)
   {
      return;
   }

   // -----------------------------------
   // 設(shè)置文件的路徑
   // -----------------------------------
   if ( ! isset($data['filepath'], $data['filename']))
   {
      return FALSE;
   }
   $filepath = APPPATH.$data['filepath'].'/'.$data['filename'];
   //文件不存在的話直接返回False
   if ( ! file_exists($filepath))
   {
      return FALSE;
   }
   // 取得類和方法的名稱和參數(shù)列表
   $class    = empty($data['class']) ? FALSE : $data['class'];
   $function  = empty($data['function']) ? FALSE : $data['function'];
   $params       = isset($data['params']) ? $data['params'] : '';

   if (empty($function))
   {
      return FALSE;
   }
   $this->_in_progress = TRUE;
   // 調(diào)用請求的類和方法
   if ($class !== FALSE)
   {
      // 如果$class在屬性_objects[]中保存直接調(diào)用方法
      if (isset($this->_objects[$class]))
      {
         if (method_exists($this->_objects[$class], $function))
         {
            $this->_objects[$class]->$function($params);
         }
         else
         {
            return $this->_in_progress = FALSE;
         }
      }
      else
      {
         //如果加載類失敗直接返回置in_progress為False
         class_exists($class, FALSE) OR require_once($filepath);
         if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function))
         {
            return $this->_in_progress = FALSE;
         }
         // 將類的實(shí)例保存在_objects[]中然后執(zhí)行方法
         $this->_objects[$class] = new $class();
         $this->_objects[$class]->$function($params);
      }
   }
   else
   {
      //如果只在文件中定義了方法也同樣嘗試調(diào)用
      function_exists($function) OR require_once($filepath);
      if ( ! function_exists($function))
      {
         return $this->_in_progress = FALSE;
      }
      $function($params);
   }
   //執(zhí)行到此處表示未成環(huán)袭景,調(diào)用成功置in_progess為False
   $this->_in_progress = FALSE;
   return TRUE;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唁桩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子耸棒,更是在濱河造成了極大的恐慌朵夏,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榆纽,死亡現(xiàn)場離奇詭異仰猖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)奈籽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門饥侵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人衣屏,你說我怎么就攤上這事±浅溃” “怎么了钻弄?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵窘俺,是天一觀的道長。 經(jīng)常有香客問我灶泵,道長对途,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任惶洲,我火速辦了婚禮,結(jié)果婚禮上儒喊,老公的妹妹穿的比我還像新娘币呵。我一直安慰自己余赢,他們只是感情好哈垢,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布耘分。 她就那樣靜靜地躺著,像睡著了一般央渣。 火紅的嫁衣襯著肌膚如雪渴频。 梳的紋絲不亂的頭發(fā)上卜朗,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天场钉,我揣著相機(jī)與錄音,去河邊找鬼春叫。 笑死泣港,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呛每。 我是一名探鬼主播坡氯,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼啥供!你這毒婦竟也來了库糠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤贷屎,失蹤者是張志新(化名)和其女友劉穎唉侄,沒想到半個(gè)月后野建,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體候生,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肿孵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晤愧,死狀恐怖官份,靈堂內(nèi)的尸體忽然破棺而出烙丛,到底是詐尸還是另有隱情,我是刑警寧澤河咽,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布飒房,位于F島的核電站,受9級特大地震影響狠毯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嫡良,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一皆刺、第九天 我趴在偏房一處隱蔽的房頂上張望凌摄。 院中可真熱鬧漓帅,春花似錦、人聲如沸器予。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至雷则,卻和暖如春月劈,著一層夾襖步出監(jiān)牢的瞬間藤乙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工而姐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罚勾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓划煮,卻偏偏與公主長得像缔俄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子俐载,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 1.什么是 Git Hooks 如同其他許多的版本控制系統(tǒng)一樣挖炬,Git 也具有在特定事件發(fā)生之前或之后執(zhí)行特定腳本...
    就叫yang閱讀 3,258評論 3 11
  • 一般請求一個(gè)頁面時(shí)意敛,只需要通過$this->load-view('test')去加載相應(yīng)的視圖就行了草姻,對于具體怎么...
    鈍感165閱讀 339評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理撩独,服務(wù)發(fā)現(xiàn)账月,斷路器,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,778評論 6 342
  • 我是2015年開始學(xué)習(xí)D3.js,這幾年也經(jīng)常寫D3的代碼担平,想給大家分享一下我D3學(xué)習(xí)的過程及用到的資源锭部。...
    廣陵嘯_e613閱讀 2,296評論 0 6