Home > Article > Backend Development > CI framework source code reading notes 6 Extension hook Hook.php, cihook.php_PHP tutorial
The CI framework allows you to add or change the system without modifying the core code of the system. core functions (such as rewriting cache, output, etc.). For example, if hooks are enabled in the system ($config['enable_hooks'] = TRUE;
in config.php), by adding specific hooks, the system can trigger specific scripts at specific times:
<span>$hook</span>['post_system'] = <span>array</span><span>( </span>'class' => 'frameLog', 'function' => 'postLog', 'filename' => 'post_system.php', 'filepath' => 'hooks',<span> );</span>
The above hook defines a post_system hook, which is used for script processing after the final page rendering (the meaning of the parameters can be referred to later or in the manual, and no further explanation will be given here for the time being).
Then the question is:
Let’s look at it step by step.
The definition of hook on Baidu Encyclopedia is:
<p><span>钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。</span></p>
We can see several points from the above definition:
CI provides 7 available preset hook points, which are:
pre_system: refers to the hook in the early stage of system loading
pre_controller: The hook before calling the controller, routing and security checks have been completed
post_controller_constructor: After the controller is instantiated and before any method is called
post_controller: After the controller is fully running
display_override: override display
cache_override: Rewrite cache
post_system: After the final page is sent to the client
The core function of hooks in CI is completed by the Hook component. Let’s look at the class diagram of this component first:
Among them:
enabled: Flag indicating whether the hook function is enabled.
Hooks: Save the list of hooks enabled in the system
in_progress: We will see later that this flag is used to prevent infinite loops caused by mutual calls between hooks.
_construct is the constructor of the Hook component, which calls _initialize to complete the initialization work
_call_hook: Call _run_hook to call the specified hook program. We have seen before in CodeIgniter.php that _call_hook is the interface actually provided for external calls.
_run_hook: The function that actually executes the hook program
Before we start, let’s post the structure of the predefined hook. This structure may appear throughout the source code, so we need to know the meaning of the parameters of this structure.
<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. Hook component initialization
_initialize function is used to initialize the hook component. The main tasks of this function are:
(1) Check whether the hook function in the configuration file is enabled, which requires loading Config (configuration management component):
$CFG =& load_class('Config', 'core'); if ($CFG->item('enable_hooks') == FALSE) { return; }
(2) Load the defined hook list
Similarly, you can set different ENVIRONMENT to enable different hooks. If there are any, the hooks under ENVRIONMENT will be loaded first:
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 inspection. If no hook is set, or the hook format is wrong, no processing will be done and you will exit directly:
if ( ! isset($hook) OR ! is_array($hook)) { return; }
After initialize, the defined hook list is stored in Hook::hooks:
$this->hooks =& $hook;
2. Call specified hook
_call_hook is the interface directly called in the main program. The main tasks of this interface are:
(1). Check whether the hook is enabled and whether the call hook is predefined (if it is not enabled or the call hook does not exist, it will return directly):
if ( ! $this->enabled OR ! isset($this->hooks[$which])) { return FALSE; }
(2). Check whether multiple hooks are enabled on the same hook point. If so, execute them in sequence:
if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0])) { foreach ($this->hooks[$which] as $val) { $this->_run_hook($val); } }
(3). Otherwise, there is only one hook, execute it
else { $this->_run_hook($this->hooks[$which]); }
_run_hook is the function that actually executes the hook.
3. run to execute a specific hook program
The_run_hook function is the actual executor of hook. This function receives a predefined hook array as a parameter and is implemented as follows:
(1). If the parameter passed is not an array at all (naturally it is not a valid hook), then return directly:
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没啥方法,倒是可以通过写模板的时候include进去header.php和footer.php,倒是还有听说smarty模板引擎中有模板继承这个概念,可以让你的内容页继承某个页面,那个页面上写着header.php和footer.php,貌似CI是可以使用smarty模板引擎的,不过我没有那样用过,还有,CI有hook(钩子)这个东西,老实说我没用过,不知道它能不能实现。
是记录有多少人进去你的网站还是说点击的某个连接多少次?