Typecho插件开发手册

插件的基本结构

在 Typecho 中,插件通常是一个独立的 PHP 文件,默认放置在usr/plugins/目录下。插件文件名即为插件名,插件类名也应与文件名相同。插件必须实现 Typecho_Plugin_Interface 接口。
基本结构:

<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;

/**
 * 插件的描述信息
 *
 * @package PluginName
 */
class PluginName_Plugin implements Typecho_Plugin_Interface
{
    // 插件激活方法
    public static function activate() {}

    // 插件停用方法
    public static function deactivate() {}

    // 插件配置面板
    public static function config(Typecho_Widget_Helper_Form $form) {}

    // 个人用户的配置面板
    public static function personalConfig(Typecho_Widget_Helper_Form $form) {}

    // 实现的插件功能方法
    // ...
}

说明:

  • Typecho_Plugin_Interface:插件类必须实现该接口,包含 activate、deactivate、config、personalConfig 四个方法。
  • activate:插件激活时调用,用于注册插件方法、初始化数据等。
  • deactivate:插件停用时调用,用于清理数据、注销插件方法等。
  • config:插件的配置界面,用于在后台展示插件的设置选项。
  • personalConfig:针对个人用户的配置界面。

激活插件

在 activate 方法中,可以进行以下操作:

  • 注册插件方法:使用 Typecho_Plugin::factory 注册插件的方法到指定的 Hook Point。
  • 创建数据库表:如果插件需要额外的数据表,可以在激活时创建。
  • 初始化数据:初始化插件所需的数据或配置。

    public static function activate()
    {
     // 注册插件方法到 Hook Point
     Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('PluginName_Plugin', 'beforeRender');
    
     // 返回激活成功的信息
     return _t('插件已激活');
    }

停用插件

在 deactivate 方法中,可以进行以下操作:

  • 注销插件方法:清理注册的插件方法(Typecho 会自动处理)。
  • 删除数据库表:如果插件创建了额外的数据表,可以选择在停用时删除。
  • 清理数据:清理插件的配置信息或缓存。

定义插件的配置界面

config 方法中定义插件的配置选项。

public static function config(Typecho_Widget_Helper_Form $form)
{
    // 添加一个文本框
    $text = new Typecho_Widget_Helper_Form_Element_Text('text', NULL, '默认值', _t('文本框标签'), _t('文本框提示'));
    $form->addInput($text);

    // 添加一个单选框
    $radio = new Typecho_Widget_Helper_Form_Element_Radio('radio', array('1' => '选项1', '2' => '选项2'), '1', _t('单选框标签'));
    $form->addInput($radio);

    // 添加一个复选框
    $checkbox = new Typecho_Widget_Helper_Form_Element_Checkbox('checkbox', array('1' => '选项1', '2' => '选项2'), NULL, _t('复选框标签'));
    $form->addInput($checkbox);
}

说明:

  • 使用 Typecho_Widget_Helper_Form_Element_* 类来创建不同类型的表单元素。
  • 调用 $form->addInput() 方法将元素添加到配置界面。

处理插件的用户配置

在插件的方法中,可以通过 Helper::options()->plugin('PluginName') 获取用户的配置信息。

public static function someMethod()
{
    $options = Helper::options()->plugin('PluginName');
    $textValue = $options->text; // 获取文本框的值
    // ...
}

说明:

  • Helper::options():获取全局的配置对象。
  • plugin('PluginName'):获取指定插件的配置信息。

注册插件方法(Hook Point)

在 activate 方法中,使用 Typecho_Plugin::factory 注册插件的方法到指定的 Hook Point。

public static function activate()
{
    Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('PluginName_Plugin', 'beforeRender');
}

说明: 具体插入点可以参考文章后半段,有专门介绍各插入点的作用及位置

  • Typecho_Plugin::factory('类名')->方法名:指定要拦截的类和方法(Hook Point)。
  • array('PluginName_Plugin', '方法名'):插件的方法回调。

定义自定义路由

如果插件需要自定义 URL 路由,可以在 activate 方法中定义。

public static function activate()
{
    // 添加自定义路由
    Helper::addRoute('plugin_route', '/plugin/route/', 'PluginName_Action', 'actionMethod');
}

public static function deactivate()
{
    // 删除自定义路由
    Helper::removeRoute('plugin_route');
}

Helper::addRoute:添加自定义路由。

  • 第一个参数:路由名称。
  • 第二个参数:路由规则(URL 模式)。
  • 第三个参数:处理该路由的类名。
  • 第四个参数:处理方法。

Helper::removeRoute:删除自定义路由。

加载插件的类和方法

如果插件包含多个类或文件,可以通过 require_once 或自动加载的方式引入。

// 在插件的主文件中
require_once 'libs/Helper.php';

// 使用命名空间和自动加载
spl_autoload_register(function ($class) {
    $prefix = 'PluginName\\';
    $base_dir = __DIR__ . '/src/';
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        return;
    }
    $relative_class = substr($class, $len);
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
    if (file_exists($file)) {
        require $file;
    }
});
  • require_once:直接引入文件。
  • spl_autoload_register:注册自动加载函数,按照命名空间和目录结构自动加载类。

