目录
CI框架源码阅读笔记6 扩展钩子 Hook.php,cihook.php
1.  钩子是什么
2.  CI中预定义钩子
3.  CI中钩子的实现
参考文献
CI框架引入模板问题
CI框架 怎实现统计网站点击量,哪位把源码发我跪
首页 php教程 php手册 CI框架源码阅读笔记6 扩展钩子 Hook.php,cihook.php

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

Jun 13, 2016 am 09:21 AM
hook 扩展 框架 源码 笔记 钩子 阅读

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

  CI框架允许你在不修改系统核心代码的基础上添加或者更改系统的核心功能(如重写缓存、输出等)。例如,在系统开启hook的条件下(config.php中$config['enable_hooks'] = TRUE;),通过添加特定的钩子,可以让系统在特定的时刻触发特定的脚本:

<span>$hook</span>['post_system'] = <span>array</span><span>(
    </span>'class'     => 'frameLog',
    'function'  => 'postLog',
    'filename'  => 'post_system.php',
    'filepath'   => 'hooks',<span>
);</span>
登录后复制

上述钩子定义了一个post_system的钩子,用于在最终的页面渲染之后的脚本处理(参数的含义可以参考后面或者手册,这里暂时不做更多解释)。

那么问题来了:

我们一步步来看。

1.  钩子是什么

  百度百科上对于钩子的定义是:

<p><span>钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。</span></p>
登录后复制

  从上述定义我们可以看出几点:

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: 实际执行钩子程序的函数

在开始之前,我们先贴出预定义钩子的结构。这个结构可能会贯穿在源代码的始终,因而我们有必要知道该结构的参数含义。

<span>$hook</span>['xx'] = <span>array</span><span>(
    </span>'class'     => 'xx', <span>//</span><span>钩子调用的类名,可以为空</span>
    'function'  => 'xx',<span>//</span><span>钩子调用的函数名</span>
    'filename'  => 'xx',<span>//</span><span>该钩子的文件名</span>
    'filepath'   => 'xx',<span>//</span><span>钩子的目录</span>
    'params'   => 'xx'<span>//</span><span>传递给钩子的参数</span>
);
登录后复制
登录后复制

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需要的参数:

<span>$hook</span>['xx'] = <span>array</span><span>(
    </span>'class'     => 'xx', <span>//</span><span>钩子调用的类名,可以为空</span>
    'function'  => 'xx',<span>//</span><span>钩子调用的函数名</span>
    'filename'  => 'xx',<span>//</span><span>该钩子的文件名</span>
    'filepath'   => 'xx',<span>//</span><span>钩子的目录</span>
    'params'   => 'xx'<span>//</span><span>传递给钩子的参数</span>
);
登录后复制
登录后复制

其中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组件的完整源码:

_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框架引入模板问题

这个……好像CI没啥方法,倒是可以通过写模板的时候include进去header.php和footer.php,倒是还有听说smarty模板引擎中有模板继承这个概念,可以让你的内容页继承某个页面,那个页面上写着header.php和footer.php,貌似CI是可以使用smarty模板引擎的,不过我没有那样用过,还有,CI有hook(钩子)这个东西,老实说我没用过,不知道它能不能实现。
 

CI框架 怎实现统计网站点击量,哪位把源码发我跪

是记录有多少人进去你的网站还是说点击的某个连接多少次?
 

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何评估Java框架商业支持的性价比 如何评估Java框架商业支持的性价比 Jun 05, 2024 pm 05:25 PM

评估Java框架商业支持的性价比涉及以下步骤:确定所需的保障级别和服务水平协议(SLA)保证。研究支持团队的经验和专业知识。考虑附加服务,如升级、故障排除和性能优化。权衡商业支持成本与风险缓解和提高效率。

PHP 框架的学习曲线与其他语言框架相比如何? PHP 框架的学习曲线与其他语言框架相比如何? Jun 06, 2024 pm 12:41 PM

PHP框架的学习曲线取决于语言熟练度、框架复杂性、文档质量和社区支持。与Python框架相比,PHP框架的学习曲线更高,而与Ruby框架相比,则较低。与Java框架相比,PHP框架的学习曲线中等,但入门时间较短。

PHP 框架的轻量级选项如何影响应用程序性能? PHP 框架的轻量级选项如何影响应用程序性能? Jun 06, 2024 am 10:53 AM

轻量级PHP框架通过小体积和低资源消耗提升应用程序性能。其特点包括:体积小,启动快,内存占用低提升响应速度和吞吐量,降低资源消耗实战案例:SlimFramework创建RESTAPI,仅500KB,高响应性、高吞吐量

Java框架的性能比较 Java框架的性能比较 Jun 04, 2024 pm 03:56 PM

根据基准测试,对于小型、高性能应用程序,Quarkus(快速启动、低内存)或Micronaut(TechEmpower优异)是理想选择。SpringBoot适用于大型、全栈应用程序,但启动时间和内存占用稍慢。

golang框架文档最佳实践 golang框架文档最佳实践 Jun 04, 2024 pm 05:00 PM

编写清晰全面的文档对于Golang框架至关重要。最佳实践包括:遵循既定文档风格,例如Google的Go编码风格指南。使用清晰的组织结构,包括标题、子标题和列表,并提供导航。提供全面准确的信息,包括入门指南、API参考和概念。使用代码示例说明概念和使用方法。保持文档更新,跟踪更改并记录新功能。提供支持和社区资源,例如GitHub问题和论坛。创建实际案例,如API文档。

如何为不同的应用场景选择最佳的golang框架 如何为不同的应用场景选择最佳的golang框架 Jun 05, 2024 pm 04:05 PM

根据应用场景选择最佳Go框架:考虑应用类型、语言特性、性能需求、生态系统。常见Go框架:Gin(Web应用)、Echo(Web服务)、Fiber(高吞吐量)、gorm(ORM)、fasthttp(速度)。实战案例:构建RESTAPI(Fiber),与数据库交互(gorm)。选择框架:性能关键选fasthttp,灵活Web应用选Gin/Echo,数据库交互选gorm。

golang框架开发实战详解:问题答疑 golang框架开发实战详解:问题答疑 Jun 06, 2024 am 10:57 AM

在Go框架开发中,常见的挑战及其解决方案是:错误处理:利用errors包进行管理,并使用中间件集中处理错误。身份验证和授权:集成第三方库并创建自定义中间件来检查凭据。并发处理:利用goroutine、互斥锁和通道来控制资源访问。单元测试:使用gotest包,模拟和存根进行隔离,并使用代码覆盖率工具确保充分性。部署和监控:使用Docker容器打包部署,设置数据备份,通过日志记录和监控工具跟踪性能和错误。

Golang框架学习过程中常见的误区有哪些? Golang框架学习过程中常见的误区有哪些? Jun 05, 2024 pm 09:59 PM

Go框架学习的误区有以下5种:过度依赖框架,限制灵活性。不遵循框架约定,代码难维护。使用过时库,带来安全和兼容性问题。过度使用包,混淆代码结构。忽视错误处理,导致意外行为和崩溃。

See all articles