模板层级与继承规则
Typecho 的模板系统遵循“约定优于配置”的原则,核心在于文件名与请求类型的映射。所有模板文件位于主题目录下(如 usr/themes/mytheme/),入口文件固定为 index.php。当用户访问不同页面时,Typecho 按以下顺序查找并加载模板:
- 首页:优先
index.php,若不存在则回退到archive.php,最后是index.php(必须存在)。 - 文章页:
post.php→single.php→archive.php→index.php。 - 独立页面:
page.php→single.php→archive.php→index.php。 - 分类/标签/归档:
category.php/tag.php/date.php→archive.php→index.php。 - 搜索页:
search.php→archive.php→index.php。 - 404 页面:
404.php→index.php。
注意:模板继承是“回退”而非“覆盖”。若post.php不存在,Typecho 会使用single.php,而不是自动包含index.php的内容。
此外,functions.php 是主题的功能入口,用于注册钩子、定义辅助函数。comments.php 负责评论区域渲染。所有模板文件最终通过 $this 对象访问当前请求的数据。
$this 常用方法与自定义字段读取
在模板中,$this 是 Widget_Archive 或 Widget_Abstract 的实例,封装了几乎所有运行时数据。常用方法如下:
$this->archiveTitle():输出当前页面的标题(如分类名、文章标题)。$this->title():输出文章标题。$this->content():输出文章正文(已过滤)。$this->excerpt():输出摘要。$this->permalink():输出当前文章/页面的永久链接。$this->date():输出发布时间,可传格式参数。$this->author():输出作者名。$this->categories():输出分类链接。$this->tags():输出标签链接。$this->commentsNum():评论数量。$this->fields:当前文章的自定义字段对象(数组)。
自定义字段(Custom Fields)是 Typecho 内置的扩展机制,允许在文章编辑页添加键值对。读取方式有两种:
// 方式一:通过 $this->fields 直接读取
$price = $this->fields->price; // 若字段不存在,返回 null
// 方式二:使用 getField 方法(需在 functions.php 中定义辅助函数)
function getCustomField($key, $default = '') {
$fields = Typecho_Widget::widget('Widget_Abstract_Contents')->fields;
return isset($fields->$key) ? $fields->$key : $default;
}自定义字段的值默认经过htmlspecialchars转义,若需输出 HTML,请使用$this->fields->price后手动处理。
插件钩子:Plugin::factory 注册与调用时机
Typecho 的插件钩子(Plugin Hooks)是扩展核心行为的关键。钩子通过 Typecho_Plugin::factory() 注册,返回一个代理对象,允许你在特定时机注入代码。注册通常在 functions.php 或插件文件的 activate 方法中完成。
注册方式:
Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('MyPlugin', 'beforeRender');调用时机:
Widget_Archive的beforeRender:在模板渲染前触发,可修改$this对象的数据。Widget_Archive的afterRender:在模板渲染后触发,适合输出额外内容。Widget_Contents_Post_Edit的finishPublish:文章发布后触发,用于保存自定义字段。Widget_Feedback的comment:评论提交后触发,用于过滤或通知。
钩子名称区分大小写,且必须与核心类名完全一致。未注册的钩子不会报错,但也不会执行。
完整可运行示例:为文章添加“阅读时长”字段
下面实现一个主题功能:在文章编辑页添加“阅读时长”自定义字段,并在文章页输出。
第一步:在 functions.php 中注册钩子
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
// 注册文章发布/编辑完成时的钩子,保存自定义字段
Typecho_Plugin::factory('Widget_Contents_Post_Edit')->finishPublish = array('MyTheme', 'saveReadingTime');
class MyTheme {
// 保存字段
public static function saveReadingTime($contents, $class) {
$fields = $class->request->get('fields');
if (isset($fields['readingTime'])) {
$contents['fields']['readingTime'] = $fields['readingTime'];
}
return $contents;
}
}
// 辅助函数:获取阅读时长
function getReadingTime($default = '未知') {
$fields = Typecho_Widget::widget('Widget_Abstract_Contents')->fields;
return isset($fields->readingTime) ? $fields->readingTime : $default;
}第二步:在 post.php 中输出
<article>
<h1><?php $this->title(); ?></h1>
<div class="meta">
<span>阅读时长:<?php echo getReadingTime(); ?> 分钟</span>
</div>
<div class="content"><?php $this->content(); ?></div>
</article>第三步:在文章编辑页添加字段输入框(通过 admin/write-post.php 的钩子或直接修改主题的 functions.php 注入表单)。
此示例展示了Plugin::factory的注册、$this->fields的读取以及自定义字段的保存流程。注意:finishPublish钩子返回的$contents必须原样返回,否则会丢失数据。
模板继承与钩子对比
| 特性 | 模板继承 | 插件钩子 |
|---|---|---|
| 作用范围 | 页面渲染 | 核心流程任意环节 |
| 修改方式 | 覆盖文件 | 注册回调 |
| 优先级 | 文件存在即生效 | 按注册顺序执行 |
| 典型用途 | 布局、样式 | 数据过滤、扩展字段 |
| 是否可逆 | 删除文件即恢复 | 需注销钩子 |
常见问题
1. 模板文件命名错误会导致什么?
Typecho 会回退到 index.php,若 index.php 也不存在,则直接报错。建议至少保留 index.php 和 style.css。
2. 自定义字段的值为什么输出为空?
检查字段是否在编辑页正确保存,以及是否在模板中使用了 $this->fields->key。若字段未定义,返回 null,需用 isset 判断。
3. 插件钩子注册后没有执行?
确认钩子名称与核心类完全一致,且注册代码在 activate 或 functions.php 中执行。部分钩子需要特定参数,参考官方文档。
4. 如何调试模板中的变量?
使用 var_dump($this->fields) 或 print_r($this->fields) 输出到页面,或开启 Typecho 的调试模式查看日志。
评论 (0)