鸡肋的pingback功能?
linkback,可以说是博客圈里最无用但最代表博客精神的特性了。
而pingback,作为linkback四大无用金刚的其中之一,在我眼里算是有点用的。
很多人会觉得,对于博主和博客来说,linkback有什么必要呢?没有增加网络互联的有效性,只会徒增服务器压力,还增加安全隐患。充其量只是完成了博文互联关系的充分性的记录而已。有没有linkback,就像你有没有上户口一样,不管怎样,你都能活不是,有了它,你也没有活得更好不是。
可你猜怎么着,我就看中pingback的互联信息的记录功能了!而且它理论上还是全自动的!
一直以来,支持我做博客的一个出发点,是希望我的所有记录,可以有一个hub来集成;并且,在这个hub之上,我可以建成一个知识星球。于是,所有的博文应该是有机耦合的,它们共同组成了一个生命体,而不只是碎片化的、扁平化的、流水帐的、新闻消费品一样的文章集合。
远的不说,作为一名崇尚轻松自由的博主,我希望在写一篇文章的时候,可以想写就写,不必把所有的点都写完。日后想补充一些内容的时候,我可以自由地选择,是对原有文章进行修订,还是新开一篇扩展的文章。当我选择新开一篇文章的时候,我可以不必重复阐述原来文章的内容,或者我新开的文章可以是原来文章的前情提要。
看到没有,当我这么构思的时候,我其实需要的是有点类似编程中的内联(inline)或者包含(include)概念。众所周知,inline和include并不需要我手动去做复杂的管理的。于是,在博文中,我要如何优雅地实现这样的需求呢?
答案就是pingback。首先,我在我文章中引用一篇文章的时候,我可以选择嵌入。这时候,嵌入的卡片会显示这个文章的一些摘要。在你嵌入之后后,pingback功能会恰到好处地闪耀登场,它会自动地联系我的引用原文,在原文文末自动添加一个评论,把看到我原文章的读者引流到新文章去。于是,这两篇文章便完成了耦合,而且我不需要手动地对两篇文章都进行更新,或者做很多工作。我需要做的,只是添加原文章的链接,仅此而已。
所以,在我看来,linkback提供的社交功能的确是多余的,但它提供的自动化联结功能却没有被大家有效地开发。而且,网上甚至有插件和教程,让你屏蔽博客内自己的文章之间的互相linkback!可在我看来,我反而只需要我自己文章之间的互相linkback。
理想很丰满,现实很骨感?
说了这么多,但实际操作你就会发现,因为缺乏用户群和去中心化等原因,实际上linkback功能很bug。
首先,WordPress执行pingback的机制不太透明,执行过程也不太鲁棒,管理上也不太规范。通常它是在文章有改动或发表时,检测一遍文章的外链情况,然后如果检测到该外链的协议头有x-pingback标记,就会发起一次pingback。
但如果此次pingback失败,后续就不太可能再被触发了,因为WordPress会为文章维护一份已pungback的列表,这个外链可能会被标记为已pungback;又或者,它已经执行了pingback,但因为某些原因,没有写入已pungback列表,后续又pingback一次的话,该外链页面上可能就会出现两次pingback评论。
而且,pingback貌似不是立即执行的,因为请求是先放入cron job列表的,这就有可能会被无限期延后执行。
再者,这个执行的结果,是完全没有输出的,你不知道它执行了没有,执行是否失败,失败的原因是什么。反正花落谁家,全凭运气。
换言之,如果你的pingback出了问题,你很难去调试或者手动修改它。你不知道问题出在哪里,也很难有重来的机会。
那该如何做呢?
如前述,我们迫切需要能够调试或者手动执行pingback的条件。
想要调试WordPress,可以参考官方的调试说明。
至于手动执行pingback,请接着往下看。
如何优雅地手动执行pingback?
经过代码调研,我发现,你要对你的一篇文章中的外链进行手动pingback的话,WordPress提供了一个很好用的函数:pingback()
具体而言,比如你要对你的一篇id为1314的博文中的外链进行一次pingback,你只需要运行一次pingback( null, 1314 ),即可。
可事情真的这么简单吗?
非也。
执行pingback,是需要对外发起http/xml请求的,于是,这个函数需要在完整的WordPress Frontend context环境中执行,而不能在URL context或者core context环境中执行。否则,即使你找到了执行的办法,而过程中开始检测外链时,你还是会遇到rewrite rules为null的问题,而且无法优雅地绕过。
换言之,pingback的执行,需要在functions.php管得到的上下文环境中以钩子的形式注入到frontend action中才能成功的。可看到functions.php估计大家就犯怵了:难道就只能去主题的functions.php中把这句代码作为钩子挂到某个frontend的action,然后手动刷新某个页面或者通过其它触发手段,大费周章一番,才能让这么一句代码被执行吗?
非也。
在我看来,最优雅的解决方案,是借助WP Crontrol插件。
这个插件,对比很多单纯的snippet插件,它是把你的代码片段放到WordPress的cron job环境中来执行的。得益于cron job的设计,它可以为你提供core、URL、PHP(frontend)三种上下文环境。
所以,我们只需要添加这个插件,然后在插件中添加一个PHP cron event,然后把这句话作为执行对象填入即可:

至于next run的时机,你可以填一个距现在很远的未来时刻。因为我们并不需要它真的被定时执行,我们只是需要在保存以后,通过强制的run once手动触发它运行一次,仅此而已。
可有后话?
在最后,可能有人会问,这个函数一次只能处理一篇文章,会不会太麻烦了?有没有一次处理所有文章的函数入口?
有倒是有的,是do_all_pingbacks()。它原本是作为’do_pings’ action的其中一个钩子而存在的,而且它的内部,也是调用pingback()函数进行处理的。所以它就是你寻找的函数。
不过,我需要指出的是,这个钩子并非会以我们期望的方式进行操作,它默认只会处理后台的那些被添加了_pingme内部标记的文章。
首先,这个标记并没有规范的增删改查接口在管理;其次,你很难通过修改do_all_pingbacks()的内部查询方式,来刚好过滤出符合你要求的文章(本质上还是meta_key内部标记管理太差导致的);最后,你其实也不会想要对所有文章进行一次pingback检查的,因为动静太大了。
所以,我更推荐你,把pingback()函数多写几行,然后手动地把你需要处理的文章的id一一填入。而不要像曾经的我那样,在那傻傻地捯饬半天,最后徒增伤悲。