开发文档

主题与插件开发完整指南

插件开发 主题开发

插件开发

插件目录结构

public/plugins/my-plugin/ ├── config.json # 插件配置(必须) ├── hooks.php # 钩子注册 ├── install.php # 安装脚本 ├── uninstall.php # 卸载脚本 ├── admin.php # 后台页面 ├── api.php # API接口 └── static/ # 静态资源

config.json 配置

{ "name": "插件名称", "app_id": "plugin-slug", "version": "1.0.0", "description": "插件描述", "author": "作者", "type": 1, "system_requirement": "FmBlog v1.0.0以上", "php_version": "7.4+", "is_system": 0, "preview": "preview.jpg", "hooks": ["admin_menu", "admin_route"], "config": { "option1": "value1" } }
字段必填说明
name插件名称
app_id插件标识(字母数字下划线短横线)
version版本号 (x.y.z)
type1=插件, 2=主题
preview预览图路径
hooks注册的钩子列表
config默认配置对象
is_system1=系统插件不可卸载
system_requirement系统要求
php_versionPHP版本要求

安装脚本 install.php

function plugin_install() { global $db; // 创建数据表 $db->query("CREATE TABLE IF NOT EXISTS fm_myplugin_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP )"); return true; }

注册后台菜单 hooks.php

// 注册后台菜单 Hook::add('admin_menu', function($menu) { $menu['功能扩展'][] = [ '我的插件', '?a=myplugin', 'layui-icon-component', 'admin' ]; return $menu; }); // 注册后台路由 Hook::add('admin_route', function($routeMap) { $routeMap['myplugin'] = 'myplugin_index'; return $routeMap; });

后台页面开发

在 admin.php 中创建后台页面函数:

function myplugin_index() { global $db; fmblog_title('我的插件', '插件管理页面'); // 处理表单提交 if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = post('action'); // 处理逻辑... } // 渲染页面 include 'views/header.php'; // 页面内容... include 'views/footer.php'; }

API 接口开发

在 api/ 目录下创建接口文件,如 api/myplugin.php:

// api/myplugin.php // 函数命名格式: api_{模块}_{动作} // 调用方式: api.php?m=myplugin&a=list function api_myplugin_list() { global $db; api_check_login(); $list = $db->name('myplugin_data')->select(); api_json_success(['list' => $list]); } function api_myplugin_add() { global $db; api_check_post(); $name = post('name', ''); if (empty($name)) { api_json_error('名称不能为空'); } $id = $db->name('myplugin_data')->insert([ 'name' => $name, 'create_time' => date('Y-m-d H:i:s') ]); api_json_success(['id' => $id], '添加成功'); } function api_myplugin_delete() { global $db; api_check_login(); $id = intval(get('id', 0)); if ($id <= 0) { api_json_error('ID无效'); } $db->name('myplugin_data')->where('id', $id)->delete(); api_json_success(null, '删除成功'); }

调用示例:

// 获取列表 GET /api.php?m=myplugin&a=list // 添加数据 POST /api.php?m=myplugin&a=add Body: {"name": "测试"} // 删除数据 GET /api.php?m=myplugin&a=delete&id=1

常用钩子列表

钩子名称说明
admin_menu注册后台菜单
admin_route注册后台路由
article_add_before文章添加前
article_add_after文章添加后
article_edit_before文章编辑前
article_edit_after文章编辑后
article_delete_before文章删除前
article_delete_after文章删除后
cate_add_after分类添加后
cate_edit_after分类编辑后
cate_delete_after分类删除后
theme_switch_before主题切换前
theme_switch_after主题切换后
应用商店相关
app_store_install_before应用安装前
app_store_install_after应用安装后
app_store_uninstall_before应用卸载前
app_store_uninstall_after应用卸载后
单页相关
page_add_after单页添加后
page_edit_after单页编辑后
page_delete_after单页删除后

主题开发

主题目录结构

public/theme/my-theme/ ├── config.json # 主题配置 ├── fun.php # 主题函数 ├── header.php # 页头模板 ├── footer.php # 页脚模板 ├── home.php # 首页模板 ├── single.php # 文章详情 ├── category.php # 分类列表 ├── page.php # 默认单页 ├── page_about.php # 自定义单页模板 ├── 404.php # 404页面 └── static/ # 静态资源

config.json 配置

{ "name": "主题名称", "slug": "theme-slug", "version": "1.0.0", "description": "主题描述", "author": "作者", "type": "theme", "preview": "preview.jpg", "settings_page": "settings.php" }
字段必填说明
name主题名称
slug主题标识
version版本号
preview预览图路径
settings_page后台设置页面

模板变量

变量说明
$GLOBALS['site_config']网站配置
$GLOBALS['current_article']当前文章数据
$GLOBALS['current_category']当前分类数据
$GLOBALS['current_page_data']当前单页数据
$GLOBALS['current_page']当前页码

站点信息函数

get_config('webtitle'); // 站点名称 get_config('seodesc'); // 站点描述 get_config('site_url'); // 站点URL get_home_url(); // 首页URL

URL 生成函数

get_article_url($article); // 文章URL get_category_url($cate); // 分类URL get_page_url($page); // 单页URL theme_url('css/style.css'); // 主题资源URL

数据获取函数

// 获取导航菜单 $menus = get_nav_menus(); // 获取分类列表 $categories = get_categories(); // 获取置顶文章 $topArticles = get_top_articles(5); // 获取最新文章 $recentArticles = get_recent_articles(10); // 获取分类文章(带分页) $result = get_articles_by_category($cateId, 10, 1); // 返回: ['list' => [...], 'total' => 100, 'page' => 1, 'pages' => 10] // 搜索文章 $result = search_articles($keyword, 10, 1);

辅助函数

// 截取摘要 echo excerpt($article['content'], 200); // 格式化日期 echo format_date($article['create_time'], 'Y年m月d日'); // 分页HTML echo pagination($current, $totalPages, '/?p='); // 设置页面标题 set_page_title($title, $isHome); // 获取页面标题 echo get_page_title(); // 获取文章导航(上一篇/下一篇) $nav = get_article_nav($article); // 返回: ['prev' => [...], 'next' => [...]]

首页模板 home.php 示例

<?php load_theme_file('header.php'); $articles = get_articles_paged(10, $GLOBALS['current_page'] ?? 1); foreach ($articles['list'] as $article): ?> <article> <h2><a href="<?php echo get_article_url($article); ?>"> <?php echo htmlspecialchars($article['title']); ?> </a></h2> <p><?php echo excerpt($article['content'], 200); ?></p> </article> <?php endforeach; ?> <?php echo pagination($articles['page'], $articles['pages'], '/?p='); ?> <?php load_theme_file('footer.php'); ?>

文章详情 single.php 示例

<?php $article = $GLOBALS['current_article'] ?? null; if (!$article) { render_404(); exit; } set_page_title($article['title']); load_theme_file('header.php'); ?> <article> <h1><?php echo htmlspecialchars($article['title']); ?></h1> <div><?php echo $article['content']; ?></div> </article> <?php $nav = get_article_nav($article); if ($nav['prev']): ?> <a href="<?php echo get_article_url($nav['prev']); ?>">上一篇:<?php echo htmlspecialchars($nav['prev']['title']); ?></a> <?php endif; ?> <?php load_theme_file('footer.php'); ?>

主题钩子 hooks.php

主题可以通过钩子系统扩展后台功能,在主题目录下创建 hooks.php 文件:

public/theme/{主题名}/hooks.php
钩子名称参数说明
后台管理
admin_menu$menu注册后台菜单
admin_route$routeMap注册后台路由
admin_header_logo后台头部Logo区域
admin_dashboard_stats$stats后台仪表盘统计
admin_article_form_after$article文章表单后
admin_page_form_after$page页面表单后
admin_cate_form_after$cate分类表单后
admin_user_form_after$user用户表单后
前台
before_page_render[]页面渲染前
文章操作
article_list_after$result文章列表获取后
article_add_before$data文章添加前
article_add_after$data文章添加后
article_edit_before['id','data','old']文章编辑前
article_edit_after['id','data','old']文章编辑后
article_delete_before['id','article']文章删除前
article_delete_after['id','article']文章删除后
article_get_after$article文章获取后
渲染
render_rich_editor['name','value','options']渲染富文本编辑器

钩子使用示例

<?php if (!defined('IN_FMCMS')) { exit('Access Denied'); } // 后台头部添加主题设置链接 Hook::on('admin_header_logo', function() { echo '<a href="?a=theme_settings">主题设置</a>'; }); // 文章表单后添加自定义字段 Hook::on('admin_article_form_after', function($article) { $customValue = $article['custom_field'] ?? ''; echo '<div class="layui-form-item">'; echo '<label class="layui-form-label">自定义字段</label>'; echo '<div class="layui-input-block">'; echo '<input type="text" name="custom_field" value="' . htmlspecialchars($customValue) . '" class="layui-input">'; echo '</div></div>'; return $article; }); // 后台仪表盘添加统计卡片 Hook::on('admin_dashboard_stats', function($stats) { global $db; $totalViews = $db->name('article')->sum('views'); $stats['total_views'] = [ 'label' => '总浏览量', 'value' => $totalViews, 'sub' => '累计', 'icon' => 'layui-icon-chart' ]; return $stats; });