Z-Blog 插件钩子入门:机制拆解与常用钩子速查

· zblog · 阅读 31
摘要

Z-Blog 钩子本质是注册回调的数组,没有优先级参数,要调顺序只能动插件加载顺序。本文按 plugin.xml → RegisterPlugin → Add_Filter_Plugin → 触发执行四步拆开链路,并按场景列出常挂的钩子,附页头注入与后台加菜单两段可直接抄的代码,最后记三个实测坑:ActivePlugin 里别查库、文章与页面类型挂错、反复 ALTER TABLE 会报错。

先说个真事。前两天我给主题加导航下拉,顺手在首页塞了个「技术片段」板块,发完文章刷新一看——「暂无记录」。

查了半小时才反应过来:板块里取文章的函数把条件写死成 log_CateID = 9,而我把文章发在了子分类 zblog 下。Z-Blog 的 GetList 不会自动带上子分类,这一点它跟 WordPress 一样,都不带。子分类的文章父分类里是查不到的,得自己拼。

那要改吗?当然要。但我不碰 zb_system/。这篇就说说为什么不碰,以及钩子这套东西到底是怎么跑起来的——不是查字典那种解释,是我自己顺了一遍之后的版本。

一、改核心文件爽三分钟,后面全是账

改 zb_system/ 下的文件最快,改完立刻生效,没有比这更爽的。问题是这笔账迟早要还:升级一次,你自己都不记得改过哪几行;哪天出 bug,排查范围直接从「我的插件」扩大到「整个核心」;再往前一步,你想把站迁走或者交给别人接手,对方打开一看满目疮痍。

所以我给自己定了条规矩:能在插件 include.php 里用 Add_Filter_Plugin() 解决的,一律不去动模板和核心。这条规矩救过我不少次,尤其是升级的时候。

二、钩子是怎么跑起来的

别看文档写得玄乎,拆开就四步:

plugin.xml
   ↓
include.php  →  RegisterPlugin('MyPlug', 'ActivePlugin_MyPlug')
   ↓(后台启用插件时执行一次)
ActivePlugin_MyPlug()  →  Add_Filter_Plugin('钩子名', '回调函数名')
   ↓(运行到该钩子时)
按注册顺序挨个调用

看懂这张图,下面三件事你就不会搞错了:

  • 钩子本质就是数组。注册就是往数组里塞函数名,触发时 foreach 一遍。所以先注册的先执行,没有优先级参数这回事。
  • 想调顺序,只能动插件加载顺序。后台插件管理里拖,或者改 ZC_USING_PLUGIN_LIST。单个钩子想加权?没有。
  • 别把 WordPress 的 add_action 搬过来。Z-Blog 里叫 Add_Filter_Plugin,参数是钩子名 + 可调用函数名。闭包能传,但我劝你别传,出问题的时候你连函数名都搜不到。

三、我常挂的钩子,按场景排

下面这些钩子名不是从文档里抄的,是直接解析本站 zb_system/function/c_system_plugin.php 拿到的,复制就能用。

我要干什么挂哪个备注
全局初始化、埋点、收尾Filter_Plugin_Zbp_PreLoad
Filter_Plugin_Zbp_Load
Filter_Plugin_Zbp_Terminate
Load 里请务必跳过 zb_system 和 zb_users/plugin,不然把自己搞挂
注册自定义模板标签Filter_Plugin_Zbp_BuildTemplate
Filter_Plugin_Zbp_MakeTemplatetags
模板里想写 {mytag} 就靠它
前台页面注入Filter_Plugin_Index_Begin
Filter_Plugin_ViewList_Begin_V2
Filter_Plugin_ViewPost_Begin
Filter_Plugin_ViewSearch_Begin
首页、列表、文章、搜索,各挂各的
后台加菜单加样式Filter_Plugin_Admin_LeftMenu
Filter_Plugin_Admin_TopMenu
Filter_Plugin_Admin_Header
LeftMenu 传的是引用,往里塞就行
给编辑页加字段Filter_Plugin_Edit_Begin
Filter_Plugin_Edit_Response
实测 Response 用来输出表单
改查询结果、拦 SQLFilter_Plugin_GetList_Result
Filter_Plugin_DbSql_Filter
想做相关推荐可以在这里动手
登录审计Filter_Plugin_VerifyLogin_Succeed
Filter_Plugin_VerifyLogin_Failed
登录失败次数限制就挂这两个

表格里没有的,自己用 grep -n "Filter_Plugin_" c_system_plugin.php 翻一遍,比搜教程靠谱得多。

四、两个能直接抄的例子

例 1:全站页头塞一段 meta

function ActivePlugin_MyPlug() {
    Add_Filter_Plugin('Filter_Plugin_Index_Begin', 'MyPlug_Index_Begin');
}

function MyPlug_Index_Begin() {
    global $zbp;
    $zbp->header .= '<meta name="x-myplug" content="1">';
}

这里有个字要注意:是 .=,不是 =。写成 = 的话主题自己加的 meta 会被你一句话抹掉,页面看着正常,其实一堆东西没了。我第一次写的时候就是这么干的。

例 2:后台左侧加一个菜单

function ActivePlugin_MyPlug() {
    Add_Filter_Plugin('Filter_Plugin_Admin_LeftMenu', 'MyPlug_Admin_LeftMenu');
}

function MyPlug_Admin_LeftMenu(&$leftMenus) {
    $leftMenus['nav_myplug'] = array(
        'id'     => 'myplug',
        'name'   => '我的插件',
        'url'    => $GLOBALS['zbp']->host . 'zb_users/plugin/MyPlug/main.php',
        'target' => '_self',
    );
}

参数是数组引用,注意那个 &。直接改数组,千万别 return——return 出去没人接,你会盯着后台刷新十遍以为缓存没清。

五、我踩过的三个坑

  1. 别在 ActivePlugin_ 里查数据库。这个函数在插件启用那一刻执行,此时连 $zbp 的数据库状态都不一定就绪。我在这里写过一次 GetList,结果是白屏,连错误提示都没有,纯白。
  2. 文章和页面是两个东西。article 和 page 是不同的 Post 类型,钩子挂错的表现就是「文章页生效、单页不生效」,或者反过来。遇到这种灵异现象先看这里。
  3. 改表结构别偷懒。不要指望在 ActivePlugin_ 里无条件 ALTER TABLE,第二次启用就报错。老老实实用 SHOW COLUMNS 判断字段在不在,再决定加不加。

六、下一步

第 2 篇进前台实战:首页、列表页、文章页、搜索页各自该挂哪个钩子,_V2 版本和老版参数差在哪,以及为什么混用会直接白屏。第 3 篇写后台(加菜单、加字段、加列表列),第 4 篇讲数据层和插件升级。

最后提醒一句:本文所有钩子名都取自本站核心文件的实际解析结果,Z-Blog 1.7 系列可以直接用。但版本升级后钩子增删是常事,抄之前最好在你自己站上 grep 一遍。