Typecho 1.3 主题 functions.php 钩子失效与修复
Author: AstrsourceCreated Jul 16, 2026Updated Jul 17, 2026
Labelsbug
描述这个 Bug
在 Typecho 1.3.0 版本中,于主题 functions.php 内通过 Plugin::factory 注册 contentEx / excerptEx 钩子,实现短代码替换功能:
\Typecho\Plugin::factory('Widget\Abstract\Contents')->contentEx = ['ContentFilter', 'parseContent'];
\Typecho\Plugin::factory('Widget\Abstract\Contents')->excerptEx = ['ContentFilter', 'parseContent'];
class ContentFilter
{
public static function parseContent($content, $widget, $lastResult = null): string
{
$html = $content;
$html = self::handleShortcodes($html);
return $html;
}
private static function handleShortcodes($html)
{
$html= preg_replace_callback(
'/\[btn\s+url="([^"]+)"\](.*?)\[\/btn\]/i',
function ($matches) {
$url = trim($matches[1], '`');
$text = $matches[2];
return '<a href="' . $url . '" class="custom-btn">' . $text . '</a>';
},
$html
);
return $html;
}
}注册语句本身能正常执行(通过打印确认 hook 已注册成功),但实际渲染时短代码替换完全不生效。
##核心原因
Typecho1.3.0版本__get()属性引入结果缓存,functions.php 的钩子注册晚于 singleHandle,首次触发时钩子不存在,缓存了未替换的原文,后续读取缓存永远跳过钩子。
##修复
将 functions.php 的加载拆为两步:require_once(注册钩子)提前到 handle 调用之前,themeInit()(使用文章数据)保持在 handle 之后。 修改文件:var/Widget/Archive.php 的 execute() 方法。
/** 提前加载皮肤函数,使钩子在 handle 触发 content 链之前注册 */
$functionsFile = $this->themeDir . 'functions.php';
$hasFunctionsFile = (!$this->invokeFromOutside || $this->parameter->type == 404 || $this->parameter->preview)
&& file_exists($functionsFile);
if ($hasFunctionsFile) {
require_once $functionsFile;
}
if (isset($handles[$this->parameter->type])) {
$handle = $handles[$this->parameter->type];
$this->{$handle}($select, $hasPushed);
} else {
$hasPushed = self::pluginHandle()->call('handle', $this->parameter->type, $this, $select);
}
/** 在 handle 执行完毕后调用 themeInit,确保此时已有文章数据 */
if ($hasFunctionsFile && function_exists('themeInit')) {
themeInit($this);
}Source: typecho/typecho