zoukankan      html  css  js  c++  java
  • php hook编程机制

    说明

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

    实例

    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
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    3d角色模型 制作 全过程 。3d max 。3d role model making process.3d Max
    Cocos2d-X开发教程-捕鱼达人 Cocos2-x development tutorial
    unity3d 给游戏添加音源 Unity3d adds a sound source to the game
    安卓 运行、调试 配置 android Run/debug configurations
    Gradle 同步 已经开始 Gradle sync started
    更新 是 可用的 针对 安卓 软件开发包和工具 Updates are available for android software development packages and tools
    [Educational Codeforces Round 16]A. King Moves
    他们在军训,我在搞 OI(一)
    [POJ1383]Labyrinth
    [POJ1157]LITTLE SHOP OF FLOWERS
  • 原文地址:https://www.cnblogs.com/aipiaoborensheng/p/10481278.html
Copyright © 2011-2022 走看看