一个产品经常需要修改需求、增加功能,就需要频繁修改逻辑,多个人开发的话一个地点频繁操作可能还冲突、看着心烦~哈哈哈哈哈
所以,钩子监听这时候就很有趣了,在行为前后都可以加上需要的钩子监听,分工合作,废话少说,先上来一个hook类(类似tp的简单版):
<?php /** * 钩子中心-核心部分 * Class Hook */ class Hook { //注册后保存下来 public static $hooks = []; //注册插件 public static function add($hook_name, $class) { $hook_name = strtolower($hook_name); if (isset(self::$hooks[$hook_name])) { self::$hooks[$hook_name] = array_merge(self::$hooks[$hook_name], $class); } else { self::$hooks[$hook_name][] = $class; } self::$hooks[$hook_name] = array_unique(self::$hooks[$hook_name]); } //批量导入 public static function batch_add($plugins = []) { if (!empty($plugins)) { //以新的key覆盖旧的hook self::$hooks = $plugins + self::$hooks; //去重 if (self::$hooks){ foreach (self::$hooks as $hook=>&$class){ $class = array_unique($class); } unset($class); } } } //获取插件值 public static function get($hook_name) { $hook_name = strtolower($hook_name); if (!isset(self::$hooks[$hook_name])) { return false; } return self::$hooks[$hook_name]; } //监听,返回一个数组结果 public static function listen($hook_name, &$param = null) { $hook_name = strtolower($hook_name); if (!self::get($hook_name)) { return false; } $result = []; foreach (self::get($hook_name) as $key => $class) { $result[$key] = static::exec($class, !empty($param['method']) ? $param['method'] : 'main', $param); if ($result[$key] === false) { break; } } return $result; } //引用模式,可以修改参数 public static function listen_p($hook_name, &$param = null) { $hook_name = strtolower($hook_name); if (!self::get($hook_name)) { return false; } $class = self::get($hook_name)[0]; return static::exec($class, !empty($param['method']) ? $param['method'] : 'main', $param); } //执行插件行为 public static function exec($class, $function = 'main', $param = null) { if (is_object($class)) { if (!is_callable([$class, $function])) { $ret = false; } else { $ret = $class->$function($param); } } else { if (!class_exists($class)){ return false; } $obj = new $class(); if (!is_callable([$class, $function])) { $ret = false; } else { $ret = $obj->$function($param); } } return $ret; } }
原理:其实很简单,就是先载入文件(最好是类,本案例用类做扩展),然后根据监听的参数实例化对象并执行。
所以,我写了个简单的加载目下xxx.php文件的文件loadfile.php:
<?php $dir = __DIR__.'/plugins/'; $files = scandir($dir); if (!empty($files)){ foreach ($files as $file){ if ($file=='.' || $file=='..'){ continue; } if (is_file(str_replace("\\",'/',$dir.$file))){ include_once str_replace("\\",'/',$dir.$file); } } }
新建plugins目录,新增Login.php:
<?php namespace hook\plugins; class Login { //默认执行的方法 public function main($param = []) { if (!empty($param['way'])) { if ($param['way'] == 'test') { return 'test'; } } return ''; } public function before($param = []) { echo 'This is before'.PHP_EOL; } public function after($param=[]) { echo 'This is after'.PHP_EOL; } }
在plugins目录新增Message.php类:
<?php namespace hook\plugins; class Message { public function main($params=[]) { if (!empty($params['call'])&&$params['call']=='sms'){ return $this->send_sms($params); }else{ //todo echo '这是消息钩子'.PHP_EOL; } } //发送短信 public function send_sms($param=null) { echo '开始发送短信啦'.PHP_EOL; return 'ok'; } }
上面两个文件作为测试文件写好了基本的测试函数,然后测试看看,新建index.php:
<?php //开启全报错 error_reporting(E_ALL); //引入文件 include_once __DIR__.'/loadfile.php'; //引入核心钩子类 include_once __DIR__.'/Hook.php'; //注册login钩子,监听\\hook\\plugins\\Login Hook::add('Login',"\\hook\\plugins\\Login"); //测试一下 $param = [ 'method'=>'before' ]; Hook::listen('login',$param); $param = [ 'method'=>'after' ]; Hook::listen('login',$param); //注册一个message钩子,监听一下\\hook\\plugins\\Message类 Hook::add('message',"\\hook\\plugins\\Message"); //默认监听,没有参数会执行main函数 Hook::listen('message'); //发送个短信吧 $param = [ 'call'=>'sms' ]; Hook::listen('message',$param);
demo的目录结构如下:
执行看看效果:
说明:一个钩子可以监听多个类,要实例化就先要确保被加载到解析器中,也就是先include或者require进来,然后才能根据你需要监听的类实例化,最后执行指定的方法。
如果系统有些固定的类需要监听,可以先定义一下plugin,然后初始化时批量注册到钩子,后面监听就可以了,参考tp添加了一个批量添加的简单方法在Hook.php:
//批量导入 public static function batch_add($plugins = []) { if (!empty($plugins)) { //以新的key覆盖旧的hook self::$hooks = $plugins + self::$hooks; //去重 if (self::$hooks){ foreach (self::$hooks as $hook=>&$class){ $class = array_unique($class); } unset($class); } } }
然后可以定义一下钩子数组:
$plugins = array ( 'init' => array ( 0=>'\\hook\\plugins\\Init' ), 'login' => array ( 0 => '\\hook\\plugins\\Login', ), 'message' => array ( 0 => '\\hook\\plugins\\Message', ), ); //批量添加注册 Hook::batch_add($plugins); //测试监听 Hook::listen('init');//在demo附件有哦
附件demo:
开发复杂点的钩子就支持复杂的应用开发了,比如我们系统的插件功能: