首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

Typecho 插件開發教程(四):插件掛載鉤子機制詳解

     編輯:PHP 綜合知識

Typecho 插件鉤子系統

Typecho 透過「插件鉤子」讓插件在核心關鍵節點介入執行流程。鉤子本質是物件的公共屬性,儲存可呼叫的回調 (函數、靜態方法、物件方法)。

鉤子類型與定義位置

鉤子名稱定義位置觸發時機傳遞參數
finishPublishWidget/Contents/Post/Edit.php
Widget/Contents/Page/Edit.php
文章/頁面發布完成($contents, $widget)
handleWidget/Archive.php路由匹配後、輸出前($archive)
headerWidget/Archive.php輸出 HTML Head 前($archive)
footerWidget/Archive.php輸出 HTML Foot 前($archive)
comment.postWidget/Feedback.php評論提交後($comment, $widget)
widgets.renderWidget/Widget.php側邊欄 Widget 輸出時($widget)

掛載語法詳解

<?php
// 在 activate() 中掛載
public static function activate() {
    // 靜態方法掛載
    Typecho_Plugin::factory('Widget_Contents_Post_Edit')->finishPublish = 
        array('MyPlugin', 'onPublish');
    
    // 物件方法掛載 (需先實例化)
    $instance = new MyPlugin();
    Typecho_Plugin::factory('Widget_Archive')->handle = 
        array($instance, 'onHandle');
    
    // 匿名函數掛載 (PHP 5.3+)
    Typecho_Plugin::factory('Widget_Feedback')->comment.post = 
        function($comment, $widget) {
            // 邏輯...
        };
    
    return '插件啟用成功,請至設定頁配置';
}
?>

Typecho_Plugin::factory() 運作原理

// 簡化版核心原理
class Typecho_Plugin {
    public static function factory($widgetClass) {
        // 取得或建立 Widget 類別的代理物件
        if (!isset(self::$widgets[$widgetClass])) {
            self::$widgets[$widgetClass] = new Typecho_Plugin_Widget($widgetClass);
        }
        return self::$widgets[$widgetClass];
    }
}

class Typecho_Plugin_Widget {
    private $widgetClass;
    private $hooks = [];
    
    public function __set($hookName, $callback) {
        // 驗證 callback 可呼叫
        if (!is_callable($callback)) {
            throw new Typecho_Plugin_Exception("鉤子 $hookName 回調不可呼叫");
        }
        $this->hooks[$hookName] = $callback;
    }
    
    public function __get($hookName) {
        return $this->hooks[$hookName] ?? null;
    }
    
    // Widget 內部觸發鉤子時:
    public function pluginHandle($hookName, ...$args) {
        if (isset($this->hooks[$hookName])) {
            return call_user_func_array($this->hooks[$hookName], $args);
        }
    }
}

Widget 內部觸發鉤子範例

// Widget/Contents/Post/Edit.php 第 736 行左右
public function finishPublish($contents) {
    // ... 存檔邏輯 ...
    
    // 觸發插件鉤子
    $this->pluginHandle('finishPublish', $contents, $this);
    
    return $contents;
}

多重掛載與優先順序

同一鉤子可被多個插件掛載,按掛載順序依序執行。若需控制順序:

// 插入陣列最前 (優先執行)
$hooks = &Typecho_Plugin::factory('Widget_Archive')->handle;
array_unshift($hooks, array('HighPriorityPlugin', 'onHandle'));

// 插入陣列最後 (最後執行)
$hooks[] = array('LowPriorityPlugin', 'onHandle');

取消掛載 (deactivate 時自動清理)

public static function deactivate() {
    // 手動移除 (通常不需要,停用插件會自動清理)
    $factory = Typecho_Plugin::factory('Widget_Contents_Post_Edit');
    unset($factory->finishPublish);
    
    return '插件已停用';
}

鉤子回調返回值處理

  • 大多數鉤子 忽略返回值 (如 finishPublish、header、footer)
  • 部分鉤子 使用返回值 (如 handle 可返回 false 阻止預設處理)
  • 若需修改傳遞參數,使用 引用傳遞:function(&$contents, $widget)

最佳實務建議

  1. 命名規範:回調方法建議前綴 on (如 onPublish、onHandle)
  2. 參數型別提示:PHP 7+ 加入型別宣告 function(array $contents, Widget_Interface $widget)
  3. 錯誤隔離:鉤子內部用 try-catch 包裹,避免單一插件錯誤導致整站崩潰
  4. 效能考量:高頻鉤子 (如 handle) 避免重度運算,必要時快取
  5. 文檔化:在插件 README 列出使用的鉤子、參數、預期行為
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved