php hook编程机制

说明

    hook,中文翻译为钩子,编程中的钩子相似咱们现实中的钩子,须要挂在东西的时候
    直接挂载到上面便可。程序中也是,须要运行的代码挂载到上面便可。
     
    具体思想就是:在项目代码中,你认为要扩展(暂时不扩展)的地方放置一个钩子函数,
    等须要扩展的时候,把须要实现的类和函数挂载到这个钩子上,就能够实现扩展了。php

实例

1,需求背景

1,产品刚开始提了一个需求,很简单,就是获取一个一维数组,再将数组打印出来

    代码以下实现便可
     
    <?php
    class printArr
    {
        public function main()
        {
            //获取数组
            $arr = $this->get_arr();
            //打印数组
            $this->print_arr($arr);
        }
        public function get_arr()
        {
            $arr = [9,5,6,0,1,4,7,34,67,28,105,278];
            return $arr;
        }
        public function print_arr($arr)
        {
            echo "<pre>";
            print_r($arr);
        }
    }
     
    $printArrObj = new printArr;
    $printArrObj->main();

2,好吧,功能完成后,产品以为这样输出不太好,最好是有序输出。熟悉冒泡排序的你,赶忙对代码作了以下调整(以下虚线内代码)

    <?php
    class printArr
    {
        public function main()
        {
            //获取数组
            $arr = $this->get_arr();
            //----------------------------------------------------
            $arr = $this->bubble_sort($arr);
            //----------------------------------------------------
            //打印数组
            $this->print_arr($arr);
        }
        public function get_arr()
        {
            $arr = [9,5,6,0,1,4,7,34,67,28,105,278];
            return $arr;
        }
        public function print_arr($arr)
        {
            echo "<pre>";
            print_r($arr);
        }
        //----------------------------------------------------
        public function bubble_sort($arr)
        {
            //自行完成
        }
        //----------------------------------------------------
    }
     
    $printArrObj = new printArr;
    $printArrObj->main();

3,好吧,你的功能完成了。可是后来数据量变大了,产品说冒泡排序运行效率过低,让你用插入排序法排序。

    你怕无理的产品后面又换回冒泡,因而注释掉冒泡,又写了插入排序法
     
    <?php
    class printArr
    {
        public function main()
        {
            //获取数组
            $arr = $this->get_arr();
            //----------------------------------------------------
            //$arr = $this->bubble_sort($arr);
            $arr = $this->insert_sort($arr);
            //----------------------------------------------------
            //打印数组
            $this->print_arr($arr);
        }
        public function get_arr()
        {
            $arr = [9,5,6,0,1,4,7,34,67,28,105,278];
            return $arr;
        }
        public function print_arr($arr)
        {
            echo "<pre>";
            print_r($arr);
        }
        //----------------------------------------------------
        /*public function bubble_sort($arr)
        {
            //自行完成
        }*/
        public function insert_sort($arr)
        {
            //自行完成
        }
        //----------------------------------------------------
    }

4,嗯,后面项目愈来愈复杂,产品愈来愈变态。说4,这个数字很差,数组中带4的unset掉。和上面代码同样,你又写了一个方法,夹在数组的获取和输出的中间,用来unset掉4这个数字。

    好吧,对你来讲就价格方法而已,那就加呗
     
    <?php
    class printArr
    {
        public function main()
        {
            //获取数组
            $arr = $this->get_arr();
            //----------------------------------------------------
            //$arr = $this->bubble_sort($arr);
            $arr = $this->insert_sort($arr);
            $arr = $this->unset_4($arr);
            //----------------------------------------------------
            //打印数组
            $this->print_arr($arr);
        }
        public function get_arr()
        {
            $arr = [9,5,6,0,1,4,7,34,67,28,105,278];
            return $arr;
        }
        public function print_arr($arr)
        {
            echo "<pre>";
            print_r($arr);
        }
        //----------------------------------------------------
        /*public function bubble_sort($arr)
        {
            //自行完成
        }*/
        public function insert_sort($arr)
        {
            //自行完成
        }
        public function unset_4($arr)
        {
            //自行完成
        }
        //----------------------------------------------------
    }

5,这个产品愈来愈变态,说3很差,也要unset掉,5很差也要unset掉。8这个数字好,给数组中加个8,又说666这个数字更好,要加个666。而且半个小时要作完。你终于受不了了,提议你一我的作不完。产品经理二话不说调来好几我的,每人作一个。

    1,这里就要说出现的问题了。每一个工程师的习惯都不同
        好比你封装了方法,写在了输出数组这个主类的下面,main函数中直接调用了
        小王同事不喜欢封装方法,直接在获取数组和打印数组之间添加了代码段。
        小李同事以为输出数组当前这个类,为主类,处理过程本身封装一个副类,副类中封装方法,在输出数组这个类引入调用便可。
        等等等等方案。
        
    2,而后产品更多的变态需求出来了,大家团队也默默的维护着输出数组中间的每一道工序。就这样不知不觉两年过去了,
        
        忽然线上出现了一个小bug,你做为技术大牛,排错交给了你,
        
        接下来你开始排错
        
        封装方法的工程师,写了500多个方法在下面,虽然难找不过耐心点还能够慢慢找到维护排错。
        封装副类的工程师,封装了100多个副类,各种下都有各类方法,再耐心点吧,慢慢找还能够维护排错。
        直接加代码段的工程师,两年积累了五六千行代码,好吧,你须要读懂每一行才能发现问题,
        
        到了这个时候,我猜你直接想骂娘了吧,气冲冲的找来加代码段的工程师,让他看看这段代码
        什么意思,结果呢,两年前的代码,他本身都不知道什么意思了,这下完了,你是否是心态崩
        了,你是否是绝望了,你是否是直接想离职了?