文件载入与资源管理

插件可能需要加载 CSS、JS 文件或其他资源,可以通过以下方式实现。

在模板中加载资源:

// 在插件的方法中
public static function header()
{
    $cssUrl = Helper::options()->pluginUrl . '/PluginName/assets/style.css';
    echo '<link rel="stylesheet" type="text/css" href="' . $cssUrl . '">';
}

public static function footer()
{
    $jsUrl = Helper::options()->pluginUrl . '/PluginName/assets/script.js';
    echo '<script type="text/javascript" src="' . $jsUrl . '"></script>';
}

注册到 Hook Point:

public static function activate()
{
    Typecho_Plugin::factory('Widget_Archive')->header = array('PluginName_Plugin', 'header');
    Typecho_Plugin::factory('Widget_Archive')->footer = array('PluginName_Plugin', 'footer');
}
  • Helper::options()->pluginUrl:获取插件目录的 URL。
  • header、footer:在页面的头部和尾部输出内容。

模板的使用与扩展

插件可以自定义模板,或者扩展主题的模板功能。在插件目录中创建模板文件:

  • 创建 themes 目录:在插件目录下创建 themes 目录,用于存放模板文件。
  • 编写模板文件:例如,创建 themes/plugin-template.php。

在插件中调用模板:

public static function renderTemplate()
{
    $template = __DIR__ . '/themes/plugin-template.php';
    if (file_exists($template)) {
        include $template;
    } else {
        echo '模板文件不存在';
    }
}

在路由处理方法中调用:

public function actionMethod()
{
    self::renderTemplate();
}

完整的插件示例

usr/
└── plugins/
    └── HelloWorld/
        ├── HelloWorld_Plugin.php
        ├── Action.php
        ├── assets/
        │   ├── style.css
        │   └── script.js
        └── themes/
            └── hello.php

钩子(Hook)插入点

在 Typecho 插件开发中,可以通过 Typecho_Plugin::factory('类名')->方法名 的形式来拦截和扩展 Typecho 的核心功能。以下是所有可用的插入位置(Hook Points),按照类名分类,并注明调用的位置及意义。

插入位置的定义方式

插入位置通常是通过以下几种方式定义的:

  1. 方法(Method)

    • 在 Typecho 的核心类中,某些方法被定义为插入位置。例如,Widget_Contents_Post_Edit 类中的 insert 方法就是一个插入位置。
    • 插件可以通过 Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert 来拦截这个方法,并在其中执行自定义代码。
  2. 属性(Property)

    • 在 Typecho 的核心类中,某些属性被定义为插入位置。例如,Widget_Contents_Post_Edit 类中的 insert 属性就是一个插入位置。
    • 插件可以通过 Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert 来拦截这个属性,并在其中执行自定义代码。

示例

假设我们有一个 Widget_Contents_Post_Edit 类,其中定义了一个 insert 方法:

class Widget_Contents_Post_Edit extends Typecho_Widget
{
    public function insert()
    {
        // 插入文章的逻辑
    }
}

在插件中,我们可以通过以下方式拦截这个 insert 方法:

class XXX_Plugin implements Typecho_Plugin_Interface
{
    public static function activate()
    {
        Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert = array('XXX_Plugin', 'publish');
    }

    public static function publish($post)
    {
        // 自定义的插入逻辑
    }
}

在这个例子中,XXX_Plugin 插件在 Widget_Contents_Post_Edit 类的 insert 方法执行时,会调用 XXX_Plugin::publish 方法来执行自定义的插入逻辑

常见Hooks整理

1. Widget_Contents_Post_Edit

  • insert:在插入文章时触发。
  • update:在更新文章时触发。
  • delete:在删除文章时触发。
  • finishPublish:在文章发布完成后触发。

2. Widget_Contents_Page_Edit

  • insert:在插入页面时触发。
  • update:在更新页面时触发。
  • delete:在删除页面时触发。
  • finishPublish:在页面发布完成后触发。

3. Widget_Comments_Edit

  • insert:在插入评论时触发。
  • update:在更新评论时触发。
  • delete:在删除评论时触发。
  • finishComment:在评论操作完成后触发。
  • finishDelete:在评论删除后触发。

4. Widget_Users_Edit

  • insert:在创建用户时触发。
  • update:在更新用户信息时触发。
  • delete:在删除用户时触发。
  • finishCreate:在用户创建完成后触发。
  • finishDelete:在用户删除后触发。

5. Widget_Metas_Category_Edit

  • insert:在创建分类时触发。
  • update:在更新分类信息时触发。
  • delete:在删除分类时触发。
  • finishCreate:在分类创建完成后触发。

6. Widget_Metas_Tag_Edit

  • insert:在创建标签时触发。
  • update:在更新标签信息时触发。
  • delete:在删除标签时触发。
  • finishCreate:在标签创建完成后触发。

7. Widget_Feedback

  • comment:在用户提交评论时触发。
  • trackback:在接收Trackback时触发。

8. Widget_Register

  • register:在用户注册时触发。

