CI框架源代码阅读笔记6 扩展钩子 Hook.php
Posted blfbuaa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CI框架源代码阅读笔记6 扩展钩子 Hook.php相关的知识,希望对你有一定的参考价值。
比如,在系统开启hook的条件下(config.php中$config[\'enable_hooks\'] = TRUE;
)。通过加入特定的钩子,能够让系统在特定的时刻触发特定的脚本:
$hook[\'post_system\'] = array( \'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:终于的页面发送到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组件的完整源代码:
參考文献
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的主要内容,如果未能解决你的问题,请参考以下文章