Interpretation of CodeIgniter source hook

And the principle of using hooks

CI like Laravel, TP as middleware provides similar functionality to support the operation before and after the implementation of the controller to perform, CI image compared it to the hook (hooks). This, we will first write an example of using hooks, then go read the source code to understand his use.

  1. Open hook (application / config.php)
/*
|--------------------------------------------------------------------------
| Enable/Disable System Hooks
|--------------------------------------------------------------------------
|
| If you would like to use the 'hooks' feature you must enable it by
| setting this variable to TRUE (boolean).  See the user guide for details.
|
*/
$config['enable_hooks'] = TRUE;# FALSE;
  1. New hook (application \ hooks \ Test_hook.php)
/**
 ** 自定义的钩子函数
 */
class Test_hook {

	public function say()
	{
		return 'i am a method say of Test_hook !';
	}
}
  1. Registration hook
    First, we can see the hook in the official website there are several triggering occasion, we come to understand the next
pre_system 在系统执行的早期调用,这个时候只有 基准测试类 和 钩子类 被加载了, 还没有执行到路由或其他的流程。
pre_controller 在你的控制器调用之前执行,所有的基础类都已加载,路由和安全检查也已经完成。
post_controller_constructor 在你的控制器实例化之后立即执行,控制器的任何方法都还尚未调用。
post_controller 在你的控制器完全运行结束时执行。
display_override 覆盖 _display() 方法,该方法用于在系统执行结束时向浏览器发送最终的页面结果。 这可以让你有自己的显示页面的方法。注意你可能需要使用 $this->CI =& get_instance() 方法来获取 CI 超级对象,以及使用 $this->CI->output->get_output() 方法来 获取最终的显示数据。
cache_override 使用你自己的方法来替代 输出类 中的 _display_cache() 方法,这让你有自己的缓存显示机制。
post_system 在最终的页面发送到浏览器之后、在系统的最后期被调用。

We first registered for use pre_controller hook point, the program is first run up

$hook['pre_controller'] = array(
    'class'    => 'Test_hook',
    'function' => 'say',
    'filename' => 'Test_hook.php',
    'filepath' => 'hooks',
    'params'   => array()
);

We then visit the destination address in the browser, get
After the visit, get results
ok, the hook has been successfully run up. Then, we went to look at the code hooks to achieve, we found a loaded Hooks class code in CodeIgniter.php file 200 Line, and then, immediately, it calls the method call_hook Hooks class, to try to hook point whether the load pre_system presence:

/*
 * ------------------------------------------------------
 *  Instantiate the hooks class
 * ------------------------------------------------------
 */
	$EXT =& load_class('Hooks', 'core');

/*
 * ------------------------------------------------------
 *  Is there a "pre_system" hook?
 * ------------------------------------------------------
 */
	$EXT->call_hook('pre_system');

Hook into our class to see what, decisive look at the constructor:

public function __construct()
{
	# 加载配置类
	$CFG =& load_class('Config', 'core');
	log_message('info', 'Hooks Class Initialized');

	// If hooks are not enabled in the config file
	// there is nothing else to do
	# 判断是否开启了 钩子
	if ($CFG->item('enable_hooks') === FALSE)
	{
		return;
	}

	// Grab the "hooks" definition file.
	# 判断配置钩子的文件是否存在,存在则引入
	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 there are no hooks, we're done.
	if ( ! isset($hook) OR ! is_array($hook))
	{
		return;
	}
	
	# $hook 数组赋值
	$this->hooks =& $hook;
	# 开启钩子标识
	$this->enabled = TRUE;
}

We can see that the constructor in a state of open hook, hook loaded configuration array, then our next class call_hook function hooks in the study:

