浅谈tp的插件(钩子)实现机制

Crasph彬 315 0

      现在主流的cms或者blog等系统中,都内置的有插件系统,但是层层深入、剖析实现的方式,其实都是最简单的钩子的复杂化的实现。php中所谓的钩子,其实就是一种事件驱动,主要分为‘注册事件’、‘触发事件’两步。所谓‘注册事件’,即目的是给未来可能发生的'事情'起一个名字,名字,可以用单例模式或者注册 为一个全局的变量,用的时候直接在对应的方法或者类再或者函数中插入这个变量即可;‘触发事件’,本质上就是在事件的全局变量中查询要触发的时间名称,然后找到注册号的类与方法,实例化运行。
举个例子来说明一下。
项目经理给我们了如下的需求:
第一天:开发注册的功能。
程序员巴拉巴拉,三下五除二就完成了
第二天:在注册前添加发送短信验证码的功能。
程序员巴拉巴拉,三峡五除二就又完成了
第三天:注册完成之后,给用户添加相应的积分。
程序员又开始巴拉巴拉ing……

class Register{
    public function index(){
        /**
         * 第二天发送短信功能
         */
        
        /**
         * 第一天注册代码
         */
        
        /**
         * 第三天增加积分功能 
         */
    }
}

    这样一个人开发还好,多个人开发,势必会造成配合麻烦的问题,同时代码也会变得混乱。

   作为优秀程序员的我们,当然不容许我们程序中代码冗余、混乱的出现,于是我们把方法写成函数独立出来,方便调用与代码简介。于是形成如下代码:

class Register{
    public function index(){
        /**
         * 第二天发送短信功能
         */
         sendMsg($data);
        /**
         * 第一天注册代码
         */
        
        /**
         * 第三天增加积分功能 
         */
        sendIntegral($data);
    }
}
/**
 * 发送短信
 * @param  {[type]} $data [description]
 * @return {[type]}       [description]
 */
function sendMsg($data){
    /*
            balabala
     */
}
/**
 * 赠送积分
 * @param  {[type]} $data [description]
 * @return {[type]}       [description]
 */
function sendIntegral($data){
    /*
            balabala
     */
}

      但是我们想要把程序开元出去让更多的人参与,这种直接修改源码码的方式始终不是太好,这个时候,我们就可以使用钩子的方式,在注册成功前后注册两个钩子,我们只需要把钩子告诉开发人员就行了,这样他们不用改变源码码就可以轻易的进行拓展。

class Register{
    public function index(){
        
        //注册前钩子
        Hook::run('registerBefore');
        /**
         * 注册代码
         */
        
        //注册后钩子
        Hook::run('registerAfter');
    }
}

下边是实现钩子的简单实现代码。

目录结构如下:

微信图片_20181220171156.png

钩子核心类Hook.php

<?php
/**
 * @Author: CraspHB彬
 * @Date:   2018-12-20 11:39:53
 * @Email:   646054215@qq.com
 * @Last Modified time: 2018-12-20 17:06:58
 */
namespace hook;
class Hook{
    
    static protected $hook = [];
    
    /**
     * 插件注册
     * @param [type] $name   [description]
     * @param [type] $addons [description]
     */
    static public function add($name,$addons){
        self::$hook[$name] = $addons;
    }
    /**
     * 插件执行
     * @param  [type] $name [description]
     * @return [type]       [description]
     */
    static public function run($name){
        if(isset(self::$hook[$name])){
            $method = (new self::$hook[$name]());
               call_user_func([$method,$name]);
        }
        
    }
}

简单的插件demo

<?php
/**
 * @Author: CraspHB彬
 * @Date:   2018-12-20 11:49:57
 * @Email:   646054215@qq.com
 * @Last Modified time: 2018-12-20 17:05:32
 */
namespace addons\demo;
class Demo{
    public function registerBefore(){
        echo 'registerBefore'.'</br>';
    }
    public function registerAfter(){
        echo 'registerAfter'.'</br>';
    }    
}

插件实现的地方,即上文的注册的文件(此处用的为index而非register,主要是懒得改了):

<?php
/**
 * @Author: CraspHB彬
 * @Date:   2018-12-20 11:26:44
 * @Email:   646054215@qq.com
 * @Last Modified time: 2018-12-20 17:05:39
 */
namespace index\controller;
use hook\Hook;
class Index{
    public function index(){
        Hook::run('registerBefore');
        echo '注册完成'.'</br>';
        Hook::run('registerAfter');
        
    }
}

入口文件index.php

<?php
/**
 * @Author: CraspHB彬
 * @Date:   2018-12-20 11:27:46
 * @Email:   646054215@qq.com
 * @Last Modified time: 2018-12-20 17:03:36
 */
use index\controller\Index;
spl_autoload_register('autoload');
function autoload($name){
    require_once('/'.str_replace('\\','/',$name).'.php');
}
//插件注册
hook\Hook::add('registerBefore','\\addons\\demo\\Demo');
hook\Hook::add('registerAfter','\\addons\\demo\\Demo');
//调用
$index = new Index();
$index->index();

运行接口如下:

registerBefore
注册完成
registerAfter

如果要添加新的功能,程序员只要修改demo的插件就可,如果要拓展新的功能,只需要拓展registerBeforeregisterAfter即可。

长时间不写文章,以前的写作功底完全丧失,还是要多多锻炼啊。

支付宝打赏
微信打赏
发表评论
表情 图片 链接 代码

分享
微信
微博
QQ