RyeBlog 采用轻量插件机制:一个目录 = 一个插件,无需注册表、无需 Composer,把插件目录放到 usr/plugins/ 并在后台启用即可。
一、插件目录结构
usr/plugins/<插件名>/
└── Plugin.php # 唯一必需文件(类文件)- 插件目录名:小写字母、数字、连字符,如
spam-guard、nav-links、english-admin - 类名规则:
Plugin_+ 目录名(连字符转下划线)→Plugin_spam_guard - 文件头部用注释声明元信息(后台自动解析):
<?php
/**
* @Title 防垃圾评论
* @Desc 多层无感防垃圾:蜜罐、时间陷阱、链接数限制…
* @Version 1.0.0
* @Author RyeBlog Team
*/
class Plugin_spam_guard { ... }二、生命周期钩子
插件类中的静态方法会被系统在对应时机自动调用:
| 方法 | 时机 |
|---|---|
| activate() | 后台启用插件时(常用于建表、初始化选项) |
| deactivate() | 后台停用插件时(用于清理,可返回 false 中止停用) |
| config() | 后台「插件配置」页显示配置表单(返回 HTML) |
| saveConfig($post) | 配置表单提交时保存 |
activate() 里建议用 dbQuery() 建表(自动适配表前缀):
public static function activate()
{
dbQuery('CREATE TABLE IF NOT EXISTS vd_my_data (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
setOption('my_plugin_enabled', '1');
return true;
}注意:SQL 里的vd_前缀会被自动转换为站点实际前缀,请勿使用裸db()->exec()。
三、钩子(Hook)系统
doHook('名称', $参数) 会调用所有已启用插件中同名的静态方法,返回值的拼接结果。核心预留的钩子:
| 钩子 | 触发位置 | 参数 |
|---|---|---|
| nav_top | 顶栏导航 | — |
| home_after | 首页列表之后 | — |
| page_replace | 独立页渲染前 | $page(返回非空字符串则替换整页) |
| post_content_after | 文章正文之后 | $post |
| articleFooter | 文章底部 | $post |
| comment_form_extra | 评论表单内 | —(注入蜜罐/时间戳等) |
| comment_check | 评论提交检查 | $_POST(返回非空字符串 = 拒绝原因) |
示例——在文章底部追加版权信息:
class Plugin_post_copyright
{
public static function post_content_after($post)
{
return '<div style="margin-top:20px;padding:12px;background:#f4fcf8;border-radius:8px">
本文由「' . esc($post['author']) . '」原创发布,转载请注明出处。</div>';
}
}四、核心 API(插件里可直接调用)
- 数据:
dbAll($sql, $params)/dbOne(...)/dbQuery(...)(自动前缀转换,参数化查询) - 选项:
getOption($name, $default)/setOption($name, $value) - 内容:
getPosts($opts)/getPost($id)/getPages()/getCategories()/getTags() - 链接:
postUrl($post)/categoryUrl($cat)/tagUrl($tag)/pageUrl($page)/homeUrl() - 安全:
esc($str)(HTML 转义)/csrfToken()/currentUser() - 多语言:
__('中文')(后台 UI 翻译)/L($row, 'title')(内容字段,英文站回退中文) - 其他:
clientIp()/formatDate($time)/slugify($text)/isLoggedIn()
五、完整示例:防垃圾评论插件(已发布到云端)
官方 spam-guard 插件展示了插件开发的全部要点:
class Plugin_spam_guard
{
// 1. 激活:建拦截日志表 + 初始化配置
public static function activate() {
dbQuery('CREATE TABLE IF NOT EXISTS vd_spam_log (
id INT AUTO_INCREMENT PRIMARY KEY,
ip VARCHAR(45) NOT NULL, reason VARCHAR(120) NOT NULL,
content TEXT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
setOption('spamguard_cfg', json_encode(['enabled'=>'1','honeypot'=>'1','time_min'=>'3','max_links'=>'2']));
return true;
}
// 2. 向评论表单注入蜜罐字段
public static function comment_form_extra($arg = null) {
return '<input type="text" name="comment_hp" value="" tabindex="-1" autocomplete="off"
style="position:absolute;left:-9999px;opacity:0">'
. '<input type="hidden" name="comment_ts" value="' . time() . '">';
}
// 3. 提交检查:蜜罐被填 / 提交过快 → 拒绝
public static function comment_check($arg = null) {
$d = is_array($arg) ? $arg : $_POST;
if (trim((string)($d['comment_hp'] ?? '')) !== '') return '提交过于频繁,请稍后再试。';
$ts = (int)($d['comment_ts'] ?? 0);
if ($ts > 0 && time() - $ts < 3) return '提交过于频繁,请稍后再试。';
return ''; // 通过
}
// 4. 后台配置页
public static function config() { /* 返回表单 HTML */ }
public static function saveConfig($post) { /* 保存配置 */ }
}六、发布到云端市场
php tools/cli_pack.php plugin <插件名> --version 1.0.0命令会生成 cloud-release/plugins/<name>-<version>.zip 并更新 repo.json(含 SHA-256 校验)。把这两个文件放到云端仓库(默认 https://ryeblog.com/cloud/)后,全站用户都可在后台「插件 → 云端」在线安装。
RyeBlogRye