Typecho 插件开发文档
1. 插件系统概述
Typecho 的插件系统基于钩子(Hook)机制,允许开发者在不修改核心代码的情况下扩展系统功能。插件通过实现特定接口并在系统的关键节点注入代码来实现功能扩展。
2. 插件目录结构
一个基本的 Typecho 插件结构如下:
usr/plugins/
└── YourPlugin/
├── Plugin.php # 插件主文件
├── README.md # 插件说明文档
├── LICENSE.txt # 许可证文件
└── assets/ # 静态资源目录
├── css/
├── js/
└── images/
3. 插件主文件 (Plugin.php)
插件必须实现 Typecho\Plugin\PluginInterface 接口,包含四个必需的方法:
<?php
namespace TypechoPlugin\YourPluginName;
use Typecho\Plugin\PluginInterface;
use Typecho\Widget\Helper\Form;
use Typecho\Widget\Helper\Form\Element\Text;
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
* 插件描述
*
* @package YourPluginName
* @author 作者名
* @version 1.0.0
* @link http://example.com
*/
class Plugin implements PluginInterface
{
/**
* 激活插件方法
* 如果激活失败, 直接抛出异常
*/
public static function activate()
{
// 注册钩子
\\Typecho\\Plugin::factory('admin/menu.php')->navBar = [__CLASS__, 'renderNavBar'];
// 可以在这里创建数据库表或执行其他初始化操作
// YourPlugin_Action::install();
}
/**
* 禁用插件方法
* 如果禁用失败, 直接抛出异常
*/
public static function deactivate()
{
// 清理钩子
// 可以在这里删除数据库表或执行清理操作
// YourPlugin_Action::uninstall();
}
/**
* 获取插件配置面板
*
* @param Form $form 配置面板
*/
public static function config(Form $form)
{
// 添加配置项
$apiKey = new Text('apiKey', null, '', _t('API密钥'), _t('请输入您的API密钥'));
$form->addInput($apiKey->addRule('required', _t('API密钥不能为空')));
}
/**
* 个人用户的配置面板
*
* @param Form $form
*/
public static function personalConfig(Form $form)
{
// 添加个人配置项
$notification = new Text('notificationEmail', null, '', _t('通知邮箱'));
$form->addInput($notification->addRule('email', _t('请输入正确的邮箱地址')));
}
/**
* 插件实现方法
*/
public static function renderNavBar()
{
echo '<span class="message success">插件已激活</span>';
}
}
4. 插件接口详解
4.1 PluginInterface 接口
插件必须实现以下四个方法:
activate()- 插件激活时调用deactivate()- 插件禁用时调用config()- 插件全局配置界面personalConfig()- 用户个人配置界面
4.2 激活和禁用方法
public static function activate()
{
// 注册系统钩子
\\Typecho\\Plugin::factory('Widget_Archive')->header = [__CLASS__, 'addHeaderCode'];
\\Typecho\\Plugin::factory('Widget_Archive')->footer = [__CLASS__, 'addFooterCode'];
// 创建数据库表
$db = \\Typecho\\Db::get();
$prefix = $db->getPrefix();
try {
$db->query("CREATE TABLE IF NOT EXISTS `{$prefix}your_table` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=%CHARSET%");
} catch (\\Typecho\\Db\\Exception $e) {
throw new \\Typecho\\Plugin\\Exception('创建数据表失败: ' . $e->getMessage(), 500);
}
}
public static function deactivate()
{
// 清理数据库表(可选)
// $db = \\Typecho\\Db::get();
// $prefix = $db->getPrefix();
// $db->query("DROP TABLE `{$prefix}your_table`");
}
5. 钩子系统
Typecho 提供了丰富的钩子点,插件可以在这些点注入自己的代码:
5.1 前端钩子
// 在页面头部添加内容
\\Typecho\\Plugin::factory('Widget_Archive')->header = [__CLASS__, 'addHeaderCode'];
// 在页面底部添加内容
\\Typecho\\Plugin::factory('Widget_Archive')->footer = [__CLASS__, 'addFooterCode'];
// 修改文章内容
\\Typecho\\Plugin::factory('Widget_Abstract_Contents')->contentEx = [__CLASS__, 'modifyContent'];
// 修改文章摘要
\\Typecho\\Plugin::factory('Widget_Abstract_Contents')->excerptEx = [__CLASS__, 'modifyExcerpt'];
5.2 后台钩子
// 在后台导航栏添加内容
\\Typecho\\Plugin::factory('admin/menu.php')->navBar = [__CLASS__, 'addAdminNavBar'];
// 在后台页脚添加内容
\\Typecho\\Plugin::factory('admin/footer.php')->end = [__CLASS__, 'addAdminFooter'];
// 在文章编辑页面添加内容
\\Typecho\\Plugin::factory('admin/write-post.php')->option = [__CLASS__, 'addPostOption'];
\\Typecho\\Plugin::factory('admin/write-page.php')->option = [__CLASS__, 'addPageOption'];
5.3 数据处理钩子
// 在保存文章前处理数据
\\Typecho\\Plugin::factory('Widget_Contents_Post_Edit')->write = [__CLASS__, 'beforeWritePost'];
// 在保存评论前处理数据
\\Typecho\\Plugin::factory('Widget_Feedback')->comment = [__CLASS__, 'beforeComment'];
6. 配置界面开发
6.1 全局配置
public static function config(Form $form)
{
// 文本输入框
$text = new Text('textOption', null, '默认值', _t('文本选项'), _t('这是一个文本输入框'));
$form->addInput($text);
// 多行文本框
$textarea = new Textarea('textareaOption', null, '', _t('文本域选项'));
$form->addInput($textarea);
// 复选框
$checkbox = new Checkbox('checkboxOption', [
'option1' => _t('选项1'),
'option2' => _t('选项2')
], ['option1'], _t('复选框选项'));
$form->addInput($checkbox->multiMode());
// 下拉选择框
$select = new Select('selectOption', [
'value1' => _t('值1'),
'value2' => _t('值2')
], 'value1', _t('选择框'));
$form->addInput($select);
// 单选框
$radio = new Radio('radioOption', [
'yes' => _t('是'),
'no' => _t('否')
], 'yes', _t('单选框'));
$form->addInput($radio);
}
6.2 验证规则
// 添加验证规则
$text = new Text('email', null, '', _t('邮箱地址'));
$form->addInput($text->addRule('required', _t('邮箱不能为空'))
->addRule('email', _t('请输入正确的邮箱地址')));
// URL验证
$url = new Text('website', null, '', _t('网站地址'));
$form->addInput($url->addRule('url', _t('请输入正确的网址')));
7. 数据库操作
插件可能需要创建和操作自己的数据表:
class Action
{
public static function install()
{
$db = \\Typecho\\Db::get();
$prefix = $db->getPrefix();
// 创建数据表
$scripts = "
CREATE TABLE `{$prefix}your_plugin_table` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(200) default NULL,
`value` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=%CHARSET%;
";
try {
$db->query($scripts);
} catch (\\Typecho\\Db\\Exception $e) {
throw new \\Typecho\\Plugin\\Exception('数据表创建失败: ' . $e->getMessage(), 500);
}
}
public static function uninstall()
{
$db = \\Typecho\\Db::get();
$prefix = $db->getPrefix();
// 删除数据表
$db->query("DROP TABLE `{$prefix}your_plugin_table`");
}
}
8. 插件间通信
插件可以通过配置系统与其他插件通信:
// 获取其他插件的配置
$options = \\Widget\\Options::alloc();
$otherPluginConfig = $options->plugin('OtherPluginName');
// 使用其他插件提供的功能
if (\\Typecho\\Plugin::exists('OtherPluginName')) {
// 调用其他插件的方法
}
9. 安全考虑
9.1 输入验证
// 验证用户输入
public static function validateInput($input)
{
// 使用内置验证
if (!empty($input['email'])) {
if (!filter_var($input['email'], FILTER_VALIDATE_EMAIL)) {
throw new \\Typecho\\Widget\\Exception('邮箱格式不正确');
}
}
// 转义输出
$output = htmlspecialchars($input['content']);
}
9.2 权限检查
// 检查用户权限
$user = \\Widget\\Users::alloc();
if (!$user->hasLogin() || !$user->pass('administrator', true)) {
throw new \\Typecho\\Widget\\Exception('权限不足', 403);
}
10. 调试和测试
10.1 调试信息
// 开启调试模式
if (defined('__TYPECHO_DEBUG__') && __TYPECHO_DEBUG__) {
error_log('Debug info: ' . var_export($data, true));
}
10.2 异常处理
public static function someMethod()
{
try {
// 插件逻辑
$result = self::doSomething();
return $result;
} catch (\\Exception $e) {
\\Typecho\\Widget::widget('Widget_Notice')->set(_t('插件执行出错: ') . $e->getMessage(), 'error');
return false;
}
}
11. 插件发布
发布插件前需要确保:
- 插件信息在注释中正确声明
- 插件名称唯一且符合命名规范
- 遵循Typecho的开源协议
- 提供完整的文档和示例
- 进行充分的测试
插件命名规范:
- 插件目录名首字母大写,使用驼峰命名法
- 命名应简洁明了,避免过于宽泛的名称
- 避免与系统或其他插件冲突的名称