Elythra's Blog

黄昏下空无一人的站台,只剩下无尽的孤寂。

TeoSeo 插件导致前台文章空白

记录一次因为使用不成熟插件导致文章不显示的问题与解决过程

事故

我用ABStore下载了这个插件,TeoSeo。
启用之后,博客爆了。

  • 启用 TeoSeo 插件后,网站前台文章正文完全不显示。
  • 页面其他部分(页头、页脚、侧边栏、标题、评论等)全部正常。
  • 关闭插件后问题依旧,文章正文仍然空白。
  • 数据库中的文章数据完好无损,后台能正常看到所有文章。

不可能是我刚换的主题问题,而且在启用这个插件之前还好好的。那么问题很显然。

排查

我反复检查了后台设置,结果无济于事,索性将目光转移到代码上。
打开插件源码,看着那一堆AI味浓浓的注释,我就知道这事情变得搞笑起来。
原因出在这里:

public static function markdown(?string $text): ?string
{
    if (!self::geoEnabled()) {
        return null;   // ← 致命错误
    }
    if (null === $text || false === strpos($text, '<details')) {
        return null;   // ← 致命错误
    }
    ...
}

Typecho的markdown是一个filter钩子,约定是传入原始Markdown文本,返回处理后的文本。当钩子返回null时,Typecho会把这个null当作最终结果传给后续渲染流程,文章正文就被“清空”了。而GEO开关默认是关闭的,所以只要插件启用,每一篇文章的正文都会被这个钩子清空。
一句话来说,就是只要启用插件,你的前台文章阅读就会全部爆掉。
那为什么关闭插件也无济于事呢?
持续原因:钩子被写进了数据库,禁用插件并不会清除。
观察Typecho数据库的options表,你会发现一个plugins字段,json格式的。页面请求时,Typecho读取数据库里的handles配置来决定调用哪些钩子。
此外,当你禁用插件,这些钩子并不会自己消失,仍然被注册,并且在每次渲染文章时被调用。
这就是为什么“关闭插件后还是没用”。

除此之外还有小问题:钩子被重复注册了两遍!
查看数据库里的plugins配置,可以看到TeoSeo的每一个钩子都出现了两份(以我的为参照):

"Widget_Abstract_Contents:markdown":{
    "0":["TeoSeo_Plugin","markdown"],
    "0.001":["TeoSeo_Plugin","markdown"]
}

原因是插件在activate()里调用了 persistHooks(),而persistHooks()的逻辑是“读库 → 合并 → 写回”,但没有先去重。多次激活/自动更新后,同一钩子被反复追加,导致重复注册。
重复注册本身不致命,但会放大问题:只要有一份钩子存在并返回null,文章就没了。

解决方法

手动清理数据库中的残留钩子(我推荐)

编辑表把有关TeoSeo的一切都删了就可以。

修改插件代码,根治问题(如果你坚持要用这个插件)

修改Plugin.php,修复markdown()返回值:不处理时返回$text,绝不返回null。这样。

php
public static function markdown(?string $text): ?string
{
    if (!self::geoEnabled()) {
        return $text;
    }
    if (null === $text || false === strpos($text, '<details')) {
        return $text;
    }
    ...
}

修复persistHooks()去重:写回数据库前先移除旧的TeoSeo钩子,再写入新的,避免重复注册。
完善deactivate():主动从options.plugins中清除TeoSeo的所有钩子,而不是只依赖Typecho自动处理。

顺从插件(GEO优化开关打开)

不过这个好像会因为某些主题不支持而根本无法打开。

结论

请各位大佬用AI写代码后自己好好把所有功能都测试一遍再发布正式版,又不费什么时间。

发表评论

电子邮件地址不会被公开。 必填项已用*标注