开发文档

主题与插件开发完整指南

目录结构

插件目录

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"]
}
字段类型必填说明
namestring必填插件名称
app_idstring必填插件标识
versionstring必填版本号
typeint必填1=插件, 2=主题(应用商店)
hooksarray注册的钩子列表

主题 config.json

{
    "name": "主题名称",
    "app_id": "theme-slug",
    "version": "1.0.0",
    "description": "主题描述",
    "author": "作者",
    "preview": "preview.jpg",
    "settings_page": true,
    "settings": [{...}]
}
字段类型必填说明
namestring必填主题名称
app_idstring必填主题标识
versionstring必填版本号
previewstring预览图
settings_pagebool启用后台设置
settingsarray设置字段定义

安装脚本

插件安装时自动执行,创建数据表、初始化配置。

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.php404 页面
// 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_editoroptions富文本编辑器
cate_add_after$data分类添加后
cate_edit_after$data分类编辑后
cate_delete_after$data分类删除后
page_add_after$data单页添加后
page_edit_after$data单页编辑后
page_delete_after$data单页删除后