9. Widget_Login

  • loginSucceed:在用户登录成功后触发。
  • loginFail:在用户登录失败后触发。

10. Widget_Archive

  • beforeRender:在渲染文章内容之前触发。
  • afterRender:在渲染文章内容之后触发。
  • beforeOutput:在输出文章内容之前触发。
  • afterOutput:在输出文章内容之后触发。
  • handleInit:在 Archive Widget 初始化时触发。
  • select:在构建内容查询时触发。

11. Widget_Comments

  • beforeRender:在渲染评论列表之前触发。
  • afterRender:在渲染评论列表之后触发。

12. Widget_Abstract_Contents

  • contentEx:在获取文章完整内容时触发。
  • excerptEx:在获取文章摘要内容时触发。
  • filter:在获取内容列表时触发。

13. Widget_Abstract_Comments

  • contentEx:在获取评论内容时触发。
  • excerptEx:在获取评论摘要时触发。
  • filter:在获取评论列表时触发。

14. Widget_Abstract_Metas

  • filter:在获取分类或标签列表时触发。

15. Widget_Abstract_Users

  • filter:在获取用户列表时触发。

16. Widget_Abstract_Options

  • filter:在获取系统选项时触发。

17. Widget_Abstract

  • beforeRender:在任何 Widget 渲染之前触发。
  • afterRender:在任何 Widget 渲染之后触发。
  • ___construct:在 Widget 初始化时触发。
  • ___init:在 Widget 初始化完成后触发。
  • ___execute:在执行 Widget 主逻辑时触发。

18. Typecho_Widget

  • widget:在创建 Widget 实例时触发。
  • widgetStart:在 Widget 开始执行时触发。
  • widgetEnd:在 Widget 执行结束时触发。

19. Typecho_Widget_Helper_Form

  • render:在渲染表单之前触发。

20. Typecho_Widget_Helper_Form_Element

  • render:在渲染表单元素之前触发。

21. Typecho_Db

  • query:在执行数据库查询时触发。
  • fetchAll:在获取查询结果时触发。

22. Typecho_Router

  • router:在路由解析时触发。
  • dispatch:在路由调度时触发。

23. Typecho_Response

  • redirect:在执行页面重定向时触发。

24. Typecho_Cookie

  • set:在设置 Cookie时触发。
  • get:在获取 Cookie时触发。

25. Typecho_Feed

  • output:在输出RSS Feed时触发。

26. Typecho

  • init:在系统初始化时触发。
  • header:在输出页面头部时触发。
  • footer:在输出页面尾部时触发。

使用方法示例

Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert = array('YourPlugin_Class', 'yourMethod');

说明

  • 插入位置(Hook Point):即可拦截的方法名。
  • 触发时机:在对应的方法执行时触发,允许插件在该时机执行自定义逻辑。
  • 使用场景:插件可以通过这些 Hook Points 实现对 Typecho 功能的扩展和定制,例如在文章发布后发送通知,在用户登录失败后记录日志等。

注意事项

  • 确保方法存在:请确保您定义的插件类和方法存在且可被调用,避免出现错误。
  • 性能考虑:插件开发时应注意性能,避免阻塞核心功能的执行。
  • 充分测试:某些 Hook Points 可能仅在特定情况下触发,开发时需充分测试。

通过以上整理,您可以清晰地了解 Typecho 插件开发中可用的所有插入位置,以及它们的调用位置和意义,方便在插件中实现所需的功能。

标签: PHP, Typecho

相关文章

PHP中利用 popen 和 pclose 实现多进程的简单方案

在 PHP 中,popen 和 pclose 函数用于打开一个指向进程的管道,并允许你通过该管道与进程进行通信。popen 函数会启动一个新的进程,并返回一个文件指针,你可以通过该指针读取或写入...

Typecho博客系统的xmlrpc的使用附PHP示例代码

XML-RPC 是一种远程过程调用(RPC)协议,它使用 XML 编码请求和响应,并通过 HTTP 进行传输。XML-RPC 允许客户端调用远程服务器上的方法,并获取返回结果。这种协议简单、轻量...

php+mysql中如何处理嵌套(子)事务并保持原子性一致

在PHP和MySQL中处理子事务并保持原子性一致性是一个复杂但非常重要的问题,尤其是在处理涉及多个数据库操作的业务逻辑时。以下是一些关键的解决方案、思路、技术要点和涉及的难点讲解。解决方案与思路...

如何使用Go编写跨平台组件并让Java或PHP调用

在现代软件开发中,跨语言调用是一个常见的需求。假设我们有一个用Go语言编写的组件,我们希望Java或PHP能够直接调用这个组件中对外提供的方法。为了实现这一目标,我们可以使用以下几种方法:1. ...

深入解析PHP的filter_var函数及其应用场景

在PHP开发中,数据的过滤与验证是至关重要的环节。PHP的 filter_var 函数提供了一种简洁而强大的方式来对输入数据进行验证和过滤。本篇文章将从专业的角度详细介绍 filter_var ...

图片Base64编码

CSR生成

图片无损放大

图片占位符

Excel拆分文件