Wordpress解析系列之PHP編寫hook鉤子原理簡單實例

Wordpress做爲全球應用最普遍的我的博客建站工具,有不少的技術架構值得咱們學習推敲。其中,最著名最經典的編碼技術架構就是採用了hook的機制。php

hook翻譯成中文是鉤子的意思,單獨看這個詞咱們難以理解這個hook機制(即鉤子機制)是什麼意思。那麼筆者就用大白話以通俗易懂方式給你們講解一下什麼是hook機制,以及用原生PHP函數編寫實現簡單實例。數組

大白話解釋:以Wordpress爲例,它的hook機制就是在網頁加載時一塊兒加載了不少hook變量,也就是鉤子變量,這些變量做用是綁定相關的函數,只要hook變量被加載,Wordpress就會用一個內置通用API函數解析出hook變量包含的函數並執行。好理解吧,一句話就說清楚了hook機制,可是實現並不簡單,最關鍵的就是那個內置API函數解析hook變量。後面筆者先不給你們掰Wordpress源碼的hook解析過程了,那個太複雜,考慮的方面不少,學習理解起來比較困難。這裏咱們採用更簡單直接的解說方式,利用原生PHP函數編寫個簡單的hook機制plugin插件管理類,和你們一塊兒更加直觀的理解鉤子機制的原理過程。架構

下面代碼的基本過程是:PluginManager內部有hook鍵值數組_listener()。函數

一、plugin註冊到PluginManager類的包含hook鍵值的監聽數組_listener()中,工具

二、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()。this

// 插件類,約定必須包含固定格式解析函數
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源碼的鉤子機制就會更加如魚得水。spa

相關文章
相關標籤/搜索