Hope CMS 文档

Hope CMS(希望CMS)是一款面向中文站长与 PHP 开发者的轻量开源内容管理系统:安装快、结构清楚、主题/插件零侵入扩展,适合个人博客、工作室站、资讯站与付费资源站。

请仅从官方域名下载安装包,其他来源存在安全风险。

命名说明

名称 用途
Hope CMS 程序主名称(文档、导航)
希望CMS 中文副名称

你能用它做什么

场景 说明
个人 / 技术博客 Markdown 写作、分类标签、评论、SEO
企业 / 工作室官网 独立页面模板、主题切换、侧栏组件
资讯 / 内容站 多作者角色、媒体库、伪静态链接
付费资源 / 会员站 个人中心、余额充值、支付网关(配合 Shop 等主题)
小程序 / 前后端分离 REST API(API Key、限流、CORS 视配置而定)

核心能力

  • 内容:Markdown(Editor.md)、分类 / 标签 / 封面 / 别名 / 置顶 / 阅读密码
  • 用户:管理员 / 编辑 / 作者 / 访客;后台模块权限
  • SEO 与链接:站点 / 文章 / 分类 / 标签 TDK;动态 / 伪静态 / index.php 三种链接模式
  • 扩展:挂载点(hope_listen / hope_emit)、主题、插件、侧边栏组件
  • 媒体与评论:附件库、评论审核、验证码、邮件通知
  • 个人中心:资料、余额、邀请奖励、在线支付(微信 / 支付宝等)
  • 运维:备份导入、更新缓存、SMTP、多语言后台(system/lang/)、AI 工作台(写作 / 对话 / 配图 / 模型配置)
  • 数据层:链式查询(HopeDb);插件在 callback_init 内建表
  • 支付:官方 / 虎皮椒 / 易支付 / 码支付;主题侧用 PayClient(见 支付接入

官方示例: 主题 content/theme/default/ · 插件 content/plugin/tips/

环境一览

项目 要求
PHP 7.0+(推荐 8.0+;建议同时验证 8.x)
数据库 MySQL 5.6+ / MariaDB 10.3+(mysqlipdo_mysql
Web Apache / Nginx(IIS 需 URL Rewrite)
扩展 建议 mbstringjsoncurlgd/imagickopenssl

详细步骤见 安装指南

文档导航

按阅读顺序:

分组 章节 内容
入门 安装指南 上传、安装向导、安全加固
入门 升级与更新 覆盖升级、插件/主题回调、更新缓存
入门 常见问题 安装排障、开发疑问
入门 目录说明 content/system/ 结构总览
开发基础 开发准备工作 目录、常量、请求流程、常用 API
开发基础 挂载点手册 HopeHooks 全表与 hope_listen / hope_emit
开发基础 数据库与 SQL HopeDb、建表、MetaStorage
主题 主题开发指南 目录结构、模板、CSF 设置、生命周期
主题 侧边栏开发说明 widgets 注册与渲染
插件 插件开发指南 结构、钩子、设置页、前台页、发布清单
接口 伪静态与路由 链接模式、服务器配置、插件路由
接口 API 开发文档 REST、个人中心 API、鉴权
接口 支付接入 PayClient / Pay_Store、回调、后台配置
关于 联系我们 官方渠道与反馈方式

目录总览见 目录说明

二次开发原则

  1. 不要改 system/ 业务逻辑(升级会覆盖);扩展放在 content/theme/content/plugin/
  2. 主题负责皮肤与布局;跨主题能力做成插件
  3. 站点标题 / 副标题 / SEO / 版权走后台「设置」(Settings::get('sitename') 等)
  4. 输出用户内容使用 htmlspecialchars;数据库用 HopeDb,禁止拼接不可信 SQL

官方域名

软件许可证

Hope CMS(希望CMS)核心代码按 Apache License 2.0 发布。捆绑的第三方库以其目录内说明为准。

安装指南

从拿到安装包到站点可访问,通常只需:上传 → 运行向导 → 加固安全

环境要求

项目 要求
PHP 7.0+(推荐 8.0+;建议同时验证 7.4 / 8.x)
数据库 MySQL 5.6+ / MariaDB 10.3+(mysqlipdo_mysql
Web Apache / Nginx(IIS 需 URL Rewrite)
扩展 建议:mbstringjsoncurlgdimagickopenssl

目录需可写:content/upload/content/cache/(Linux 常见 755/775,属主为 Web 用户)。


三步安装

第一步:上传程序

  1. 官方下载页 获取最新安装包并解压
  2. 将全部文件上传到网站根目录(虚拟主机常见 public_html / wwwroot),或子目录
  3. 确认 content/cache/content/upload/ 可写
子目录安装时,后续需正确配置站点 URL,伪静态还要改 RewriteBase / Nginx 前缀。

第二步:运行安装向导

浏览器访问:

http://你的域名/install.php

按向导填写:

说明
数据库主机 多为 localhost
库名 / 用户 / 密码 事先在面板或 phpMyAdmin 建空库
表前缀 默认一般可用;同库多套程序时请改前缀
管理员账号 站点最高权限,密码请足够复杂

完成后会写入 content/cache/install.lock,并尽量将 install.php 重命名为 install.php.disabled

第三步:删除安装入口并登录

  1. 立即删除或重命名 install.php(若仍存在)
  2. 访问前台域名确认主题正常
  3. 后台默认入口:admin.php(见下文安全加固)

安装后必做

  1. 登录后台
  2. 重命名后台入口:将根目录 admin.php 改为不易猜测的文件名(如 manage_x8k2.php),并更新书签
    仍使用 admin.php 时,示例插件「小贴士」会在后台首页提示安全风险
  3. 外观 → 主题:启用所需主题;有 settings.php 时可配置外观
  4. 设置 → 站点信息 / SEO:站点标题、副标题、描述、版权(主题通过系统配置读取)
  5. 设置 → 链接:选择动态 / 伪静态;伪静态需配置服务器,见 伪静态与路由
  6. 插件 → 插件管理:按需启用
  7. 按需配置 SMTP、支付网关
  8. 执行一次 设置 → 更新缓存

重装

  1. 备份数据库与 content/upload/
  2. 删除 content/cache/install.lock
  3. 如程序要求,设置环境变量 HOPE_ALLOW_REINSTALL=1
  4. 必要时从 install.php.bak 恢复 install.php
  5. 重新访问安装向导
重装会覆盖库表数据,生产环境务必先备份。

常见问题

打开 install.php 空白 / 报错

  • 检查 PHP 版本与必装扩展
  • 查看 content/cache/error.log 或 Web 服务器错误日志
  • 确认未误删 system/content/ 关键文件

提示目录不可写

  • 调整 content/cachecontent/upload 权限与属主
  • 部分主机禁止 777,优先用属主可写的 755/775

安装完成仍可访问 install.php

  • 手动删除或重命名 install.php
  • 确认存在 content/cache/install.lock

子目录安装后样式 / 链接错乱

  • 后台核对站点 URL(含路径与尾斜杠)
  • 伪静态时修正 RewriteBase 或 Nginx location 前缀
  • 更新缓存后再测

后台无法登录

  • 确认使用的是重命名后的入口文件
  • 清除浏览器 Cookie 后重试
  • 检查数据库用户表管理员是否安装成功

前台 500 / 白屏

  • 开启开发者模式排查(见 开发准备工作
  • 确认 system/config.php 数据库信息正确
  • PHP 版本过低或缺少扩展时,安装后也可能在个别页面报错

从旧版本升级

已有站点不要再跑安装向导。覆盖文件、插件字段迁移与缓存刷新见 升级与更新

开发环境与目录约定见 开发准备工作

升级与更新

本文说明 Hope CMS 程序覆盖升级插件/主题升级数据库字段迁移站点缓存刷新

适用场景速查

场景 推荐做法 详见
覆盖更新核心包 备份 → 覆盖 system/ 等 → 更新缓存 程序升级
插件版本迭代加字段 callback_up() + 幂等 schema 插件更新
主题启用时建表 / 初始化 主题 callback.phpcallback_init() 主题更新
改菜单/标签/分类名后前台未变 后台「更新缓存」 更新缓存
仅刷新某类缓存 $SNAP->refresh([...]) 更新缓存
覆盖更新可能改写 system/。业务扩展请写在 content/plugin/content/theme/,勿改核心后指望下次升级保留。

升级前建议:备份数据库 + 备份 content/upload/ +(可选)整站文件快照。


程序升级

从 HopeCMS 1.0.4 覆盖升级后,旧的 controller / modelbase.php、旧类名 lib 等文件可能仍留在 system/
逻辑见 system/lib/legacy_cleanup.php

  • 在线升级成功后自动执行
  • 也可在后台 设置 → 更新缓存 旁手动「清理旧版残余」

仅删除白名单路径,不会动 config.php、主题、插件、上传目录。


插件更新

插件目录:content/plugin/{插件名}/

生命周期

回调 时机 建议
callback_init() 启用时 建表、初始化 MetaStorage、幂等 schema
callback_up() 更新时 再跑同一 schema 升级函数
callback_rm() 删除时 MetaStorage::deleteAllName('YES');按需 HopeSql::dropTables

启用顺序:加载 {插件}_callback.phpcallback_init()不会自动执行 install.sql)。

推荐写法

// tips_callback.php
function callback_up() {
    callback_init();
}
function callback_init() {
    // 有表时:myplugin_upgrade_schema();
}
function callback_rm() {
    MetaStorage::getInstance('tips')->deleteAllName('YES');
}

有表插件将 CREATE + ALTER 放在同一升级函数。完整示例见 数据库与 SQL

字段升级原则

  1. 建表用 CREATE TABLE IF NOT EXISTS
  2. 新字段先 SHOW COLUMNSALTER(可重复执行)
  3. callback_initcallback_up 调用同一函数
  4. 禁止改核心表结构(articleuseroption 等)

主题更新

  • 建表 / 初始化:主题根目录 callback.phpcallback_init()(删除时 callback_rm()
  • CSF settings.php 增删字段后,读取必须带默认值:hope_option('key', 'default') / _hope()
  • 新增 pages/*.php 页面模板后:后台新建页面并选择该模板
  • $prefix(如 default_options不要随意改名,否则已保存配置失效

主题结构见 主题开发指南


更新缓存

后台:设置 → 数据 → 更新缓存(或系统设置中的「更新缓存」入口)。

会做什么

全量 SnapshotBank::refresh()(全局实例常为 $SNAP),重建包括但不限于:

optionsuserstacommenttagscategorylinkmenunewlogrecordlogaliaslogcategorylogtags

并调用 Settings::resetRoutingTableCache() 清空伪静态路由表内存缓存(保存「设置 → 链接」时也会重置)。

菜单标题同步

刷新 menu 时会同步文章 / 页面 / 分类 / 标签类型菜单项标题为来源最新名称;自定义链接名不受影响。

改了标签名、分类名或文章标题后,点一次「更新缓存」即可让导航跟上。

代码中局部刷新

global $SNAP;
$SNAP = SnapshotBank::getInstance();

$SNAP->refresh();                                // 全量
$SNAP->refresh(['tags', 'menu', 'category']);    // 指定
$SNAP->refreshPosts();                           // 文章相关常用组合

缓存文件在 content/cache/*.php;请确保目录可写。


开发自检清单

  • [ ] 插件 / 主题 Version 头信息已递增
  • [ ] 新表与新字段在幂等升级函数中(callback_init / callback_up
  • [ ] callback_rm 已清理 MetaStorage;有表则明确是否 DROP
  • [ ] 未改核心表结构与 system/ 业务文件
  • [ ] 主题新配置均有默认值;$prefix 未误改
  • [ ] 升级前已备份;升级后已「更新缓存」并抽查前台

相关文档

常见问题

集中说明安装、升级与二次开发中的常见疑惑。

相关:安装指南 · 升级与更新 · 主题开发指南 · 插件开发指南


安装与后台

打开 install.php 空白 / 报错

  • 检查 PHP 版本与必装扩展(见 安装指南
  • 查看 content/cache/error.log 或 Web 服务器错误日志
  • 确认未误删 system/content/ 关键文件

安装后无法进后台

  • 确认已删除或重命名根目录 install.php
  • 入口默认为 admin.php;若已改名,请使用新文件名
  • 清除浏览器缓存后重试

前台样式丢失 / 链接异常

  • 后台 设置 → 更新缓存
  • 检查「设置 → 链接」模式是否与服务器伪静态配置一致(见 伪静态与路由

升级后仍看到旧报错 / 旧类文件

从 1.0.4 覆盖升级后,可用后台 设置 → 清理旧版残余system/lib/legacy_cleanup.php)删除废弃的 controller/model 等文件;在线升级成功时也会自动清理。


主题与插件开发

主题设置页不出现

主题根目录需有 settings.php(CSF)。确认当前主题已启用,并刷新后台菜单。

侧边栏组件不显示

  • 确认 widgets/config.php 已注册对应 side_* key
  • 确认存在 widgets/widget-{name}.php
  • 换主题后到 外观 → 侧边栏 重新勾选组件

插件启用后无效果

  • 确认入口文件头信息与目录名一致
  • 钩子是否写在入口或 *_lib.php 且已被 require
  • 查看是否与其它插件互斥;需要时检查 content/cache/error.log

后台语言包在哪?

核心后台文案在 system/lang/{语言码}.php(如 zh-cn.php),由 system/lib/support/hope_lang.php 加载。
插件自有文案可放在 content/plugin/{插件}/lang/

主题 / 插件如何发起支付?

不要手写第三方签名逻辑。使用:

require_once HOPE_ROOT . 'system/lib/payment/pay_client.php';
$order = (new Pay_Store())->createOrder([...]);
$pay = PayClient::createPayment($order);

详见 支付接入;后台配置页:admin.php?act=pay


更多

目录总览见 目录说明。反馈渠道见 联系我们

目录说明

Hope CMS 根目录只保留两个文件夹:content/(内容区)与 system/(程序区),外加少量入口文件。

相关:开发准备工作 · 常见问题


根目录

hope/
├── index.php      # 前台入口
├── admin.php      # 后台入口
├── install.php    # 安装向导
├── content/       # 主题、插件、上传、缓存等可变内容
└── system/        # 内核、业务、后台逻辑、公共库、语言包
文件 / 目录 说明
index.php 前台统一入口
admin.php 后台统一入口
install.php 首次安装(库表 SQL 内嵌)
content/ 站点内容与扩展(升级主题/插件主要改这里)
system/ 程序核心(一般升级程序改这里)

content/ — 内容区

content/
├── theme/       # 前台主题
├── plugin/      # 插件
├── admin/       # 后台视图(HTML / CSS / JS)
├── upload/      # 用户上传文件
├── cache/       # 运行时缓存(配置、分类、统计等)
└── logs/        # 日志
目录 作用
theme/ 可切换的前台主题,当前主题由站点配置决定
plugin/ 可启用/停用的插件;插件自带语言包可放在 {插件}/lang/
admin/ 后台界面模板与静态资源,与 system/admin 控制器配合
upload/ 媒体与附件存储
cache/ PHP 数组缓存文件,加速读写
logs/ 运行/审计日志
static/ 公共静态文件

system/ — 程序区

system/
├── Hope/            # 内核命名空间:启动、路由、配置、事件
├── app/             # 业务分层:handler / store / service
├── admin/           # 后台控制器
├── lib/             # 公共库
├── lang/            # 后台多语言包(zh-cn.php、en.php …)
├── options/         # 主题选项框架(Codestar)
├── deploy/          # 部署示例(如 nginx)
├── bootstrap.php    # 内核引导(前台 / 后台共用)
├── config.php       # 数据库等站点配置
└── checkcode.php    # 验证码兼容入口

system/Hope/ — 内核

system/Hope/
├── Foundation/            # Application、Paths、ClassLoader、path_constants
├── Bootstrap/             # RuntimeBoot 启动编排
├── Http/                  # FrontRouter、Request、RouteMatch
├── Config/                # Settings 站点配置
├── Runtime/               # SnapshotBank 快照缓存
├── Presentation/          # PageComposer 视图路径解析
└── Event/                 # HookBus 事件总线

system/app/ — 业务分层

目录 角色 示例
handler/ 前台请求处理 Post_HandlerCategory_HandlerUser_HandlerPay_Handler
store/ 数据访问 Post_StoreComment_StoreUser_StorePay_Store
service/ 领域服务 UserServiceNoticeServiceMediaServiceai_service

system/admin/ — 后台控制器

按功能拆分,例如:article.phparticle_edit.phpcomment.phpsetting.phptheme.phpplugin.phppay.phpai_studio.php 等。
视图在 content/admin/

system/lib/ — 公共库

lib/
├── auth/          # 登录会话、密码、授权
├── database/      # HopeDb / HopeMysqli / 查询与元存储
├── http/          # 请求输入、站点 URL、JSON 输出、HTTP 客户端
├── mail/          # 邮件发送
├── payment/       # 支付 SDK(PayClient、通道、收银台)
├── captcha/       # 验证码与字体
├── markdown/      # Markdown 解析
├── support/       # 语言(hope_lang.php)、异常等支撑
├── view/          # 侧栏日历、应用商店视图 helpers
├── common.php     # 全局辅助函数
├── hooks.php      # 钩子常量
├── sidebar.php    # 侧栏注册与渲染
└── upgrade_helper.php

system/lang/ — 后台语言包

路径 说明
system/lang/zh-cn.php 后台界面多语言文案;由 hope_lang_file() 加载
system/lib/support/hope_lang.php __() / hope_lang() 实现

新增语言:复制一份已有 *.php,在 hope_lang_registry() 中登记语言代码。

其他

路径 说明
system/options/ 主题选项 UI 框架(CSF)
system/deploy/ 部署配置示例
system/config.php DB_*、表前缀、密钥等

请求流程

前台

index.php
  → system/bootstrap.php
  → Application 启动
  → FrontRouter 匹配路由
  → app/handler/*
  → content/theme/<当前主题>/

后台

admin.php
  → system/bootstrap.php(加载内核)
  → system/admin/globals.php
  → system/admin/<act>.php
  → content/admin/<act>.php(视图)

记忆口诀

  • content:装什么(主题、插件、文件、缓存)
  • system:怎么跑(内核、业务、后台、公共库、语言包)

二次开发时:改界面与扩展优先看 content/;改路由、保存逻辑、权限与数据层优先看 system/

开发准备工作

Hope CMS 支持主题与插件扩展。本文说明本地环境、目录约定、请求流程与常用 API,便于二次开发。

相关:挂载点手册 · 主题开发指南 · 插件开发指南 · 数据库与 SQL


开发环境

项目 要求
PHP 7.0 ~ 8.x
MySQL 5.6+ / MariaDB 10.3+
Web Apache / Nginx
扩展 mysqlipdo_mysql;建议 mbstringjsoncurl
浏览器 Chrome / Edge 等现代浏览器

将站点根目录(含 index.php)设为 Web 根目录,并确保 content/upload/content/cache/ 可写。


目录结构

根目录只保留 content/(内容区)与 system/(程序区),外加入口文件:

hope/                              # 站点根(HOPE_ROOT)
├── index.php                      # 前台入口
├── admin.php                      # 后台入口
├── install.php                    # 安装向导(库表 SQL 内嵌)
├── system/                        # 内核(升级会覆盖,勿直接改业务)
│   ├── Hope/                      # Foundation / Bootstrap / Http / Config / Runtime / Presentation / Event
│   ├── app/
│   │   ├── handler/               # 前台请求处理(*_Handler)
│   │   ├── store/                 # 数据访问(*_Store)
│   │   └── service/               # 领域服务
│   ├── admin/                     # 后台控制器(与 content/admin 视图配对)
│   ├── lib/                       # 公共库(如:工具类、第三方库)
│   ├── lang/                      # 后台多语言包
│   ├── options/                   # 主题选项框架(CSF)
│   ├── bootstrap.php              # 内核引导
│   └── config.php                 # 数据库、AUTH_KEY 等
└── content/                       # ★ 开发者主要工作区
    ├── theme/                     # 主题
    ├── plugin/                    # 插件
    ├── admin/                     # 后台 HTML / CSS / JS
    ├── upload/                    # 用户上传
    ├── cache/                     # 运行时缓存
    └── logs/                      # 日志

记忆口诀: content 装什么;system 怎么跑。

完整说明见 目录说明


快速上手:示例插件

官方示例: content/plugin/tips/

  1. 复制 tips 目录并改名,修改 Plugin Name 与函数前缀
  2. 后台 插件 → 插件管理 启用
  3. 启用时加载 {插件}_callback.php 并调用 callback_init()
// tips.php
hope_listen('adm_main_top', 'tips_render_admin_banner');
hope_listen('adm_head', 'tips_enqueue_admin_css');

// tips_callback.php
function callback_init() { /* 启用即生效 */ }
function callback_rm() {
    MetaStorage::getInstance('tips')->deleteAllName('YES');
}

完整说明见 插件开发指南


快速上手:示例主题

官方示例: content/theme/default/

  1. 复制 default 为新主题目录,修改 head.php 元信息与 settings.php$prefix
  2. pages/ 下新建页面模板,首行写 /*@name 关于我们*/
  3. 后台 页面 → 新建页面,选择对应模板
  4. 布局参考同主题 head.php / foot.php
<?php
/*@name 关于我们*/
defined('HOPE_ROOT') || exit('access denied!');
?>
<section class="about">
    <h1><?= htmlspecialchars((string) Settings::get('sitename')) ?></h1>
</section>
<?php include PageComposer::themePath('footer'); ?>

文件清单、CSF、模板变量见 主题开发指南


请求流程

入口 流程
前台 index.php bootstrap.php → Application → 加载插件 → include 主题 hooks.phphope_emit('init')FrontRouter*_Handler → 主题模板
后台 admin.php bootstrap.phpsystem/admin/globals.phpact 分发 → system/admin/{act}.phpcontent/admin/{act}.php
REST API ?rest-api=方法名 → API Handler → JsonOut::ok() / JsonOut::error()
插件设置页 {后台入口}?act=plugin_set&plugin=插件名{插件}_setting.phpplugin_setting_view()

插件可通过 HopeHooks::ROUTING_REGISTER 注册前台伪静态路由,见 伪静态与路由


常用常量

常量 说明
HOPE_ROOT 站点根目录绝对路径(含尾 /
DB_PREFIX 表前缀(system/config.php
SITE_URL 站点 URL(含末尾 /
THEME_PATH / THEME_URL 当前主题物理路径 / URL
HOPE_THEMES_PATH / HOPE_THEMES_URL 主题根目录
HOPE_PLUGINS_PATH / HOPE_PLUGINS_URL 插件根目录
ADMIN_TEMPLATE_PATH 后台视图目录(content/admin/
ISLOGIN / UID / ROLE 登录态、用户 ID、角色

路径常量:HOPE_PATH_APPHOPE_PATH_STOREHOPE_PATH_HANDLERHOPE_PATH_SERVICEHOPE_PATH_ADMINHOPE_PATH_LIBRARY


用户角色

常量 典型权限
ROLE_ADMIN admin 全部后台权限
ROLE_EDITOR editor 管理内容与评论
ROLE_WRITER writer 管理自己的文章
ROLE_VISITOR visitor 前台只读

插件后台页由核心保障管理员权限;独立 AJAX 需自行校验:

if (!AuthSession::isLogin() || ROLE !== ROLE_ADMIN) {
    JsonOut::authError('权限不足');
}

挂载点(速览)

hope_listen(HopeHooks::ADM_HEAD, 'tips_enqueue_admin_css');
hope_listen(HopeHooks::ADM_MAIN_TOP, 'tips_render_admin_banner');
hope_emit(HopeHooks::INDEX_HEAD);
函数 行为
hope_listen($hook, $callback) 注册
hope_emit($hook, ...) 执行全部回调
hope_emit_once($hook, $input, &$ret) 仅第一个,可改 $ret
hope_emit_pipe($hook, $input, &$ret) 链式变换 $ret
hope_unlisten / hope_has_listener 移除 / 判断

完整列表见 挂载点手册。常量源码:system/lib/hooks.php


配置与选项

系统设置

$sitename = Settings::get('sitename');
Settings::updateOption('apikey', $key);
Settings::updateOption('is_openapi', 'y');

主题配置(CSF)

$color = hope_option('primary_color', '#3b82f6');
$layout = _hope('site_layout', 'double');

站点标题等请用 Settings::get('sitename') / hope_site_name()

插件私有配置

$storage = MetaStorage::getInstance('tips');
$storage->setValue('config', ['foo' => 'bar'], 'array');
$config = $storage->getValue('config');
$storage->deleteAllName('YES'); // 卸载时

输入输出

$id   = RequestInput::getIntVar('id');
$page = RequestInput::getIntVar('page', 1, 1);
$name = RequestInput::postStrVar('name');
$pwd  = RequestInput::postRawStr('password');

JsonOut::ok(['list' => $rows]);
JsonOut::error('参数错误');
JsonOut::authError('未登录');

hope_abort('权限不足', './');

模板与 URL

include PageComposer::themePath('header');     // → head.php
include PageComposer::themePath('log_list');   // → archive.php
include PageComposer::themePath('pages/about'); // 独立页模板
include PageComposer::userPath('index');       // account/

SiteUrl::post($gid);
SiteUrl::category($sid);
SiteUrl::tag($tagname);
SiteUrl::plugin('tips', ['id' => 1]);
SiteUrl::userCenter(['api' => 1]);

URL 规则受后台「设置 → 链接」影响,见 伪静态与路由


开发规范

  • PHP 遵循 PSR-1PSR-12
  • 文件开头:defined('HOPE_ROOT') || exit('access denied!');
  • 主题配置用 hope_option / _hope,始终带默认值
  • 禁止在业务里 HopeDb::getInstance()->query() 拼接不可信输入
  • 输出用户内容用 htmlspecialchars / hope_html_clean

开启开发者模式

方式一: 后台开启「调试模式」。

上线前请改回 production 并关闭调试。错误日志多在 content/cache/error.logcontent/logs/

方式二:system/config.php 末尾:

const ENVIRONMENT = 'develop';

下一步

挂载点手册

Hope CMS 通过挂载点(Hook)在不改核心的前提下扩展功能。常量定义于 system/lib/hooks.phpHopeHooks),注册/触发 API 在 system/lib/common.php

相关:开发准备工作 · 插件开发指南 · 伪静态与路由


API 一览

// 注册(推荐常量)
hope_listen(HopeHooks::INDEX_HEAD, 'myplugin_head_css');
hope_listen('adm_main_top', 'tips_render_admin_banner'); // 字符串亦可

// 触发
hope_emit(HopeHooks::INDEX_HEAD);
hope_emit(HopeHooks::SAVE_LOG, $gid, $logData);
函数 行为 典型场景
hope_listen($hook, $callback) 注册回调 插件入口
hope_emit($hook, ...$args) 执行该点全部回调 输出 CSS/JS、记日志
hope_emit_once($hook, $input, &$ret) 第一个回调,可改 $ret 接管上传、用户中心 API
hope_emit_pipe($hook, $input, &$ret) 链式执行,逐级变换 $ret 正文过滤
hope_unlisten($hook, $callback) 移除回调 调试 / 互斥
hope_has_listener($hook, $callback) 是否已注册 条件注册

约定:

  • 回调内输出用户内容用 htmlspecialchars
  • 需要 JSON 中断时用 JsonOut::* / hope_abort(),避免半截 HTML
  • 函数名加插件/主题前缀,避免冲突

启动与路由

常量 字符串 说明
HopeHooks::INIT init 插件与主题 hooks.php 加载完成后
HopeHooks::ROUTE_DISPATCH route_dispatch 路由分发前(model, method, params)
HopeHooks::ROUTING_REGISTER routing_register 路由表构建后扩展(hope_emit_pipe,可追加规则)
HopeHooks::PAGE_NOT_FOUND page_not_found 404 页
hope_listen(HopeHooks::ROUTING_REGISTER, 'myplugin_register_routes');

详见 伪静态与路由


前台页面

常量 字符串 建议埋点位置
INDEX_HEAD index_head </head>
INDEX_BODY_START index_body_start <body>
INDEX_SIDEBAR index_sidebar 侧边栏 widgets 之前
INDEX_FOOTER index_footer 页脚脚本前
INDEX_BODY_END index_body_end </body>

主题模板负责 hope_emit(...),插件负责 hope_listen(...)


文章与评论

常量 字符串 参数 / 说明
LOG_VIEW log_view 文章页渲染前(logid, logData)
ARTICLE_CONTENT_ECHO article_content_echo 正文输出前过滤(hope_emit_pipe
LOG_RELATED log_related 相关推荐 / 扩展区
LOG_DIRECT_LINK log_direct_link 外链跳转前
SAVE_LOG save_log 文章/页面保存后
DEL_LOG del_log 文章/页面删除后
COMMENT_FORM comment_form 评论表单区
COMMENT_POST comment_post 评论提交前
COMMENT_SAVED comment_saved 评论保存后
COMMENT_REPLY comment_reply 后台回复评论后
POST_COMMENT post_comment 前台评论发布成功
POST_NOTE post_note 微语/笔记发布
hope_listen(HopeHooks::ARTICLE_CONTENT_ECHO, function ($content, &$ret) {
    $ret = str_replace('foo', 'bar', $ret ?: $content);
});

用户与登录

常量 字符串 说明
USER_MENU user_menu 用户中心侧栏扩展
USER_CENTER_API user_center_api 个人中心 API(hope_emit_once,可接管 action)
LOGIN_HEAD login_head 登录页 head
LOGIN_EXT login_ext 登录表单扩展
LOGIN_SUCCESS login_success 登录成功
SIGNUP_EXT signup_ext 注册表单扩展
hope_listen(HopeHooks::USER_MENU, function () {
    echo '<a href="' . htmlspecialchars(SiteUrl::plugin('myplugin')) . '">我的扩展</a>';
});

后台

常量 字符串 说明
ADM_HEAD adm_head 后台 <head>
ADM_FOOTER adm_footer 后台页脚
ADM_MAIN_TOP adm_main_top 后台首页顶部(如 tips 横幅)
ADM_MAIN_BOTTOM adm_main_bottom 后台首页底部
ADM_MENU adm_menu 侧栏菜单项(靠前)
ADM_MENU_PLUGIN adm_menu_plugin 侧栏「插件」子菜单(管理项之后)
ADM_MENU_EXT adm_menu_ext 侧栏菜单末尾
ADM_WRITELOG_BAR adm_writelog_bar 写文章工具栏
ADM_WRITELOG_TITLE adm_writelog_title 写文章标题旁扩展
ADM_COMMENT_DISPLAY adm_comment_display 评论列表扩展
ADM_LINK_DISPLAY adm_link_display 链接列表扩展
ADM_USER_DISPLAY adm_user_display 用户列表行($user
ADM_USER_FORM adm_user_form 用户编辑表单
// 插件分组内(「管理」之后)
hope_listen(HopeHooks::ADM_MENU_PLUGIN, function () {
    echo '<li class="nav-item"><a class="nav-link" href="./?act=plugin_set&plugin=myplugin">'
        . '<span class="sidenav-mini-icon text-xs">我的</span>'
        . '<span class="sidenav-normal">我的插件</span></a></li>';
});

// 侧栏末尾顶级项
hope_listen(HopeHooks::ADM_MENU_EXT, function () {
    echo '<li class="nav-item"><a class="nav-link" href="./?act=plugin_set&plugin=myplugin">我的插件</a></li>';
});

具体菜单 HTML 请对照当前后台模板。


用户管理事件

常量 字符串 参数
ADD_USER add_user uid, userData
UPDATE_USER update_user uid, userData
DEL_USER del_user uid
FORBID_USER forbid_user uid
UNFORBID_USER unforbid_user uid

媒体与下载

常量 字符串 说明
UPLOAD_MEDIA upload_media 附件上传(常配合 hope_emit_once 接管)
DEL_MEDIA del_media 附件删除
ATTACH_UPLOAD attach_upload 编辑器附件上传
DOWNLOAD_RESOURCE download_resource 资源下载

分类、设置与插件生命周期

常量 字符串 说明
SAVE_CATEGORY / DEL_CATEGORY save_category / del_category 分类保存 / 删除后
SAVE_SETTING save_setting 系统设置保存后
PLUGIN_ACTIVE plugin_active 插件启用
PLUGIN_INACTIVE plugin_inactive 插件停用
PLUGIN_DELETED plugin_deleted 插件删除
LANG_LOADED lang_loaded 语言包加载后
GET_GRAVATAR get_Gravatar Gravatar 地址(hope_emit_once

示例:最小可用

// myplugin.php
hope_listen(HopeHooks::ADM_HEAD, 'myplugin_admin_css');
hope_listen(HopeHooks::SAVE_LOG, 'myplugin_on_save');

function myplugin_admin_css() {
    echo '<link rel="stylesheet" href="' . HOPE_PLUGINS_URL . 'myplugin/assets/admin.css">' . "\n";
}

function myplugin_on_save($gid, $data = null) {
    // 文章保存后的业务逻辑
}

源码常量以 system/lib/hooks.php 为准;本页与代码不一致时以代码为准。

数据库与 SQL

Hope CMS 使用 MySQL / MariaDB。业务访问统一通过 HopeDbHopeSql 完成。

  • 生命周期 / MetaStorage:对照示例插件 tips
  • 自建表:对照 oauthvideomusic*_upgrade_schema() / callback_init()

相关:插件开发指南 · 升级与更新


核心类与文件

路径 说明
HopeDb system/lib/database/hope_db.php 数据库入口,链式查询与快捷方法
HopeQuery system/lib/database/hope_query.php 链式查询构建器
HopeSql system/lib/database/hope_sql.php SQL 文件读取与批量执行
MetaStorage system/lib/database/meta_storage.php 插件键值存储(hope_storage 表)
HopeMysqli / HopePdo 同目录 底层驱动

表前缀

const DB_PREFIX = 'hope_'; // system/config.php

约定:

  • HopeDb::table('article') 不要手动加前缀,框架会拼为 hope_article
  • 原生 SQL 可拼接 DB_PREFIX,或在 HopeSql / HopeDb::executeFile 中使用 {db_prefix}
  • 少数场景:DB_PREFIX . 'myplugin_log'

链式查询(推荐)

$rows = HopeDb::table('myplugin_log')
    ->where(['status' => 1, 'uid' => $uid])
    ->whereLike('title', $keyword)
    ->whereIn('id', $ids)
    ->order('id DESC')
    ->limit(20, 0)
    ->findAll();

$row = HopeDb::table('myplugin_log')->where(['id' => $id])->find();

$total = HopeDb::table('myplugin_log')->where(['status' => 1])->count();

$list = HopeDb::table('myplugin_log', 'l')
    ->select(['l.id', 'l.title', 'u.nickname'])
    ->leftJoin('user', 'u', 'l.uid = u.uid')
    ->where(['l.status' => 1])
    ->order('l.id DESC')
    ->limit(10)
    ->findAll();

链式方法一览

方法 说明
select($fields) 指定字段
where($condition) 数组或字符串条件
whereRaw($sql) 原生 WHERE 片段
whereIn / whereLike IN / 模糊
join / leftJoin 表关联
order / limit / group 排序、分页、分组
findAll / find 多行 / 单行
insert / update / delete / count 写与计数

快捷方法

HopeDb::select('article', ['hide' => 'n']);
HopeDb::getOne('user', ['uid' => 1]);
HopeDb::insert('myplugin_log', ['title' => '示例', 'status' => 1]);
HopeDb::update('myplugin_log', ['id' => 1], ['status' => 0]);
HopeDb::delete('myplugin_log', ['id' => 1]);
HopeDb::count('myplugin_log', ['status' => 1]);

原生 SQL

$rows = HopeDb::fetchAll(
    'SELECT * FROM `' . DB_PREFIX . 'myplugin_log` WHERE uid = ' . (int) $uid
);

$row = HopeDb::fetchOne('SELECT COUNT(*) AS cnt FROM `' . DB_PREFIX . 'myplugin_log`');

HopeDb::execute('UPDATE `' . DB_PREFIX . 'myplugin_log` SET views = views + 1 WHERE id = ' . (int) $id);

$safe = HopeDb::escape($keyword);
优先链式 where / whereLike;原生 SQL 中数值 (int),字符串务必 HopeDb::escape()

插件/主题建表(callback_init)

不再使用目录下的 install.sql 自动执行。启用时核心只调用 callback_init()

function myplugin_upgrade_schema() {
    $table = DB_PREFIX . 'myplugin_log';
    if (HopeDb::fetchOne("SHOW TABLES LIKE '{$table}'")) {
        return;
    }
    HopeDb::execute("CREATE TABLE IF NOT EXISTS `{$table}` (
        `id` int unsigned NOT NULL AUTO_INCREMENT,
        `title` varchar(255) NOT NULL DEFAULT '',
        `uid` int unsigned NOT NULL DEFAULT 0,
        `status` tinyint NOT NULL DEFAULT 1,
        PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", true);
}

function callback_init() {
    myplugin_upgrade_schema();
}

function callback_rm() {
    MetaStorage::getInstance('myplugin')->deleteAllName('YES');
    HopeSql::dropTables(['myplugin_log']);
}

何时执行

时机 调用
插件启用 callback_init()
主题启用 主题 callback.phpcallback_init()
插件升级 callback_up()(通常再调同一 upgrade)
手动 SQL 文件 HopeDb::executeFile / HopeSql::executeFile

HopeSql 高级选项

HopeSql::executeFile(HOPE_ROOT . 'path/to/custom.sql', [
    'replace_prefix' => true,
    'ignore_error'   => false,
    'charset_setup'  => false,
    'version_gate'   => false,
]);

HopeSql::dropTables(['table_a', 'table_b']):逻辑表名(不含前缀)批量 DROP。


字段升级(Schema Migration)

function myplugin_upgrade_schema_fields() {
    static $done = false;
    if ($done) return;
    $done = true;

    $table = DB_PREFIX . 'myplugin_log';
    HopeDb::execute("CREATE TABLE IF NOT EXISTS `{$table}` (...)", true);

    $existing = [];
    foreach (HopeDb::fetchAll("SHOW COLUMNS FROM `{$table}`") as $col) {
        $existing[$col['Field']] = true;
    }

    if (empty($existing['new_field'])) {
        HopeDb::execute(
            "ALTER TABLE `{$table}` ADD COLUMN `new_field` varchar(100) NOT NULL DEFAULT ''",
            true
        );
    }
}

callback_init() / callback_up() 中调用同一函数即可。


MetaStorage 插件存储

$storage = MetaStorage::getInstance('tips');

$storage->setValue('config', [
    'enabled' => 'y',
    'per_page' => 20,
], 'array');

$config = $storage->getValue('config');
$storage->deleteAllName('YES');

适合开关、JSON 配置、计数;列表/关联数据请自建表。


系统核心表(只读参考)

不得修改以下核心表结构,仅可通过 Store / API 读写:

逻辑名 实际表名 说明
article hope_article 文章/页面
user hope_user 用户
comment hope_comment 评论
sort / category 视版本 分类
tag hope_tag 标签
media hope_media 附件
option hope_option 系统配置
storage hope_storage 插件键值存储
pay_order hope_pay_order 支付订单

插件自建表命名建议:{DB_PREFIX}{插件名}_{用途},如 hope_oauth_bind


开发建议

  1. 生命周期 → 先写好 callback_* 与 MetaStorage
  2. 新建表callback_init / *_upgrade_schema()
  3. 加字段 → 同一升级函数内 SHOW COLUMNS + ALTER
  4. 日常查询 → 优先 HopeDb::table() 链式
  5. 复杂 SQLfetchAll / fetchOne / execute,注意转义
  6. 卸载清理callback_rm() 必须清 MetaStorage;有表则明确是否 DROP

主题开发指南

主题位于 content/theme/{主题目录}/。启用后前台页面由该目录下的 PHP 模板渲染。

官方示例主题:content/theme/default/。学习列表页、侧边栏、用户中心、CSF 设置时优先对照此主题。

相关文档:开发准备工作 · 挂载点手册 · 侧边栏开发说明 · 伪静态与路由 · 数据库与 SQL

站内「主题生成器」页可拼装主题 PHP 片段(输出适配现行 Hope CMS API)。


1. 从零创建主题

  1. 复制 content/theme/default/content/theme/你的主题/
  2. 修改 head.php 顶部 Theme Name / Version / Description
  3. 修改 settings.php$prefix(如 yourtheme_options)与标题文案
  4. functions.php 中函数前缀(如 nova_*)改为自己的命名空间,避免冲突
  5. 后台 外观 → 主题 启用;如有 callback.php,启用时会执行 callback_init()
  6. 建议执行一次 设置 → 更新缓存
  7. 外观 → 侧边栏 检查组件是否仍可用
不要直接改正在线上使用的主题;复制一份再改,便于回滚。

2. 目录结构

官方 default 采用下列命名(推荐新主题照此创建):

content/theme/default/
├── head.php               # 必需:主题元信息 + HTML 头部
├── foot.php               # 必需:页脚与闭合标签
├── archive.php            # 必需:首页 / 分类 / 标签 / 搜索 / 归档列表
├── single.php             # 必需:文章详情
├── 404.php                # 推荐:404 页
├── functions.php          # 推荐:主题函数库(模板中 require)
├── settings.php           # 可选:CSF 主题设置
├── hooks.php              # 可选:主题级钩子(每请求自动 include)
├── callback.php           # 可选:启用 / 删除 / 升级回调
├── preview.png            # 推荐:后台主题列表预览图
├── widgets/               # 可选:侧边栏
│   ├── sidebar.php
│   ├── config.php         # 组件注册表(注册 key 仍可用 side_*)
│   └── widget-*.php       # 如 widget-blogger.php
├── pages/                 # 可选:独立页面模板
│   └── page.php
├── account/               # 可选:登录 / 注册 / 用户中心
│   ├── index.php
│   ├── login.php
│   ├── register.php
│   ├── auth.php
│   └── profile.php
├── partials/              # 可选:可复用片段
└── assets/                # 静态资源
    ├── css/
    ├── img/
    └── js/

最小可运行主题

至少需要:head.phpfoot.phparchive.phpsingle.php。其余按需增加。


3. 主题元信息

写在 head.php 文件最顶部的注释块,后台主题列表会解析:

<?php
/*
Theme Name: 默认主题
Theme Type: cms
Theme Url: http://www.hopecms.cn
Author: Hope CMS
Author Url: http://www.hopecms.cn
Version: 1.0.0
Description: Hope CMS 默认主题:大标题首页、双栏列表、标签云侧栏与个人中心。
*/
defined('HOPE_ROOT') || exit('access denied!');
require_once PageComposer::themePath('module'); // → functions.php
字段 说明
Theme Name 后台显示名称(必需)
Version 版本号
Description 简介
Theme Type 可选分类关键词(见下表);可写多个,后台按匹配显示中文标签
Author / Theme Url 作者与主题主页

Theme Type 与后台展示对应关系(Theme_Store):

写入值(英文关键词) 后台显示
cms 博客
music 音乐
video 视频
forum 论坛

示例:Theme Type: cms → 显示「博客」;Theme Type: cms video → 显示「博客.视频」。


4. 核心模板与变量

控制器先 include PageComposer::themePath('header'),再 include 列表或详情模板;模板内再 include footer(逻辑名;物理文件可为 head.php / foot.php)。

4.1 列表页 archive.php(逻辑名 log_list

用于首页、分类、标签、搜索、日期归档、作者页等。

变量 说明
$logs 当前页文章数组
$lognum 符合条件的文章总数
$page / $index_lognum / $pageurl 分页参数
$sortName / $sort 分类名 / 分类信息(分类页)
$tag 标签名(标签页)
$keyword 搜索关键词(搜索页)
$record 归档年月(归档页)
$author_name 作者展示名(作者页)
$page_html = hope_pagination($lognum, $index_lognum, $page, $pageurl);
foreach ($logs as $value) {
    // $value['gid'] $value['log_title'] $value['log_cover'] ...
    echo '<a href="' . SiteUrl::post($value['gid']) . '">'
        . htmlspecialchars($value['log_title']) . '</a>';
}

列表变体(可选):archive_card.php / archive_grid.php / archive_seamless.php,可由主题设置切换;薄封装内通常 include PageComposer::themePath('archive')

4.2 详情页 single.php(逻辑名 echo_log

变量 说明
$logid / $gid 文章 ID
$log_title / $log_content 标题 / 正文 HTML
$log_cover 封面图 URL
$date / $views / $comnum 时间戳 / 阅读 / 评论数
$author / $sortid / $tags 作者 UID / 分类 ID / 标签 ID 串
$neighborLog 上一篇 / 下一篇
$password 访问密码(有则需校验)
<?php include PageComposer::themePath('sidebar'); ?>
<?php include PageComposer::themePath('footer'); ?>

4.3 head.php / foot.php 常用变量

变量 说明
$site_title 当前页 <title>
$site_key / $site_description SEO keywords / description
$sitename 站点名(系统设置)

头部 / 页脚应输出挂载点:

<?php hope_emit('index_head'); ?>
<?php hope_emit('index_footer'); ?>

站点标题请读系统设置,例如:

htmlspecialchars((string) Settings::get('sitename'));
// 或主题助手:hope_site_name() / hope_site_info()

5. 自定义页面

后台创建「页面」时可选择模板,文件放在 pages/

<?php
/*@name 关于我们*/
defined('HOPE_ROOT') || exit('access denied!');
?>
<main>
    <h1>关于我们</h1>
    <div><?= $log_content ?></div>
</main>
<?php include PageComposer::themePath('footer'); ?>
  • @name 后的文字会出现在后台模板下拉里
  • 页面同样会先加载 head.php(逻辑名 header),模板内记得闭合 footer
  • 复杂站点(如本主题 hopecms)可有多个:page_docs.phppage_download.phppage_generator.php(主题生成器)等

6. functions.php

建议所有主题辅助函数集中在此,并在 head.php 中:

require_once PageComposer::themePath('module'); // 逻辑名 → functions.php
function your_asset($path) {
    $path = ltrim((string) $path, '/');
    if ($path !== '' && strpos($path, 'assets/') !== 0) {
        $path = 'assets/' . $path;
    }
    return THEME_URL . $path;
}

function your_brand() {
    $name = trim((string) Settings::get('sitename'));
    return htmlspecialchars($name !== '' ? $name : 'Hope CMS');
}
常量 说明
THEME_PATH 当前主题物理路径
THEME_URL 当前主题 URL
SITE_URL 站点根 URL(含末尾 /

生成内容链接用核心类,不要手拼规则:

SiteUrl::post($gid);          // 文章
SiteUrl::category($sid);      // 分类
SiteUrl::tag($tagname);       // 标签
SiteUrl::author($uid);        // 作者
SiteUrl::userCenter();        // 个人中心

7. settings.php(主题设置)

主题目录内有 settings.php 时,后台出现主题设置页(Codestar / CSF)。

文件 用途
default/settings.php 可运行起点(精简实用)
content/theme/参考主题配置.php CSF 字段大全(不会自动加载,仅对照拷贝)

约定

说明
$prefix 唯一,推荐 {主题目录}_options设定后勿改
读取 hope_option('字段id', '默认值')_hope()
无文件 无主题设置页,主题仍可正常启用
站点信息 标题 / 副标题 / SEO / 版权走后台「设置」;主题用 Settings::get('sitename') 读取
<?php
if (!defined('HOPE_ROOT')) exit;

$prefix = 'yourtheme_options';

CSF::createOptions($prefix, [
    'plugin_title' => '某某主题设置',
    'footer_text'  => 'Hope CMS',
    'theme'        => 'light', // light | dark | auto
]);

CSF::createSection($prefix, [
    'title'  => '基本设置',
    'icon'   => 'fa fa-cog',
    'fields' => [
        [
            'type'    => 'notice',
            'style'   => 'info',
            'content' => '站点标题、副标题、SEO 描述与页脚版权请在后台「设置 → 站点信息 / SEO 设置」中维护。',
        ],
        [
            'id'      => 'primary_color',
            'type'    => 'color',
            'title'   => '主题色',
            'default' => '#3b82f6',
        ],
        [
            'id'      => 'hero_enable',
            'type'    => 'switcher',
            'title'   => '启用首页 Hero',
            'default' => true,
        ],
    ],
]);

options完整的.php 覆盖:文本、选择、开关、上传、颜色、调色板、repeater/group、accordion/tabbed、code_editor、依赖 dependency、backup 等。

WordPress Codestar 可迁移:API 用 CSF::;读取用 hope_option(),不要用 get_option


8. hooks.php(主题钩子)

每个前台请求在加载已启用插件之后,会自动 include 当前主题的 hooks.php(若存在),再执行 hope_emit('init')

适合:注册主题专属钩子、挂文章自定义字段、改写用户中心菜单等。

<?php
defined('HOPE_ROOT') || exit('access denied!');

hope_listen('index_head', 'yourtheme_extra_css');
hope_listen('user_menu', 'yourtheme_user_menu_item');

9. callback.php(主题生命周期)

函数 时机
callback_init() 主题启用
callback_rm() 主题删除
callback_up() 主题升级后(若实现)

可在启用时建表、写默认配置;删除时清理 MetaStorage / 自建表。建表写法见 数据库与 SQL

<?php
defined('HOPE_ROOT') || exit('access denied!');

function callback_init() {
    // 同步默认配置、建扩展表
}

function callback_rm() {
    // MetaStorage::getInstance('yourtheme')->deleteAllName('YES');
}

10. 侧边栏与用户中心

  • 侧边栏widgets/ + config.php,详见 侧边栏开发说明
  • 用户中心:优先使用主题 account/;若主题未提供,系统可能回退到其它主题的 user/
  • 登录 / 注册页同理,建议主题内自备完整 account/ 目录
include PageComposer::userPath('index');

11. 挂载点(主题侧)

主题模板负责 埋点,插件负责 注册回调

挂载点 建议位置
index_head </head>
index_body_start <body>
index_footer / index_body_end 页脚脚本区
index_sidebar 侧边栏容器内
log_related 文章相关推荐区

完整列表见 挂载点手册


12. 检查清单与注意事项

  1. 不硬编码域名,用 SITE_URLTHEME_URLSiteUrl::*
  2. 输出用户内容用 htmlspecialchars
  3. 勿修改 system/;业务扩展用主题 hooks.php 或独立插件
  4. $prefix 与函数名前缀全局唯一
  5. hope_option / _hope 始终带默认值
  6. SITE_URL 已有尾斜杠,拼接时避免双斜杠
  7. 静态资源建议放在 assets/
  8. 启用新主题后更新缓存;换主题后检查侧边栏组件是否仍注册
  9. 站点标题 / SEO 用系统设置,不在主题 CSF 重复配置

侧边栏开发说明

Hope CMS 主题侧边栏由「容器模板 + 配置注册 + 组件文件」组成,统一放在 widgets/ 目录。后台在 外观 → 菜单 → 侧边栏 中按组管理组件;前台按槽位渲染。

无侧边栏的主题可不创建 widgets/ 目录。

相关:主题开发指南


目录约定

推荐(与官方 default 一致):

content/theme/default/
├── functions.php
└── widgets/
    ├── sidebar.php          # 默认侧边栏容器
    ├── config.php           # $sidebar 组件注册表
    ├── widget-search.php
    ├── widget-newlog.php
    ├── widget-hotlog.php
    └── ...
文件 作用
widgets/sidebar.php / sidebarN.php 侧边栏外壳;文件名决定后台「侧边栏组」
widgets/config.php 中的 $sidebar 向后台声明可用组件类型与默认字段
widgets/widget-{name}.php 单个组件渲染逻辑

*注册 key 仍建议使用 `side_`**(与后台已存配置兼容)。默认解析:

  1. widgets/widget-{name}.php(如 key side_bloggerwidget-blogger.php

列表页、文章页中引入:

<?php include PageComposer::themePath('sidebar'); ?>

第二组:

<?php include PageComposer::themePath('sidebar1'); ?>

容器模板

<?php
defined('HOPE_ROOT') || exit('access denied!');
?>
<aside class="sidebar">
    <?php hope_emit('index_sidebar'); ?>
    <?php if (!hope_render_sidebar_widgets('sidebar')): ?>
        <?php
        hope_include_sidebar_widget('side_newlog', '最新文章', ['显示数量' => '5']);
        ?>
    <?php endif; ?>
</aside>

要点:

  1. hope_emit('index_sidebar'):插件可在此插入内容
  2. hope_render_sidebar_widgets($slot):按组输出;$slot 与文件名一致(sidebar / sidebar1
  3. 核心 API 在 system/lib/sidebar.php

注册组件(widgets/config.php

<?php
defined('HOPE_ROOT') || exit('access denied!');

$sidebar = [
    'side_search' => [
        'title' => '搜索',
        'default' => [
            '输入提示' => '搜索文章、标签…',
        ],
    ],
    'side_hotlog' => [
        'title' => '热门文章',
        'default' => [
            '显示数量' => '5',
        ],
    ],
];
字段 说明
数组键 组件标识(建议 side_*),对应 widget-*.php
title 后台下拉显示的名称
default 可选;字段名 → 默认值

自定义 HTML 组件无需注册:后台类型选「自定义组件」。


编写组件文件

例如 widgets/widget-hotlog.php(注册 key 为 side_hotlog):

<?php
defined('HOPE_ROOT') || exit('access denied!');

$limit = (int) hope_widget_field($fields, '显示数量', $widget_config['default']['显示数量'] ?? 5);
$Post_Store = new Post_Store();
$logs = $Post_Store->getHotLog(max(1, $limit));
?>
<div class="widget widget-hotlog">
    <h3 class="widget-title"><?= htmlspecialchars($widget_title ?: '热门文章') ?></h3>
    <!-- 循环输出 $logs -->
</div>

渲染时注入的变量:

变量 说明
$widget_title 后台填写的组件名称
$fields 组件自定义字段(键多为中文,与 default 一致)
$widget_config $sidebar[$widget_key]
$widget_key 组件标识
$limit = (int) hope_widget_field($fields, '显示数量', $widget_config['default']['显示数量'] ?? 5);
$hint  = hope_widget_field($fields, '输入提示', '搜索…');

自定义 HTML 组件

无需在 config.php 注册。后台添加侧边栏项时类型选「自定义组件」,直接填 HTML。

未配置任何组件时,容器内应用 hope_include_sidebar_widget(...) 做兜底,避免空白侧栏。

切换主题后:旧主题注册的组件类型若新主题没有对应文件,该项可能不渲染——启用新主题后请到 外观 → 侧边栏 检查并调整。


多侧边栏组

  1. widgets/ 增加 sidebar1.phpsidebar2.php…(内调 hope_render_sidebar_widgets('sidebar1')
  2. 后台「菜单 → 侧边栏」会出现组切换 Tab
  3. 添加的组件会写入对应组

渲染流程

后台菜单(type=sidebar, url=slot)
    → SnapshotBank menu 缓存
    → hope_menu_sidebar_widgets(slot)
    → hope_render_sidebar_widget()
         ├─ 解析 widget-*.php / side_*.php → include
         └─ 否则输出自定义 content

检查清单

  1. config.php$sidebar 键名与组件文件映射一致(side_foowidget-foo.php
  2. 容器已调用 hope_render_sidebar_widgets('sidebar')
  3. 后台能看到已注册组件,多组时能切换
  4. 未配置组件时有合理兜底
  5. 无侧边栏主题不创建 widgets/

更多主题约定见 主题开发指南

插件开发指南

插件是在不修改 system/ 核心的前提下扩展 Hope CMS 的主要方式:通过挂载点插入逻辑、可选建表、提供后台设置页与前台页面。

官方示例插件: content/plugin/tips/(小贴士)。无自建表、结构完整,适合作为脚手架。

有表 / 存储的对照:content/plugin/oauth/callback_init 建表 + MetaStorage 配置)。

相关文档:开发准备工作 · 挂载点手册 · 数据库与 SQL · 升级与更新 · 伪静态与路由 · API 开发文档 · 常见问题


1. 从 tips 起步

  1. 复制 content/plugin/tips/content/plugin/你的插件名/
  2. 目录名与入口文件名一致:你的插件名/你的插件名.php
  3. 重命名 tips_*.php,并全局替换函数前缀(如 tips_myplugin_
  4. 修改入口文件头信息中的 Plugin Name / Description / Version
  5. 后台 插件 → 插件管理 启用
  6. 确认钩子生效;需要持久化时再补 MetaStorage / 建表

启用时核心流程:写入 active_plugins → 加载 {插件}_callback.php → 调用 callback_init()不会自动执行 install.sql)。

每请求加载:对已启用插件 include_once content/plugin/{名}/{名}.php,再加载当前主题 hooks.php,最后 hope_emit('init')


2. 目录结构

content/plugin/tips/
├── tips.php                 # 必需:入口 + 头信息 + 注册钩子
├── tips_callback.php        # 强烈推荐:启用 / 更新 / 删除
├── tips_setting.php         # 可选:后台设置页(有则显示「设置」)
├── tips_lib.php             # 推荐:业务函数
├── tips_show.php            # 可选:前台独立页(SiteUrl::plugin)
├── preview.png              # 可选:插件列表预览图
└── assets/
    └── tips.css
文件 必需 说明
{名}.php 入口;启用期间每个请求都会加载
{名}_callback.php 强烈建议 callback_init / callback_up / callback_rm
{名}_setting.php 定义 plugin_setting_view()
{名}_lib.php 建议 函数库
{名}_show.php 前台展示页
{名}_rewrite.php 按需拆分(如伪静态规则)

命名规则:目录名、入口文件名、钩子函数前缀保持一致。


3. 入口文件与头信息

<?php
/*
Plugin Name: 小贴士
Version: 1.0.0
Plugin URL: http://www.hopecms.cn
Description: 插件启用后,在后台首页随机展示一句内置使用提示。
Author: Hope CMS
Author URL: http://www.hopecms.cn
*/
!defined('HOPE_ROOT') && exit('error');

require_once HOPE_ROOT . 'content/plugin/tips/tips_lib.php';

hope_listen('adm_main_top', 'tips_render_admin_banner');
hope_listen('adm_head', 'tips_enqueue_admin_css');
头字段 说明
Plugin Name 后台显示名(必需)
Version 版本号
Description 简介
Plugin URL / Author / Author URL 可选元数据

active_plugins 中存储的是相对路径:tips/tips.php


4. 生命周期回调

文件:content/plugin/{名}/{名}_callback.php

函数 时机 典型工作
callback_init() 启用 建表、写默认 MetaStorage、注册一次性数据
callback_up() 更新插件后 通常再调 callback_init()*_upgrade_schema()
callback_rm() 删除 删 MetaStorage、HopeSql::dropTables([...])

tips(无表)

<?php
defined('HOPE_ROOT') || exit('access denied!');

function callback_init() {
}

function callback_up() {
    callback_init();
}

function callback_rm() {
    MetaStorage::getInstance('tips')->deleteAllName('YES');
}

有表时(对照 oauth)

function callback_init() {
    require_once HOPE_ROOT . 'content/plugin/oauth/oauth_lib.php';
    oauth_upgrade_schema(); // 内含 CREATE TABLE IF NOT EXISTS
    $storage = MetaStorage::getInstance('oauth');
    if (!$storage->getValue('config')) {
        $storage->setValue('config', oauth_default_config(), 'array');
    }
}

function callback_rm() {
    MetaStorage::getInstance('oauth')->deleteAllName('YES');
    HopeSql::dropTables(['oauth_bind']); // 不带表前缀
}

要点:

  • 建表在 PHP 里执行,不要依赖自动跑 install.sql
  • HopeDb::table('xxx') 不带前缀;原生 SQL / dropTables 注意 DB_PREFIX
  • 卸载是否删表由你决定:商业数据可选择只清 MetaStorage、保留业务表

详见 数据库与 SQL升级与更新


5. 挂载点(Hook)

hope_listen('adm_main_top', 'tips_render_admin_banner');
hope_listen(HopeHooks::ADM_HEAD, 'tips_enqueue_admin_css'); // 推荐常量
API 行为
hope_emit($hook, ...) 执行该点全部回调
hope_emit_once($hook, $input, &$ret) 只跑第一个,可改 $ret
hope_emit_pipe($hook, $input, &$ret) 链式变换 $ret
hope_unlisten / hope_has_listener 移除 / 判断

常用挂载点

常量 / 字符串 用途
init 插件与主题 hooks 加载完后
index_head / index_footer 前台头尾
index_body_start / index_body_end body 始末
adm_head / adm_footer 后台头尾
adm_menu / adm_menu_plugin / adm_menu_ext 后台侧栏菜单
adm_main_top 后台首页顶部(tips 横幅)
adm_writelog_bar 写文章工具栏
user_menu 用户中心侧栏
user_center_api 用户中心 API 扩展
save_log / del_log 文章保存 / 删除后
log_view / log_related 文章渲染前 / 相关推荐
comment_saved / post_comment 评论相关
upload_media 媒体上传(常配合 hope_emit_once)
plugin_active / plugin_inactive 启停插件时
routing_register 注册前台路由

完整参数与示例见 挂载点手册

约定: 钩子回调里输出 HTML 时对用户数据做 htmlspecialchars;需要中断请求时用 JsonOut::* / hope_abort()


6. 后台设置页

存在 {名}_setting.php 且插件已启用时,插件列表显示「设置」。

访问:{后台入口}?act=plugin_set&plugin=tips
(默认入口为 admin.php,生产环境应重命名。)

必须定义:

<?php
defined('HOPE_ROOT') || exit('access denied!');

function plugin_setting_view() {
    // 输出后台 HTML(可沿用 content/admin 的 Bootstrap / card 样式)
}

保存配置推荐两种方式:

A. MetaStorage(键值,适合开关与小配置)

$storage = MetaStorage::getInstance('myplugin');
$storage->setValue('config', ['enable' => 1], 'array');
$config = $storage->getValue('config');

B. 自建表

HopeDb::table(...)->insert/update;设置页里处理 POST 时用 RequestInput::postStrVar 等过滤。

设置页由核心保证管理员登录;若另写 AJAX 接口,须自行校验:

if (!AuthSession::isLogin() || ROLE !== ROLE_ADMIN) {
    JsonOut::authError('权限不足');
}

7. 前台页面 {名}_show.php

放置该文件且插件已启用时,可通过插件路由访问:

$url = SiteUrl::plugin('tips');
$url = SiteUrl::plugin('tips', ['id' => 1]);
  • 未启用或文件不存在 → 404
  • 页面可自绘完整 HTML,也可 include 当前主题头尾保持站点风格
include PageComposer::themePath('header');
// ... 插件内容 ...
include PageComposer::themePath('footer');

静态资源:

function tips_asset_url($path) {
    return HOPE_PLUGINS_URL . 'tips/assets/' . ltrim($path, '/');
}
常量 说明
HOPE_PLUGINS_PATH content/plugin/ 物理路径
HOPE_PLUGINS_URL content/plugin/ URL 前缀

8. 数据库与配置

场景 推荐
开关、JSON 配置、计数 MetaStorage::getInstance('插件名')
列表、关联、订单等 自建表 + HopeDb::table
系统级选项 Settings::get / Settings::updateOption(慎用,避免污染核心键)
$rows = HopeDb::table('myplugin_log')
    ->where(['uid' => $uid])
    ->order('id DESC')
    ->limit(20)
    ->findAll();

禁止在业务里直接拼接不可信输入执行 SQL。详见 数据库与 SQL


9. 安全规范

  1. 每个 PHP 文件检查 HOPE_ROOT
  2. 读写请求用 RequestInput::getIntVar / postStrVar / postRawStr
  3. 输出 HTML 用 htmlspecialchars;JSON 用 JsonOut::ok / JsonOut::error
  4. CSRF:后台表单遵循现有 admin 习惯;前台写操作校验登录态与 token
  5. 不修改核心表结构语义;不覆盖 system/content/admin 核心文件
  6. SQL 表名、字段名白名单化,勿把用户输入拼进标识符

10. 调试

  1. 后台开启调试模式 或 system/config.php 增加 const ENVIRONMENT = 'develop';
  2. 错误日志:content/logs/content/cache/error.log
  3. 改钩子后:禁用再启用插件,确保 callback_init 重跑
  4. 确认 active_plugins 中路径为 目录/入口.php
  5. 前台 _show.php 404:检查是否启用、文件名是否 {名}_show.php、链接是否 SiteUrl::plugin

11. 与主题的分工

能力 放插件 放主题
跨主题可复用功能
仅当前皮肤的样式 / 布局
商业支付、OAuth、统计 主题只负责展示
CSF 外观选项 settings.php
文章保存后业务逻辑 save_log 主题 hooks 仅皮肤相关

Shop 等主题依赖的商业能力应做成普通插件,可启用 / 禁用 / 删除,而不是改核心。


12. 发布检查清单

  • [ ] 头信息完整,Version 可递增
  • [ ] callback_rm 清理 MetaStorage;有表则明确是否 dropTables
  • [ ] callback_up / *_upgrade_schema 可重复执行(IF NOT EXISTS / 判列)
  • [ ] 设置页、前台页在未启用时不可访问
  • [ ] 函数名、MetaStorage 名、表名带插件前缀,无全局污染
  • [ ] 附简短说明(README 或设置页文案)
  • [ ] 在 PHP 7.4+ / 8.x 各测一次启停与卸载

13. 最小插件骨架

<?php
/*
Plugin Name: 演示插件
Version: 1.0.0
Description: 最小可运行示例
Author: You
*/
!defined('HOPE_ROOT') && exit('error');

hope_listen('adm_head', function () {
    echo "<!-- myplugin loaded -->\n";
});

仅入口也可运行;正式发布请补全 _callback.php 与卸载清理。

伪静态与 URL 路由

Hope CMS 通过 链接模式(linkmode路由表 控制前台 URL。后台在 设置 → 链接 → 伪静态管理 配置;路由表由 Settings::getEnhancedRoutingTable() 生成,由 Hope\Http\FrontRouter 匹配并分发到对应 *_Handler

相关:挂载点手册 · API 开发文档 · 插件开发指南 · 支付接入


链接模式

linkmode 后台名称 URL 形态 说明
0 动态 ?article=1?category=2&page=3 无需服务器重写,兼容性最好
1 伪静态 /article/1.html/user 需将非文件请求重写到 index.php
2 仿伪静态 /index.php/article/1.html 依赖 PATH_INFO,多数主机免改 Rewrite
$linkmode = (string) Settings::get('linkmode');
$isRewrite = ($linkmode === '1' || $linkmode === '2');

保存链接设置或后台 设置 → 更新缓存(全量 $SNAP->refresh())会调用 Settings::resetRoutingTableCache(),清空进程内路由表缓存。单独刷新 options 缓存时同样会重置。


可配置 URL 规则

在伪静态管理中可改五项(hope_option 键名)。生成链接由 SiteUrl 按规则替换占位符。

配置项 动态默认 伪静态推荐 Handler
rule_article ?article={%id%} /article/{%id%}.html Post_Handler::displayContent
rule_page ?id={%id%} /page/{%alias%}.html Post_Handler::displayContent
rule_category ?category={%id%}&page={%page%} /category/{%alias%}/{%page%}/ Category_Handler::display
rule_tag ?tags={%tag%}&page={%page%} /tags-{%tag%}_{%page%}.html Tag_Handler::display
rule_record ?record={%record%}&page={%page%} /date/{%record%}/{%page%}/ Record_Handler::display

后台提供「应用推荐方案」「检测冲突」;保存时 SiteUrl::normalizeRewriteRulesForSave() 会:

  • 强制首页分页为 ?page={%page%}(见下节,不再支持自定义 rule_index 路径)
  • 去掉文章 / 独立页规则中的 {%page%}(详情不分页;评论页用 ?comment-page=
  • 将归档规则里错误的 {%date%} / date= 规范为 {%record%} / record=
  • 检测文章 / 分类 / 独立页路径同形冲突,并以 rewrite_warnings 提示

首页分页(固定)

首页列表分页固定为查询参数,再使用 /page/{%page%}/ 路径(避免与独立页 /page/{%alias%}.html 抢匹配):

场景 第 1 页 第 2 页及以后
默认首页 / /?page=2
自定义首页(home_page_id > 0 /posts /posts?page=2

SiteUrl::postPage() 按上述规则生成链接。

常用占位符

占位符 说明
{%host%} 站点 URL(SITE_URL
{%id%} 数字 ID(含义由所在规则决定)
{%alias%} 按规则隔离:文章 / 分类 / 页面各自别名
{%category%} / {%category_alias%} 所属分类标识(文章规则用)
{%category_name%} 分类显示名
{%page%} 列表页码
{%tag%} 标签名
{%record%} 日期归档(YYYYMM / YYYYMMDD
{%year%} / {%month%} / {%day%} 文章发布日期
{%parent%} 父分类别名

变量隔离: 文章路径里的分类写 {%category_alias%};分类列表自身写 {%alias%}

{%page%} 在生成非分页链接时会被自动剔除。推荐连接写法:/{%page%}/_{%page%}-{%page%}?page={%page%}。分类 / 标签 / 归档规则应包含 {%page%}

勿让文章 {%category_alias%}/{%alias%}.html 与分类 {%alias%}/{%page%}.html 同形。推荐:文章 article/{%id%}.html、分类 category/{%alias%}/{%page%}/、独立页 page/{%alias%}.html

内置活动路由

type = active 的路由在所有 linkmode 下参与匹配(含伪静态下的兼容 query)。

路径 / 参数 Handler 说明
?author=1 · /author/{id} · /author/{id}/page/{n} Author_Handler 作者页
?keyword=关键词 Search_Handler 搜索
/user?user User_Handler::index 个人中心
/login?login User_Handler::login 登录
/register?register User_Handler::register 注册
/auth_api?auth_api User_Handler::authApi 认证 API
?rest-api=方法名 Api_Handler / 支付等 REST API
/plugin/{名}?plugin={名} Plugin_Handler 插件前台页
?resource_alias=别名 Download_Handler 附件下载
POST ?action=addcom Comment_Handler 提交评论
/?page={n} Post_Handler::display 首页分页
/posts · /posts?page={n} Post_Handler::display 自定义首页时的文章列表

伪静态开启时的 query 兼容: 仍匹配 ?article=?id=(独立页,且排除已带 article/category 等)、?category=?plugin=,避免旧链接落到默认首页。

评论分页统一为文章 URL 上追加 ?comment-page={n}(不走路径型伪静态)。


请求匹配流程

浏览器请求
  → Web 服务器重写到 index.php(linkmode=1)
  → FrontRouter::resolvePath() 解析路径
  → Settings 路由表按顺序匹配
  → 校验 must_get / not_get / request_method
  → 写入 $_GET,可选 processor
  → *_Handler 执行

路由表顺序:

  1. 活动路由(含 rest-api、用户中心、兼容 query)
  2. 内容路由(文章 / 分类 / 标签 / 首页分页 / 归档 / 独立页;随 linkmode 为 rewritequery
  3. 默认路由 /Post_Handler::display
  4. 插件通过 HopeHooks::ROUTING_REGISTER(事件名 routing_register)向表头部追加规则

匹配要点:

  • 空 path(首页)跳过 type=rewrite,但仍匹配 query / active
  • 动态模式(linkmode=0)跳过纯 path 的 rewrite 规则
  • 伪静态模式跳过纯 query 型内容路由(活动与 default 保留)
  • 分类规则中的 {%page%} 匹配时强制为数字({%page&type=num%}),避免与独立页混淆
  • 归档规则排在独立页之前,避免 date-….html 被当成页面别名

分发前触发钩子 route_dispatch(参数:Handler 类名、方法、参数)。


Web 服务器配置

Apache(站点根目录 .htaccess

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
</IfModule>

子目录安装(如 /hope/)将 RewriteBase 改为 /hope/,并保证 SITE_URL 含正确子路径。

Nginx

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

仿伪静态(linkmode=2) 若 PATH_INFO 无效可改用:

location / {
    if (!-e $request_filename) {
        rewrite ^/(.*)$ /index.php/$1 last;
    }
}

IIS

安装 URL Rewrite,将非文件请求重写到 index.php。系统兼容 HTTP_X_REWRITE_URL

改服务器配置后,确认后台 linkmode 与规则已保存,再测试文章、分类、用户中心、插件页。

生成链接(开发)

主题与插件应使用 SiteUrl,不要手写路径:

SiteUrl::post($gid);
SiteUrl::category($sid);
SiteUrl::tag($tagname);
SiteUrl::record($yyyymm);
SiteUrl::author($uid);
SiteUrl::postPage();
SiteUrl::userCenter(['api' => 1]);
SiteUrl::plugin('video', ['vid' => 1]);
SiteUrl::login();
SiteUrl::register();
SiteUrl::authApi();
SiteUrl::comment($gid, $pageId, $cid);

SiteUrl::plugin():伪静态下为 /plugin/video?vid=1,动态下为 ?plugin=video&vid=1


插件扩展伪静态

站点 已开启 linkmode 1 或 2 时,可注册独立 path 规则:

  1. 新建 {插件名}_rewrite.php,实现注册函数
  2. 入口中 hope_listen(HopeHooks::ROUTING_REGISTER, 'xxx_register_rewrite_routes')
  3. processor(函数名或闭包)把路径参数写入 $_GET['plugin']
hope_listen(HopeHooks::ROUTING_REGISTER, 'myplugin_register_routes');

function myplugin_register_routes($table, &$routes) {
    $linkmode = (string) Settings::get('linkmode');
    if ($linkmode !== '1' && $linkmode !== '2') {
        return;
    }

    $routes = array_merge([
        [
            'type'      => 'rewrite',
            'model'     => 'Plugin_Handler',
            'method'    => 'loadPluginShow',
            'pattern'   => '/myplugin/{%id&type=num%}',
            'processor' => function ($params, $route) {
                $_GET['plugin'] = 'myplugin';
                $params['plugin'] = 'myplugin';
                return $params;
            },
        ],
    ], $routes);
}

路由项字段

字段 说明
type rewrite / query / active / default
model / method Handler 与方法(Plugin_Handler 或可解析的别名)
pattern 支持 {%param%}{%param&type=num%}
must_get / not_get 必须存在 / 不得存在的 $_GET
processor 匹配成功后的参数处理
request_method 限制 GET / POST

Pattern 类型修饰:{%id&type=num%} 仅数字;{%slug&type=alnum%} 字母数字;alpha 仅字母。

现成实现:content/plugin/apply/apply_rewrite.phpcontent/plugin/oauth/oauth_rewrite.php


常见问题

开启伪静态后全部 404

  • 检查是否重写到 index.php
  • 子目录确认 RewriteBase / SITE_URL
  • 确认 linkmode12 且规则已保存

首页正常,文章 / 分类 404

  • 检查 rule_articlerule_category 是否与服务器兼容
  • 勿遗漏必要占位符;保存后「更新缓存」
  • 用后台「检测冲突」查看路径同形警告

/page/docs.html 被当成首页分页

当前内核首页分页已固定为 ?page=,独立页使用 /page/{%alias%}.html 不再冲突。若仍异常,确认已保存链接设置并清理路由缓存。

插件伪静态不生效

  • 站点 linkmode 必须为 12
  • 确认 routing_register 已注册且插件已启用
  • 更具体的 pattern 应用 array_merge($newRoutes, $routes) 插到表头

动态模式下用户中心 API

  • ?user&api=1&action=profile
  • 认证专用:/auth_api?auth_api
  • 使用 SiteUrl::userCenter()SiteUrl::authApi(),勿硬编码

相关文件

文件 职责
system/Hope/Config/Settings.php 路由表、linkmode、规则解析
system/Hope/Http/FrontRouter.php 路径解析与匹配、分发
system/lib/http/site_url.php 按规则生成链接、保存前规范化
system/lib/hooks.php HopeHooks::ROUTING_REGISTER
content/admin/setting.php 后台「伪静态管理」界面
system/admin/setting.php 保存链接设置

API 开发文档

Hope CMS 提供 REST 风格接口,通过 URL 参数 rest-api 指定方法名。响应均为 JSON。

相关:伪静态与路由 · 挂载点手册 · 插件开发指南


基础地址

https://hopecms.cn/?rest-api={方法名}

示例:

GET https://hopecms.cn/?rest-api=article_list&page=1&count=10

开关

除支付相关接口外,其余 API 需在后台开启 OpenAPI(Settings::get('is_openapi') !== 'n'),否则返回 api is closed

支付接口(pay_notifypay_createpay_cashierpay_status)不受此限制。


鉴权方式

需要鉴权的接口(如发文、上传)支持两种方式:

1. API Key 签名(服务端 / 第三方)

后台「设置 → API」获取 apikey,请求携带:

参数 说明
req_time 当前 Unix 时间戳(与服务器时差 ≤ 300 秒)
req_nonce 必填,16–64 位十六进制随机串,防重放
req_sign md5(req_time + req_nonce + apikey)
$req_time = time();
$req_nonce = bin2hex(random_bytes(16));
$apikey   = '你的apikey';
$req_sign = md5($req_time . $req_nonce . $apikey);
POST https://hopecms.cn/?rest-api=article_post&req_time=...&req_nonce=...&req_sign=...

缺少参数、签名错误或 nonce 重放时返回相应错误信息。

已登录用户携带站点认证 Cookie 时,可免 API Key,且 author_uid 可自动取当前用户。


响应格式

成功:

{ "code": 200, "msg": "ok", "data": { ... } }

失败:

{ "code": 0, "msg": "错误说明" }

认证失败:code 多为 401(以 JsonOut::authError 实际返回为准)。


文章接口

article_list — 文章列表

GET ?rest-api=article_list
参数 类型 说明
page int 页码,默认 1
count int 每页条数
category_id int 分类 ID
keyword string 标题关键词
tag string 标签名
order string views / comnum,默认按置顶与时间

返回 data.articles(含 idtitlecoverurldateauthor_nametags 等)。

article_detail — 文章详情

GET ?rest-api=article_detail&id=1

密码保护文章返回错误。成功返回 data.article

article_post — 发布文章

POST ?rest-api=article_post

需鉴权。主要字段:titlecontent(必填),以及 excerptcategory_idtagscoverdraftaliastopsortopallow_remarkpasswordtemplate 等。

article_update — 更新文章

POST ?rest-api=article_update

需鉴权。需传 id 及要更新的字段。


分类与微语

接口 方法 说明
category_list GET 分类列表
note_list GET 微语列表
note_post POST 发布微语(需鉴权)

评论与用户

接口 方法 说明
comment_list GET 文章评论,id 为文章 ID
userinfo GET 当前登录用户信息(需 Cookie)

个人中心 API

前台个人中心通过 ?user&api=1(或伪静态 /user?api=1)调用,需登录 Cookie。POST 须携带页面输出的 token

GET  https://hopecms.cn/?user&api=1&action=profile
POST https://hopecms.cn/?user&api=1
     Body: token=...&action=profile&nickname=...
action 方法 说明
profile GET / POST 读取或更新个人资料
avatar_upload POST 上传头像
avatar_qq POST 使用 QQ 头像
recharge POST 创建充值订单
recharge_records GET 充值记录
invite_info GET 邀请码与奖励统计
forum_posts / forum_get / forum_save / forum_delete 论坛相关(主题提供时)
forum_categories GET 可选论坛分类
my_comments GET 我的评论
comment_reply POST 回复评论
my_media / media_upload / media_delete 媒体库

业务实现位于 system/app/service/(如 UserHub / 用户中心模块)。主题可在 account/ 提供模板并对接前端 JS。

插件可通过 HopeHooks::USER_CENTER_APIhope_emit_once)扩展自定义 action


媒体上传

POST ?rest-api=upload

需 API Key 鉴权。multipart/form-data 字段:file、可选 sidauthor_uid

成功返回 media_idurlfile_info


支付接口

Pay_Handler 处理,不依赖 OpenAPI 开关:

接口 说明
pay_create 创建支付订单
pay_cashier 收银台页面数据
pay_status 查询订单状态
pay_notify 支付平台异步回调

网关密钥、回调 URL 在后台 支付中心admin.php?act=pay)配置。

主题 / 插件侧业务创建支付请使用 PayClient / Pay_Store,完整示例见 支付接入。实现入口:system/app/handler/pay_handler.phpsystem/lib/payment/pay_client.php

若后台开启 API 限流,超限请降低频率或调整限额。


调用示例(PHP)

$base = 'https://hopecms.cn/';
$req_time = time();
$req_nonce = bin2hex(random_bytes(16));
$apikey = 'your_apikey';
$sign = md5($req_time . $req_nonce . $apikey);

$url = $base . '?rest-api=article_list&page=1&count=5'
    . '&req_time=' . $req_time
    . '&req_nonce=' . $req_nonce
    . '&req_sign=' . $sign;

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = curl_exec($ch);
curl_close($ch);

$data = json_decode($body, true);

扩展 API

优先通过插件扩展,避免直接改核心 Api_Handler

  1. 个人中心类:挂载 HopeHooks::USER_CENTER_API 接管自定义 action
  2. 独立前台接口:插件 _show.php + 自建鉴权,或注册路由后由插件处理
  3. 拦截 rest-api:在 init 钩子里识别方法名并 JsonOut::ok 后结束(注意与核心方法名冲突)

若维护私有分支必须改核心:在 Api_Handler 增加私有方法,方法名即 rest-api 值;需鉴权则调用 Handler 内鉴权方法。

实现入口:system/app/handler/api_handler.php

支付接入

主题 / 插件侧创建充值、购买订单时,请使用核心支付 SDK:Pay_Store + PayClient。后台入口:admin.php?act=pay(支付配置 · 订单 · PHP 用法)。

相关:API 开发文档 · 目录说明 · 数据库与 SQL


推荐流程

require_once HOPE_ROOT . 'system/lib/payment/pay_client.php';
$Pay_Store = new Pay_Store();

// 1. 写本地订单
$order = $Pay_Store->createOrder([
    'user_id'  => UID,
    'title'    => '账户充值',
    'amount'   => 10.00,
    'pay_type' => 'alipay',   // alipay | wechat
    'biz_type' => 'recharge', // recharge | apply | hp_log | hp_vip ...
    'biz_id'   => 0,
]);

// 2. 向通道发起支付
$pay = PayClient::createPayment($order);
// $pay 常见字段: ok, url / qrcode / html, msg

// 3. 引导用户
// 站内收银台:
PayClient::officialCashierUrl($order['order_no']);
// 直达网关:后台开启「直接跳转支付网关」后使用 $pay['url']

常用 API

方法 说明
PayClient::getConfig() 读取 options.payment 配置
PayClient::getChannel('wechat') 通道:official / hpj / epay / codepay
PayClient::isChannelReady('alipay') 当前通道参数是否齐全
PayClient::createPayment($order) 创建第三方支付
PayClient::queryAndSync($order) 主动查单并同步本地
PayClient::refundOrder($order) 退款(需 order['refund_reason']
PayClient::notifyUrl($channel) 异步通知地址
Pay_Store::createOrder($data) 创建本地订单
Pay_Store::getByOrderNo($no) 按订单号查询
Pay_Store::markPaid($no, $trade) 标记已支付(回调内)

异步回调

说明
前台路由 ?rest-api=pay_notify&channel=official\|hpj\|epay\|codepay
处理文件 system/app/handler/pay_handler.php
通道实现 system/lib/payment/pay_channel.phpofficial_pay.php

回调成功后应调用 Pay_Store::markPaid,再按 biz_type 发放业务权益(充值加余额、应用发货等)。

完整回调 URL、收银台与完成页地址可在后台 支付中心 → 接口地址 复制。


配置项(options.payment

字段 说明
payment_enabled 1/0 总开关
pay_direct_redirect 1/0 跳过站内收银台,直达网关
weixinapi / alipayapi 0 官方 · 1 虎皮椒 · 2 易支付 · 3 码支付
各通道 appid / key / 网关 见后台通道参数表单

后台管理

操作 说明
?act=pay&save POST 保存支付配置
?act=pay&del 删除订单
?act=pay&refund 退款
?act=pay&sync 查单同步
POST order_operate 批量 delete / close / sync

订单表:hope_pay_order(见 数据库与 SQL)。

联系我们

欢迎通过以下渠道获取帮助、反馈问题或参与社区交流。

联系方式

官方渠道

渠道 说明
官方网站 下载、应用中心与站内文档入口
用户论坛 使用讨论、开发问答与经验分享
开发文档 安装、升级与二次开发说明
应用中心 主题 / 插件扩展与用户反馈
下载中心 最新安装包

如何反馈问题

提交问题时请尽量提供:

  1. Hope CMS 版本(后台首页)
  2. PHP / MySQL(或 MariaDB)版本
  3. 当前主题与已启用插件
  4. 复现步骤与报错信息(可附 content/cache/error.log 相关片段)
安全漏洞请优先通过官方渠道私下反馈,勿在公开 QQ 群 / 论坛直接贴出可被滥用的细节。

获取程序

社区参与

  • 在用户论坛发帖互助
  • 分享自研主题与插件到应用中心
  • 对照官方示例主题 default、示例插件 tips 提交改进建议

文档与参考

站内文档按「入门 → 开发基础 → 主题 → 插件 → 接口」组织;还可参阅:

站内「主题生成器」可快速拼装主题 PHP 片段。

软件许可证

Hope CMS(希望CMS)核心代码按 Apache License 2.0 发布。部分捆绑的第三方库可能采用各自许可证,以其目录内说明为准。