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
*
* @access private
* @return void
*/
function _initialize()
{
$CFG =& load_class('Config', 'core'); // If hooks are not enabled in the config file
// there is nothing else to do if ($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
*
* @access private
* @param string the hook name
* @return mixed
*/
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
*
* @access private
* @param array the hook details
* @return bool
*/
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 happen if ($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 function
if ($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的更多相关文章

  1. CI框架源代码阅读笔记3 全局函数Common.php

    从本篇開始.将深入CI框架的内部.一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说.全局函数具有最高的载入优先权.因此大多数的框架中BootStrap ...

  2. CI框架源代码阅读笔记5 基准測试 BenchMark.php

    上一篇博客(CI框架源代码阅读笔记4 引导文件CodeIgniter.php)中.我们已经看到:CI中核心流程的核心功能都是由不同的组件来完毕的.这些组件类似于一个一个单独的模块,不同的模块完毕不同的 ...

  3. CI框架源代码阅读笔记2 一切的入口 index.php

    上一节(CI框架源代码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程.这里再次贴出流程图.以备參考: 作为CI框架的入口文件.源代码阅读,自然由此開始. 在源代码阅读的 ...

  4. CI框架源码阅读笔记6 扩展钩子 Hook.php

    CI框架允许你在不修改系统核心代码的基础上添加或者更改系统的核心功能(如重写缓存.输出等).例如,在系统开启hook的条件下(config.php中$config['enable_hooks'] = ...

  5. CI框架源代码阅读笔记7 配置管理组件 Config.php

    原文见这里:http://www.cnblogs.com/ohmygirl/p/CIRead-7.html 一个灵活可控的应用程序中,必定会存在大量的可控參数(我们称为配置),比如在CI的主配置文件里 ...

  6. Mongodb源代码阅读笔记:Journal机制

    Mongodb源代码阅读笔记:Journal机制 Mongodb源代码阅读笔记:Journal机制 涉及的文件 一些说明 PREPLOGBUFFER WRITETOJOURNAL WRITETODAT ...

  7. Spark源代码阅读笔记之DiskStore

    Spark源代码阅读笔记之DiskStore BlockManager底层通过BlockStore来对数据进行实际的存储.BlockStore是一个抽象类,有三种实现:DiskStore(磁盘级别的持 ...

  8. 框架Thinkphp5 简单的实现行为 钩子 Hook

    这篇文章主要介绍了关于框架Thinkphp5 简单的实现行为 钩子 Hook,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 实现在一个方法开始和结束加入两个行为:api_init.ap ...

  9. Java Jdk1.8 HashMap源代码阅读笔记二

    三.源代码阅读 3.元素包括containsKey(Object key) /** * Returns <tt>true</tt> if this map contains a ...

随机推荐

  1. hpuoj--校赛--面试难题(区间相交问题)

    问题 F: 感恩节KK专场--面试难题 时间限制: 1 Sec  内存限制: 128 MB 提交: 294  解决: 39 [提交][状态][讨论版] 题目描述 有n个人要来面试学生会XX部门,要求面 ...

  2. Python 标准库 —— 队列(Queue,优先队列 PriorityQueue)

    优先队列,有别于普通队列的先入先出(虽然字面上还是队列,但其实无论从含义还是实现上,和普通队列都有很大的区别),也有别于栈的先入后出.在实现上,它一般通过堆这一数据结构,而堆其实是一种完全二叉树,它会 ...

  3. Metasploit的攻击实例讲解----ms10_046快捷方式图标漏洞

    不多说,直接上干货! 准备工具 1.Kali linux 2016.2(Rolling)系统  IP:  192.168.1.103 2.受害者机子(windows XP系统)   IP: 10.10 ...

  4. checkbox和文字不再同一水平线上

    为了演示效果,我故意将文字变为12px,将复选框变大,看到的效果就是下面的那样 然后,我们通过给复选框添加vertical-align:middle:让文字和复选框达到同一水平线的效果

  5. gym 100971 J Robots at Warehouse

    Vitaly works at the warehouse. The warehouse can be represented as a grid of n × m cells, each of wh ...

  6. 2018年湘潭大学程序设计竞赛 Fibonacci进制

    Fibonacci数是非常有名的一个数列,它的公式为 f(n)=f(n-1)+f(n-2),f(0)=1,f(1)=2.  我们可以把任意一个数x表示成若干不相同的Fibonacci数的和, 比如说1 ...

  7. NOIP 2017 逛公园 记忆化搜索 最短路 好题

    题目描述: 策策同学特别喜欢逛公园.公园可以看成一张N个点MM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. ...

  8. perl异常处理

    程序脚本在运行过程中,总会碰到这样那样的问题,我们会预知一些问题并为其准备好处理代码,而有一些不能预知.好的程序要能尽可能多的处理可能出现的异常问题,本文就总结了一些方法来解决这些异常,当然perl在 ...

  9. python常用函数库收集。

    学习过Python都知道python中有很多库.python本身就是万能胶水,众多强大的库/模块正是它的优势. 收集一些Python常用的函数库,方便大家选择要学习的库,也方便自己学习收集,熟悉运用好 ...

  10. mod_php模式原理探析

    1.PHP与Apache工作模式 在传统的LAMP架构中,PHP与Apache交互时,至少有两种方式『运行PHP』: 使用CGI:Apache发送请求至php-cgi进程,php-cgi进程调用PHP ...