目录结构
插件目录
plugins/my-plugin/
config.json # 插件配置(必须)
hooks.php # 钩子注册
install.php # 安装脚本
uninstall.php # 卸载脚本
admin.php # 后台页面
api/
{module}.php # API 接口
static/ # 静态资源
主题目录
theme/my-theme/
config.json # 主题配置(必须)
fun.php # 主题函数
hooks.php # 钩子注册
header.php # 页头模板
footer.php # 页脚模板
home.php # 首页
single.php # 文章详情
category.php # 分类列表
page.php # 默认单页
page_{slug}.php # 自定义单页
search.php # 搜索结果
404.php # 404 页面
preview.jpg # 预览图
配置文件
插件 config.json
{
"name": "插件名称",
"app_id": "plugin-slug",
"version": "1.0.0",
"description": "插件描述",
"author": "作者",
"type": 1,
"php_version": "7.4+",
"hooks": ["admin_menu", "admin_route"]
}
| 字段 | 类型 | 必填 | 说明 |
| name | string | 必填 | 插件名称 |
| app_id | string | 必填 | 插件标识 |
| version | string | 必填 | 版本号 |
| type | int | 必填 | 1=插件, 2=主题(应用商店) |
| hooks | array | 否 | 注册的钩子列表 |
主题 config.json
{
"name": "主题名称",
"app_id": "theme-slug",
"version": "1.0.0",
"description": "主题描述",
"author": "作者",
"preview": "preview.jpg",
"settings_page": true,
"settings": [{...}]
}
| 字段 | 类型 | 必填 | 说明 |
| name | string | 必填 | 主题名称 |
| app_id | string | 必填 | 主题标识 |
| version | string | 必填 | 版本号 |
| preview | string | 否 | 预览图 |
| settings_page | bool | 否 | 启用后台设置 |
| settings | array | 否 | 设置字段定义 |
安装脚本
插件安装时自动执行,创建数据表、初始化配置。
function plugin_install()
{
global $db;
// 创建数据表
$db->query("CREATE TABLE IF NOT EXISTS fm_my_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
status INTEGER DEFAULT 1,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
)");
return true;
}
钩子系统
hooks.php 是插件的事件注册中心,所有功能通过钩子扩展。
// hooks.php
if (!defined('IN_FMCMS')) exit('Access Denied');
// 注册后台菜单
Hook::on('admin_menu', function($menu) {
$menu['扩展功能'][] = [
'菜单名称', '?a=my_plugin',
'layui-icon-component', 'all'
];
return $menu;
});
// 注册后台路由
Hook::on('admin_route', function($routeMap) {
$routeMap['my_plugin'] = 'plugin_admin_page';
return $routeMap;
});
后台页面
在 admin.php 创建路由函数,实现后台管理界面。
// admin.php
function plugin_admin_page()
{
global $db;
fmblog_title('插件管理', '管理插件数据');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = post('action');
}
$list = $db->name('my_data')->select();
?>
<div class="layui-card">
<div class="layui-card-header">数据列表</div>
<div class="layui-card-body">
<!-- 页面内容 -->
</div>
</div>
<?php
}
API 接口
在 api/{模块}.php 创建函数,通过 /api.php 调用。
// api/my_plugin.php
// 函数命名: api_{模块}_{动作}
function api_my_plugin_list()
{
global $db;
api_check_login();
$list = $db->name('my_data')->select();
api_json_success(['list' => $list]);
}
function api_my_plugin_add()
{
global $db;
api_check_post();
$title = post('title', '');
if (empty($title)) {
api_json_error('标题不能为空');
}
$id = $db->name('my_data')->insert([
'title' => $title,
'create_time' => date('Y-m-d H:i:s')
]);
api_json_success(['id' => $id], '添加成功');
}
模板系统
| 模板文件 | 对应页面 |
| home.php | 首页 |
| single.php | 文章详情 |
| category.php | 分类列表 |
| page.php | 默认单页 |
| page_{slug}.php | 自定义单页 |
| search.php | 搜索结果 |
| 404.php | 404 页面 |
// home.php 示例
<?php load_theme_file('header.php');
$articles = get_recent_articles(10);
foreach ($articles 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;
load_theme_file('footer.php'); ?>
模板变量
| 变量 | 可用页面 | 说明 |
| $GLOBALS['current_article'] | single.php | 当前文章数据 |
| $GLOBALS['current_category'] | category.php | 当前分类数据 |
| $GLOBALS['current_page_data'] | page*.php | 当前单页数据 |
| $GLOBALS['current_page'] | 所有页面 | 当前页码 |
| $GLOBALS['site_config'] | 所有页面 | 全站配置 |
// 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 class="content"><?php echo $article['content']; ?></div>
</article>
<?php load_theme_file('footer.php'); ?>
常用函数
URL 生成
get_home_url(); // 首页地址
get_article_url($article); // 文章地址
get_category_url($cate); // 分类地址
get_page_url($page); // 单页地址
get_search_url($keyword); // 搜索地址
theme_url('style.css'); // 主题资源地址
数据获取
get_nav_menus(); // 导航菜单
get_categories(); // 全部分类
get_category_name($cateId); // 分类名称
get_top_articles(5); // 置顶文章
get_recent_articles(10); // 最新文章
$r = get_articles_by_category($cateId, 10, 1); // 分类分页
$r = search_articles($keyword, 10, 1); // 搜索
辅助函数
excerpt($text, 200); // 截取摘要
format_date($dateStr, 'Y-m-d'); // 格式化日期
pagination($page, $totalPages); // 分页 HTML
get_article_nav($article); // 上/下一篇
主题钩子
主题目录下创建 hooks.php,注册后台扩展。
// theme/my-theme/hooks.php
<?php
if (!defined('IN_FMCMS')) exit('Access Denied');
// 文章表单后添加自定义字段
Hook::on('admin_article_form_after', function($article) {
$value = $article['custom_field'] ?? '';
echo '<div class="layui-form-item">';
echo '<label class="layui-form-label">自定义字段</label>';
echo '<input type="text" name="custom_field" value="' . htmlspecialchars($value) . '" class="layui-input">';
echo '</div></div>';
return $article;
});
主题设置
在 config.json 中定义 settings,系统自动生成设置页面。
| 类型 | 说明 | 额外属性 |
| text | 文本输入框 | upload: true 开启文件选择 |
| textarea | 多行文本 | - |
| switch | 开关 | 保存 "1" 或 "0" |
// fun.php 读取设置
function theme_get_setting($key = null, $default = '')
{
static $settings;
if ($settings === null) {
$rows = $GLOBALS['db']->name('config')
->where('type', 'theme_' . get_config('theme'))
->select();
foreach ($rows as $row) {
$settings[$row['conf_key']] = $row['conf_value'];
}
}
return $key === null ? $settings : ($settings[$key] ?? $default);
}
// 模板中使用
<?php echo theme_get_setting('site_logo'); ?>
<?php if (theme_get_setting('show_sidebar', '1') === '1'): ?>
<!-- 显示侧边栏 -->
<?php endif; ?>
钩子列表
后台管理
| 钩子 | 参数 | 说明 |
| admin_menu | $menu | 注册后台菜单 |
| admin_route | $routeMap | 注册后台路由 |
| admin_header_logo | - | 后台头部区域 |
| admin_dashboard_stats | $stats | 仪表盘统计 |
| admin_article_form_after | $article | 文章表单后 |
文章操作
| 钩子 | 参数 | 说明 |
| article_add_before | $data | 添加前 |
| article_add_after | $data(含id) | 添加后 |
| article_edit_before | ['id','data','old'] | 编辑前 |
| article_edit_after | ['id','data','old'] | 编辑后 |
| article_delete_before | ['id','article'] | 删除前 |
| article_delete_after | ['id','article'] | 删除后 |
其他
| 钩子 | 参数 | 说明 |
| before_page_render | [] | 页面渲染前 |
| render_rich_editor | options | 富文本编辑器 |
| cate_add_after | $data | 分类添加后 |
| cate_edit_after | $data | 分类编辑后 |
| cate_delete_after | $data | 分类删除后 |
| page_add_after | $data | 单页添加后 |
| page_edit_after | $data | 单页编辑后 |
| page_delete_after | $data | 单页删除后 |