6,如今想一想,大家公司内部两年积累的代码已经能让你到了崩溃的边缘,那若是开源代码,天下人都能维护,而且维护了十多年,若是按照上面维护的路子来维护,那这个开源软件早都嗝屁了吧。

故事讲到这里,就到此为止。hook编程机制就是解决此类问题应运而生的。

2,解决方案

    对,解决上面的问题,从三个方向入手。
        1,对应产品提出不一样需求改动,用插件的形式整齐划一的放在对应插件库里。
        2,插件管理类,用来注册插件,监听插件是否须要运行,须要的直接hook过来,运行便可。
        3,在获取数组和输出数组中间放一段插件触发器,用来触发须要运行的插件。
    以上也就是插件的三要素。

3,示例代码

    按照hook原理作一个简单的插件用例
     
    结构:hookDemo
            |-----index.php//主运行代码,触发器hook放在可能须要利用插件的地方
            |-----pluginManager.php//插件管理类
            |-----plugin//插件目录
                    |------sort.php//插件
                    |------unsetElement.php//插件

index.php

    <?php
    class printArr
    {
        public $pluginManagerObj;
        public function __construct()
        {
            //凡事用到hook的类,优先加载hook类
            require './pluginManager.php';
            $this->pluginManagerObj = new pluginManager();
        }
        public function main()
        {
            //获取数组
            $arr = $this->get_arr();
            //hook,也就是触发函数
            $arr = $this->pluginManagerObj->trigger('unsetElement',$arr);
            $arr = $this->pluginManagerObj->trigger('sort',$arr);
            $this->print_arr($arr);
            //输出数组
        }
        public function get_arr()
        {
            $arr = [9,5,6,0,1,4,7,34,67,28,105,278];
            return $arr;
        }
        public function print_arr($arr)
        {
            echo "<pre>";
            print_r($arr);
        }
    }
    echo "<pre>";
    $printArrObj = new printArr;
    $printArrObj->main();

pluginManager.php

    <?php
    class pluginManager
    {
        //监听数组
        public $listen = [];
        public function __construct()
        {
            //构造方法,引入插件以及监听
            $plugin_list = scandir('./plugin');
            foreach($plugin_list as $k=>$v){
                if($v == '.' || $v == '..'){
                    unset($plugin_list[$k]);
                }
            }
            if($plugin_list){
                foreach($plugin_list as $v){
                    if(file_exists('./plugin/'.$v)){
                        //引入插件
                        require_once('./plugin/'.$v);
                        //获取插件名
                        $class = explode('.',$v);
                        $class = $class[0];
                        if(class_exists($class)){
                            new $class($this);
                        }
     
                    }
                }
            }
        }
        //注册插件
        public function register($hook,&$obj,$method)
        {
            $key = get_class($obj).'->'.$method;
            $val = [$obj,$method];
            $this->listen[$hook][$key] = $val;
        }
        //触发插件
        public function trigger($hook,$data)
        {
            if(isset($this->listen[$hook]) && is_array($this->listen[$hook]) && count($this->listen[$hook])>0){
                foreach($this->listen[$hook] as $key => $val){
                    $hook_obj = &$val[0];
                    $method = $val[1];
                    $res = $hook_obj->$method($data);
                }
            }
            return $res;
     
        }
    }

sort.php

    <?php
    class sort
    {
        public function __construct(&$pluginManagerObj)
        {
            $pluginManagerObj->register('sort',$this,'sort');
        }
        public function sort($arr)
        {
            for($i=1;$i<count($arr);$i++){
                for($j=$i-1;$j>=0;$j--){
                    if($arr[$j+1] > $arr[$j]){
                        $tmp = $arr[$j];
                        $arr[$j] = $arr[$j+1];
                        $arr[$j+1] = $tmp;
     
                    }else{
                        break;
                    }
                }
            }
            return $arr;
        }
    }

unsetElement.php

    <?php
    class unsetElement
    {
        public function __construct(&$pluginManagerObj)
        {
            $pluginManagerObj->register('unsetElement',$this,'unset_4');
        }
        public function unset_4($arr)
        {
            foreach($arr as $k=>$v){
                if($v == 4){
                    unset($arr[$k]);
                }
            }
            return array_values($arr);
        }
    }

#####心得

1,关于hook

    结合上面的例子,hook最好的方面是,插件和项目互相独立,下降耦合性。第三方开发人员也不
    须要知道项目如何处理,相似api同样,只须要知道插件需求便可开发。
    二来,插件跟核心代码分离,方便各个区间段排错
    三来,项目开发过程当中,防止了代码的冗余杂乱,更适用于开源项目

2,关于实现

    三要素,
        触发函数,plugin管理类,plugin代码。
    实现过程当中,
        插件管理类和插件类实例化的对象必须贯穿全局且惟一。由于注册和执行过程当中,监听信息
        要保持全局。实现方案有三种:
            1,对象的各类操做必须用引用&。
            2,用单例模式获取对象
            3,$_GLOBAL;
    上面的例子只是一个简单的说明,
        熟悉原理后,能够拓展设置plugin配置文件对plugin管理,
        对plugin自己作到互相联系,互相调用。具体拓展状况按照需求而定
    hook思路理解清楚后,不拘泥于我上面写的这种方式,由于这种方式只是本身写框架玩的时候
    利用了面向对象方式。
        总之hook最普遍的原理,就是
            开局注册全局监听变量,
            全局监听变量负责监听,
            触发函数负责触发,
            一旦触发执行代码

---------------------
做者:敦煌的驼铃
来源:CSDN
原文:https://blog.csdn.net/SiuKong_Ngau/article/details/83587048
版权声明:本文为博主原创文章,转载请附上博文连接!编程

相关文章
相关标签/搜索