前台钩子实战:首页、列表、文章页、搜索页到底能挂在哪
前台四类页面的钩子位置汇总:Index、List、Article、Search 前缀相同语义一致——Begin 插顶部、End 插底部、Core 改数据层、Content 改正文本身。附三个实测坑:Begin 触发时 head 已输出、漏写 & 引用改了无效、Article_Content 在后台预览也触发需要隔开。
上一篇把「钩子怎么加载、怎么注册」说清楚了,后台部分也顺手写了个示例。这篇接着往下走,说前台——也就是首页、分类列表、文章详情、搜索页这几个页面,插件到底能在哪些位置插手。
先说个小插曲。写这篇之前我拿一个临时插件把下面这些钩子挨个挂了一遍,结果第一个就翻车:在 Filter_Plugin_Index_Begin 里 echo 了一段 HTML,页面顶部确实出来了,但样式全乱。原因很简单——Index_Begin 触发的时候 <head> 已经输出了,你 echo 的东西落在 <body> 开头,而主题的 header 结构还没开,等于把一段 div 塞在了不该塞的地方。所以下面每个钩子我都会标一句「这时候页面上已经有什么」,省得你也踩这一脚。
一、首页:Index_Begin 和 Index_End 是两个完全不同的世界
首页的钩子按执行顺序排,其实就三处值得记:
| 我要干什么 | 挂哪个 | 这时候页面上已经有什么 |
|---|---|---|
| 往首页顶部塞一个公告条 / 横幅 | Filter_Plugin_Index_Begin | head 已输出,body 刚开始,header 结构还没出来 |
| 往文章列表下方塞分页、推荐位、广告 | Filter_Plugin_Index_End | 列表已渲染完,footer 还没出来,位置最稳 |
| 改首页文章列表本身(数量、排序、过滤) | Filter_Plugin_Index_Core | SQL 执行后、模板渲染前,改的是数据不是位置 |
注意最后一行的区别。Index_Begin 和 Index_End 是「在页面某个位置插东西」,拿到的参数是那个位置附近的东西;而 Index_Core 是在数据层面动手,你在里面拿到的是已经查出来的文章数组,改它比改模板稳得多。我第一次想「首页只显示带封面的文章」,绕了一大圈去改模板,其实在 Core 里循环一遍把没封面的 unset 掉就完了。
function mytheme_index_core(&$articles) {
foreach ($articles as $key => $article) {
if ($article->Metas->thumb == '') {
unset($articles[$key]);
}
}
}
这段挂到 Filter_Plugin_Index_Core 上就生效。注意是 &$articles,带引用的,不带引用等于白改。
二、分类列表页:List_Begin / List_End / List_Core
和首页几乎一一对应,换成 List 前缀就行:
Filter_Plugin_List_Begin—— 列表顶部,比如在分类页标题下面加一句分类描述Filter_Plugin_List_End—— 列表底部,分页后面那块空地方Filter_Plugin_List_Core—— 又是数据层,改当前分类的文章列表
这里有个我卡了半天的点:List_Core 拿到的参数里带的是「当前分类」和「文章数组」,但如果你只想对某个分类动手,得自己判断 $cate->ID。我一开始以为它会按分类分别触发,结果一次触发全站分类共用,判断写漏了,整个站所有分类的列表都被我筛成了 6 篇。刷新首页没事,点进任何一个分类都只有 6 篇——排查了二十分钟,最后发现是自己写的。
三、文章详情页:Article_Begin 里能做很多,但别乱来
文章页的钩子是最常被问到的,因为「文章正文下方加个相关推荐」几乎是每个站的需求:
| 我要干什么 | 挂哪个 | 备注 |
|---|---|---|
| 正文上方插内容(作者信息、更新时间) | Filter_Plugin_Article_Begin | 拿到的是当前 $article,可以读它的分类、标签 |
| 正文下方插内容(相关推荐、作者卡片) | Filter_Plugin_Article_End | 最常用,位置稳,不破坏正文结构 |
| 改正文本身(加目录、替换图片链接、插广告) | Filter_Plugin_Article_Content | 参数是 &$content 字符串,直接改正文 HTML |
| 改标题、改摘要、改 SEO 字段 | 也是 Article_Content 那几个变量 | 记得带引用,不带引用改了不生效 |
Article_Content 是我用得最多的一个。相关推荐用 End 插,正文处理用 Content 改——这两个别搞混。End 插的东西在正文容器外面,Content 改的东西在正文里面。我之前把广告代码挂到 Content 上,结果每个段落里都被塞了一遍,因为那是个过滤整篇正文的钩子,不是插一次就完事的。
四、搜索页:Search_Begin / Search_Core
搜索页相对冷门,但有个很实用的场景:搜索结果为空的时候给用户推点东西。
function mytheme_search_begin() {
global $zbp;
if (count($zbp->search_result) === 0) {
echo '<div class="empty-tip">没搜到?试试分类目录</div>';
}
}
Filter_Plugin_Search_Begin 触发时搜索结果已经查完了,所以能直接判断空不空。Search_Core 则是给你改搜索条件用的,比如把搜索范围限制在某个分类里——做站内搜索优化的时候会用到,但注意别把用户的搜索词搞丢了。
五、几个前台钩子共用的坑
这三条是我这一个星期实打实撞上的,写完就记下来了:
- echo 的位置比你想的重要。
Begin系列大多在 body 刚开、header 还没出来的时候触发,你 echo 的 div 会变成 header 的兄弟。要做浮层、横幅这类东西,挂End更安全。 - 引用符号漏一个就白干。
&$articles、&$content、&$article,函数签名里少写一个&,代码不报错、页面没变化,你会以为钩子没生效,其实是自己改的是副本。 - 前台钩子也会影响后台。 这点最坑。
Article_Content在后台预览文章时同样会触发,你插的广告代码会出现在后台编辑器预览里。要区分就判断$zbp->is_admin或者干脆在Load里只在前台注册。
六、小结
前台这四个页面,记住一个规律就够了:Begin 是页面顶部、End 是页面底部、Core 是数据层、Content 是正文本身。名字前缀换成 Index / List / Article / Search,覆盖你 90% 的插入需求。
下一篇想写「钩子拿到的东西怎么用」——比如 Article_Content 里怎么按分类取相关文章、怎么给正文自动加目录、怎么在列表页拿到当前分类的父子关系。这些才是真正花时间的地方,钩子名字反而不难记。
如果你手上正卡在某个位置插不进去,可以把「想插在哪、现在挂的哪个钩子、页面表现是什么」三句话丢过来,我对着这张表帮你对一下。