zoukankan      html  css  js  c++  java
  • 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源码的钩子机制就会更加如鱼得水。

  • 相关阅读:
    119. Pascal's Triangle II
    118. Pascal's Triangle
    112. Path Sum
    111. Minimum Depth of Binary Tree
    110. Balanced Binary Tree
    108. Convert Sorted Array to Binary Search Tree
    88. Merge Sorted Array
    83. Remove Duplicates from Sorted List
    70. Climbing Stairs
    陌陌面试经历
  • 原文地址:https://www.cnblogs.com/whiterock/p/7286188.html
Copyright © 2011-2022 走看看