Hook技术(钩子函数)
hook钩子:
使用技术手段在运行时动态的将额外代码依附现进程,从而实现替换现有处理逻辑或插入额外功能的目的。
它的技术实现要点有两个:
1)如何注入代码(如何将额外代码依附于现有代码中)。
2)如何确定目标函数的地址及替换。
Hook类
<?php class Hook{ static private $events = array();// 存储事件注入的静态变量
// 执行函数
// 逻辑概述:执行注入事件指定的函数$action,参数$args
static public function apply($action,$args=array()) { if(!is_string($action)){ return; } if(strstr($action,'.')){ $arr = explode('.',$action); if(count($arr) !== 2){ return; } $className = $arr[0]; $functionName = $arr[1]; if(class_exists($className)){ $class = new $className(); if( method_exists($class,$functionName) ){ //return $class -> $functionName($args); return @call_user_func_array(array($class,$functionName), $args); } } }else{ if(function_exists($action)){ return @call_user_func_array($action, $args); } } } // 事件注入(绑定)
// 逻辑概述:在程序全局中注册一个事件,用$event标识,执行函数(或类.方法)$acion,出发一次标识$once
static public function bind($event,$action,$once=false) { if(!isset(self::$events[$event])){ self::$events[$event] = array(); } if(!is_array($action)){ $action = array($action); } for ($i=0; $i < count($action); $i++) { self::$events[$event][] = array( 'action' => $action[$i], 'once' => $once, 'times' => 0 ); } } // 解绑事件
static public function unbind($event) { self::$events[$event] = false; } // 触发执行事件
// 执行事件标识为$event的事件
static public function trigger($event) { $events = self::$events; if( !isset($events[$event]) ){ return; } $actions = $events[$event]; $result = false; if(is_array($actions) && count($actions) >= 1) { $args = func_get_args(); array_shift($args); for ($i=0; $i < count($actions); $i++) { $action = $actions[$i]; if( $action['once'] && $action['times'] > 1){ continue; } if(defined("GLOBAL_DEBUG_HOOK") && GLOBAL_DEBUG_HOOK){ write_log($event.'==>start: '.$action['action'],'hook-trigger'); } self::$events[$event][$i]['times'] = $action['times'] + 1; $res = self::apply($action['action'],$args); if(defined("GLOBAL_DEBUG_HOOK") && GLOBAL_DEBUG_HOOK){ write_log($event.'==>end['.$action['times'].']: '.$action['action'],'hook-trigger'); } //避免循环调用 if( $action['times'] >= 5000){ show_json("ERROR,Too many trigger on:".$event.'==>'.$action['action'],fasle); } if(!is_null($res)){ $result = $res; } } } return $result; } }
以上是类的实现逻辑。
在系统框架中应该要怎么实现呢?
1、在系统初始化时注册事件。入口文件代码:
<?php // 实例化系统 $app = new Application; // 初始化注册事件 Hook::bind('writelog', 'writelogfunc'); // 注册一个日志记录事件 // 运行 $app->run();
2、事件处理函数实现。代码:
<?php function writelogfunc($code=0,$msg='',$data=[]) {
file_put_content('/log.txt', json_encode(['code' => $code, 'msg' => $msg, 'data' => $data ]));
}
3、设置触发事件。
<?php try { // ... } catch(Exception $e) {
// 上面代码报错时触发 writelog 事件 对应函数 writelogfunc 参数$event,$code,$msg,$data
Hook::trigger('writelog', 400, $e->getMessage(), []);
}
重点函数:func_get_args, call_user_func_array

浙公网安备 33010602011771号