# 方法接受一个参数,就是钩子的名字,也就是支持的那几个,如:pre_system,pre_controller...
public function call_hook($which = '')
{
	# 首先判断是否开启钩子
	if ( ! $this->enabled OR ! isset($this->hooks[$which]))
	{
		return FALSE;
	}
	
	# 钩子配置数组存在且 function 配置项为空
	if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))
	{
		# 这里为啥会是遍历钩子呢?
		# 这是因为假如一个钩子 $hooks[pre_controller][] 他可能是多个钩子
		foreach ($this->hooks[$which] as $val)
		{
			# 看啥看,还不赶紧去 _run_hook()
			$this->_run_hook($val);
		}
	}
	else
	{
		$this->_run_hook($this->hooks[$which]);
	}

	return TRUE;
}
protected function _run_hook($data)
{
	// Closures/lambda functions and array($object, 'method') callables
	# 闭包函数调用
	if (is_callable($data))
	{
		is_array($data)
			? $data[0]->{$data[1]}()
			: $data();

		return TRUE;
	}
	elseif ( ! is_array($data))
	{
		return FALSE;
	}

	// -----------------------------------
	// Safety - Prevents run-away loops
	// -----------------------------------

	// If the script being called happens to have the same
	// hook call within it a loop can happen
	# 看注释罗,不解释罗
	if ($this->_in_progress === TRUE)
	{
		return;
	}

	// -----------------------------------
	// Set file path
	// -----------------------------------
	# 判断 路径 和 文件名 是否存在
	if ( ! isset($data['filepath'], $data['filename']))
	{
		return FALSE;
	}
	# 拼接钩子实现类(Test_hook)文件路径
	$filepath = APPPATH.$data['filepath'].'/'.$data['filename'];

	if ( ! file_exists($filepath))
	{
		return FALSE;
	}

	// Determine and class and/or function names
	# 获取 类名、方法名、传参
	$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;
	}

	// Set the _in_progress flag
	$this->_in_progress = TRUE;

	// Call the requested class and/or function
	if ($class !== FALSE)
	{
		// The object is stored?
		if (isset($this->_objects[$class]))
		{
			if (method_exists($this->_objects[$class], $function))
			{
				# 执行方法
				$this->_objects[$class]->$function($params);
			}
			else
			{
				return $this->_in_progress = FALSE;
			}
		}
		else
		{
			class_exists($class, FALSE) OR require_once($filepath);

			if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function))
			{
				return $this->_in_progress = FALSE;
			}

			// Store the object and execute the method
			# 保存已实例化的钩子类,避免重复实例化
			$this->_objects[$class] = new $class();
			# 执行钩子函数
			$this->_objects[$class]->$function($params);
		}
	}
	else
	{
		function_exists($function) OR require_once($filepath);

		if ( ! function_exists($function))
		{
			return $this->_in_progress = FALSE;
		}
		# 类名为空时,直接调用方法,该方法可能是 Common.php 文件定义的,
		# 或者必须在加载钩子之前加载
		$function($params);
	}

	$this->_in_progress = FALSE;
	return TRUE;
}

Realization of the entire hook, everyone is familiar with it, is actually very simple, we have serious shoes, you should be able to find CodeIgniter.php also not lacking there is such a statement:

/*
 * ------------------------------------------------------
 *  Is there a "pre_controller" hook?
 * ------------------------------------------------------
 */
	$EXT->call_hook('pre_controller');

/*
 * ------------------------------------------------------
 *  Is there a "post_controller" hook?
 * ------------------------------------------------------
 */
	$EXT->call_hook('post_controller');
/*
 * ------------------------------------------------------
 *  Send the final rendered output to the browser
 * ------------------------------------------------------
 */
	if ($EXT->call_hook('display_override') === FALSE)
	{
		$OUT->_display();
	}

/*
 * ------------------------------------------------------
 *  Is there a "post_system" hook?
 * ------------------------------------------------------
 */
	$EXT->call_hook('post_system');

ok, interested in children's shoes, you can change the type of hook, to study under the bar on this issue ~ ~ Speaking of which, we next goodbye! !

Released eight original articles · won praise 0 · Views 124

Guess you like

Origin blog.csdn.net/weixin_43950095/article/details/104646260