钩子与插件开发
用 TubePress 的 WordPress 风格 action 与 filter 钩子系统扩展功能:15+ 个钩子点、插件生命周期,以及如何构建和打包你自己的插件。
插件是在不修改核心代码的情况下为 TubePress 添加功能的方式。它们通过一套类似 WordPress 风格的小型系统接入 CMS,包含动作(触发即忘事件)和过滤器(修改某个值并返回)。三个内置插件——Age Gate、Backup Pro 和 CTR Ranking——完全使用此处记录的同一套 API 构建。
钩子系统
静态的 HookSystem 类管理两种类型的钩子。回调按优先级升序执行(默认为 10);回调抛出的任何异常都会被捕获并记录,因此一个有问题的插件永远不会导致页面崩溃。
// Actions — do something when an event fires
HookSystem::addAction('head.meta', function () {
echo '<meta name="rating" content="adult">';
}, 10);
// Filters — receive a value, return a (possibly) changed value
HookSystem::addFilter('theme.template_data', function ($data, $template) {
if ($template === 'home') {
$data['promo'] = Promo::current();
}
return $data;
});| 方法 | 用途 |
|---|---|
addAction($hook, $cb, $priority = 10) | 注册一个动作监听器。 |
doAction($hook, ...$args) | 触发一个动作(核心会调用这些)。 |
addFilter($hook, $cb, $priority = 10) | 注册一个过滤器。 |
applyFilter($hook, $value, ...$args) | 将一个值通过其过滤器处理并返回。 |
hasAction() / hasFilter() | 检查是否有任何监听器。 |
removeAction() / removeFilter() | 移除某个钩子的所有监听器。 |
可用的钩子点
核心会触发这些钩子。你也可以在插件中定义并触发自己的钩子。
动作
| 钩子 | 触发时机 |
|---|---|
head.meta | 在 <head> 内,CSS 之后——添加 meta 标签、验证标签等。 |
footer.scripts | 在 </body> 之前,JS 之后——添加统计代码或小组件。 |
routes.registered | 所有核心路由注册完毕后,在通用页面捕获之前。 |
router.before_dispatch | 在匹配的处理程序运行之前。参数:解析后的路径。 |
过滤器
| 钩子 | 修改内容(参数) |
|---|---|
theme.template_data | 模板渲染前的数据数组。参数:($data, $templateName)。 |
head.css | 合并后的 CSS 输出字符串。 |
footer.scripts | 合并后的 JS 输出字符串。 |
video.card.html | 完整的视频卡片 HTML。参数:($html, $video)。返回非空字符串以替换它。 |
插件结构
插件是 plugins/ 下的一个文件夹,包含清单文件和一个实现 PluginInterface 的类。
plugins/myplugin/
├── plugin.json Manifest (name, slug, version, …)
├── myplugin.php Main class implementing PluginInterface
├── icon.svg Optional icon shown in the admin
└── … Your assets, templates, migrations清单文件的格式与内置插件相同:
{
"name": "My Plugin",
"slug": "myplugin",
"description": "What it does, in one sentence.",
"version": "1.0.0",
"author": "You",
"requires": "1.0.0",
"icon": "icon.svg"
}主类实现了 PluginInterface 的六个生命周期方法:
| 方法 | 调用时机 |
|---|---|
boot() | 插件激活期间的每次请求——在此注册钩子、路由和资源。 |
activate() | 管理员启用插件时。 |
deactivate() | 管理员停用插件时。 |
install() | 首次激活——创建数据表,填充初始设置。 |
uninstall() | 插件被移除——执行清理。 |
info() | 以数组形式返回清单数据。 |
注册路由与资源
将所有注册工作放在 boot() 中:
public function boot(): void
{
// A front-end route
Router::get('/promo/{code}', [PromoController::class, 'show']);
// Inject a meta tag
HookSystem::addAction('head.meta', [$this, 'meta']);
// Tweak every video card
HookSystem::addFilter('video.card.html', [$this, 'badge'], 20);
}设计上自给自足。插件应自行添加所需的一切——包括自有的数据库结构、设置项、路由、管理页面和定时任务——并在卸载时一并移除,无需修改基础 CMS。CTR Ranking 插件是这方面的良好示例。
插件中的定时任务
在 boot() 中使用 CronManager 注册定期任务——它基于内置伪 cron 运行,无需系统 crontab。CTR 插件重新计算评分以及 Backup Pro 执行定时备份均使用此方式。
内置插件参考
分发与授权
你可以将插件保持私有、自由分享,或通过市场出售——这将添加签名授权和按域名激活功能。有关展示层面的等效内容,请参阅主题开发;模板代码中的所有可用内容,请参阅主题 API 参考。