一个产品经常需要修改需求、增加功能,就需要频繁修改逻辑,多个人开发的话一个地点频繁操作可能还冲突、看着心烦~哈哈哈哈哈
所以,钩子监听这时候就很有趣了,在行为前后都可以加上需要的钩子监听,分工合作,废话少说,先上来一个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:
开发复杂点的钩子就支持复杂的应用开发了,比如我们系统的插件功能:

