• Wordpress解析系列之PHP编写hook钩子原理简单实例


    Wordpress作为全球应用最广泛的个人博客建站工具,有很多的技术架构值得我们学习推敲。其中,最著名最经典的编码技术架构就是采用了hook的机制。

    hook翻译成中文是钩子的意思,单独看这个词我们难以理解这个hook机制(即钩子机制)是什么意思。那么笔者就用大白话以通俗易懂方式给大家讲解一下什么是hook机制,以及用原生PHP函数编写实现简单实例。

    大白话解释:以Wordpress为例,它的hook机制就是在网页加载时一起加载了很多hook变量,也就是钩子变量,这些变量作用是绑定相关的函数,只要hook变量被加载,Wordpress就会用一个内置通用API函数解析出hook变量包含的函数并执行。好理解吧,一句话就说清楚了hook机制,但是实现并不简单,最关键的就是那个内置API函数解析hook变量。后面笔者先不给大家掰Wordpress源码的hook解析过程了,那个太复杂,考虑的方面很多,学习理解起来比较困难。这里我们采用更简单直接的解说方式,利用原生PHP函数编写个简单的hook机制plugin插件管理类,和大家一起更加直观的理解钩子机制的原理过程。

    下面代码的基本过程是:PluginManager内部有hook键值数组_listener()。

    1、plugin注册到PluginManager类的包含hook键值的监听数组_listener()中,

    2、PluginManager类实例对象调用trigger函数实现加载所有plugin插件并执行功能方法。

     具体过程详见如下代码注释分析:

    <?php
    
    // 已注册插件管理核心类
    class PluginManager
    {
        /**
         * 监听数组,保存所有已注册插件的类私有的核心数组变量,数组的键名是钩子名,值是对应的插件信息
         * @var array private $_listeners
         */
        private $_listeners = array();
         /**
         * 默认构造函数的作用是通过get_active_plugins()读取plugins目录下所有已激活插件信息
         * 同时初始化这些插件,注册到核心类PluginManager数组变量$_listeners中
         *
         * @var array $plugins
         * @return void
         */
        public function __construct()
        {
            //这里$plugins数组包含我们获取已激活的所有插件信息,通过get_active_plugins()函数获取具体信息
            $plugins = array();
            $plugins = $this->get_active_plugins();
    
            if(is_array($plugins) && !empty($plugins) && count($plugins) > 0)
            {
                foreach($plugins as $plugin)
                {
                        // 约定每个插件类的名字为如下格式,例如DemoActions;
                        $class = $plugin['name'].'Actions';
                        if(class_exists($class))
                        {
                            //初始实例化已激活插件,$this代表PluginManager实例为参数
                            new $class($this);
                        }
                }
            }
        }
    
        /**
         * 注册需要监听插件的功能方法绑定到hook钩子,并把hook钩子加入到$_listeners数组
         *
         * @param string $hook 钩子变量,就是数组的键名,每个钩子可以绑定多个plugin插件类
         * @param object $plugin 插件变量,get_class($plugin)获取插件对应的类
         * @param string $method 插件$plugin类对应的功能方法
         */
        function register($hook, $plugin, $method)
        {
            //获取插件实现的功能方法
            $key = get_class($plugin).'->'.$method;
            //echo $key.'<br>'; //这里可以测试$key的值是否是实例方法引用;
            //将插件的实例对象和功能方法保存入对应键值为hook名的监听数组中
            $this->_listeners[$hook][$key] = array($plugin, $method);
        }
    
        /**
         * 返回已激活的所有插件名称和路径,读取plugins目录下所有已激活插件信息
         * 
         * @return array() $plugins 返回数组包含每组插件$name:插件名称,也是php文件名;$directory:插件所在路径
         */
        function get_active_plugins()
        {
            $dir = dirname(__FILE__).DIRECTORY_SEPARATOR.'plugins';
            $filesnames = scandir($dir);
            $plugins = array();
            foreach($filesnames as $filename)
            {
                if($filename!='.' &&$filename!='..')
                {
                    $plugins[] = array(
                        'name' => strstr($filename,'.', true),
                        'directory'=>$dir);
                }
            }
            return $plugins;
        }
    
        /**
         * 触发一个钩子名称下所有的插件自定义功能方法
         *
         * @param string $hook 钩子的名称
         * @param mixed $data 输入钩子内对应插件自定义方法的参数,默认为空
         * @return mixed
         */
        function trigger($hook, $data='')
        {
            //查看要实现的钩子,是否在监听数组之中
            if (isset($this->_listeners[$hook]) && is_array($this->_listeners[$hook]) && count($this->_listeners[$hook]) > 0)
            {
                // 循环调用hook钩子所有插件功能方法
                foreach ($this->_listeners[$hook] as $listener)
                {
                    // 取出插件实例对象类名
                    $class = $listener[0];
                    // 取出插件实例对象自定义的功能方法
                    $method = $listener[1];
                    if(method_exists($class,$method))
                    {
                        // 动态调用hook钩子下所有插件的功能方法,这里$data为可无的方法参数
                        $class->$method($data);
                    }
                }
            }
        }
    
    }

    下面的是插件类DemoActions,其解析函数内包含对pluginManager对象的引用,对应的插件文件是Demo.php,该插件自定义功能方法为sayHello()。

    // 插件类,约定必须包含固定格式解析函数
    class DemoActions
    {
        /** 
         * 解析函数的参数是pluginManager类的引用实例
         * 函数调用pluginManager实例的register方法注册这个插件
         */
        function __construct(&$pluginManager)
        {
            /* hookdemo参数是钩子的名称
             * $this是Demo_actions类的实例
             * say_hello参数是此插件的功能方法
             */
            $pluginManager->register('hookdemo', $this, 'sayHello');
        }
    
        // 这里是自定义的插件功能方法
        function sayHello()
        {
            echo '<br>Hello World<br>';
        }
    }
    

    实际使用的时候,编辑如下代码程序:

    //实际应用程序
    $pluginManager = new PluginManager;  //插件管理类实例化对象
    $pluginManager->trigger('hookdemo','');  //启动绑定到hookdemo钩子的所有插件功能;

    至此,我们就完整的实现了hook钩子绑定插件信息及如何利用hook钩子执行插件自定义功能方法的原理。Wordpress的hook钩子原理与此类似,理解了上面的代码,再逐步深入理解Wordpress源码的钩子机制就会更加如鱼得水。

  • 相关阅读:
    The Country List
    hdoj1215--七夕节(数学)
    Poj 1654--Area(叉积)
    Poj2229--Sumsets(递推)
    数据预处理 center&scale&box-cox
    caret 分类回归树 用法
    ensemble 的2篇入门 文章
    数组 array 矩阵 list 数据框 dataframe
    R list frame, matrix
    R 如何 隐藏坐标轴
  • 原文地址:https://www.cnblogs.com/whiterock/p/7286188.html
Copyright © 2020-2023  润新知