插件开发
YikaiCMS 的插件系统采用 WordPress 风格的 Action / Filter 钩子机制,无需修改核心代码即可扩展功能。
目录结构
每个插件是 plugins/ 下的一个文件夹,至少包含两个文件:
plugins/
my-plugin/
plugin.json # 插件描述文件(必须)
main.php # 入口文件(必须)
admin.php # 后台管理页面(可选)
plugin.json
描述插件的元信息,系统据此在后台展示插件列表。
{
"name": "我的插件",
"version": "1.0.0",
"description": "这是插件的功能描述。",
"author": "你的名字",
"requires_php": "8.0",
"requires_cms": "1.0.0"
}
name — 后台显示的插件名称
requires_php — 最低 PHP 版本要求
requires_cms — 最低 CMS 版本要求
钩子系统
在 main.php 中通过 add_action() 和 add_filter() 注册钩子回调。
可用 Action 钩子
| 钩子名 | 触发位置 |
|---|---|
plugins_loaded | 所有插件加载完成后 |
ik_head | 前台 </head> 前,注入 CSS / meta |
ik_header_after | 前台导航栏之后 |
ik_footer_before | 前台 footer 开始前 |
ik_footer_scripts | 前台 </body> 前,注入 JS |
ik_admin_head | 后台 </head> 前 |
ik_admin_footer_scripts | 后台 </body> 前 |
API 函数
// 注册回调(priority 越小越先执行,默认 10) add_action(string $hook, callable $callback, int $priority = 10): void // 触发钩子 do_action(string $hook, mixed ...$args): void // 注册过滤器 add_filter(string $hook, callable $callback, int $priority = 10): void // 应用过滤器,返回处理后的值 apply_filters(string $hook, mixed $value, mixed ...$args): mixed
后台管理页面
如果插件需要后台设置界面,创建 admin.php,通过 /admin/plugin_page.php?plugin=插件目录名 访问。
<?php
// plugins/my-plugin/admin.php
if (!defined('ROOT_PATH')) {
exit('Access Denied');
}
$pageTitle = '我的插件设置';
require_once ROOT_PATH . '/admin/includes/header.php';
?>
<div class="max-w-3xl">
<h2 class="text-xl font-bold mb-4">插件设置</h2>
<!-- 你的设置表单 -->
</div>
<?php require_once ROOT_PATH . '/admin/includes/footer.php'; ?>
在 main.php 中可以给插件卡片添加"管理页面"入口链接:
add_action('ik_admin_footer_scripts', function () {
if (basename($_SERVER['SCRIPT_NAME']) !== 'plugin.php') return;
echo '<script>
(function(){
var card = document.getElementById("plugin-my-plugin");
if (!card) return;
var info = card.querySelector(".text-xs.text-gray-400");
if (info) {
var a = document.createElement("a");
a.href = "/admin/plugin_page.php?plugin=my-plugin";
a.className = "text-primary hover:underline ml-4";
a.textContent = "进入管理页面";
info.appendChild(a);
}
})();
</script>';
});
完整示例:返回顶部插件
一个最简单的前台插件,在页面右下角显示"返回顶部"按钮。
plugins/back-to-top/plugin.json
{
"name": "返回顶部",
"version": "1.0.0",
"description": "在前台页面右下角显示返回顶部按钮。",
"author": "Yikai CMS",
"requires_php": "8.0",
"requires_cms": "1.0.0"
}
plugins/back-to-top/main.php
<?php
if (!defined('ROOT_PATH')) {
exit('Access Denied');
}
add_action('ik_footer_scripts', function () {
?>
<div id="backToTop" onclick="window.scrollTo({top:0,behavior:'smooth'})"
style="position:fixed;bottom:30px;right:30px;z-index:9999;
width:44px;height:44px;border-radius:50%;
background:#3b82f6;color:#fff;display:none;
align-items:center;justify-content:center;
cursor:pointer;box-shadow:0 2px 12px rgba(59,130,246,0.4)">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5">
<path d="M18 15l-6-6-6 6"/>
</svg>
</div>
<script>
(function(){
var btn = document.getElementById('backToTop');
window.addEventListener('scroll', function(){
btn.style.display = window.scrollY > 400 ? 'flex' : 'none';
});
})();
</script>
<?php
});
主题设计
YikaiCMS 的主题系统基于文件覆盖机制——将模板文件放入主题目录,系统自动优先加载主题版本,未覆盖的文件回退到 includes/ 默认模板。
目录结构
themes/
my-theme/
theme.json # 主题描述(必须)
assets/
images/
screenshot.png # 预览图(后台展示)
css/
style.css # 主题自定义样式(可选)
layouts/
header.php # 页头布局
footer.php # 页脚布局
blocks/
banner.php # 首页轮播图区块
about.php # 首页关于我们区块
channel.php # 首页栏目内容区块
stats.php # 数据统计区块
advantage.php # 优势展示区块
testimonials.php # 客户评价区块
cta.php # 行动号召区块
partials/
breadcrumb.php # 面包屑导航
page-hero.php # 页面顶部横幅
pagination.php # 分页组件
article-card.php # 文章卡片
product-card.php # 产品卡片
case-card.php # 案例卡片
job-card.php # 招聘卡片
所有文件均为可选,只需覆盖你想自定义的文件。
theme.json
{
"name": "我的主题",
"description": "简洁大方的企业官网模板",
"version": "1.0.0",
"author": "你的名字",
"screenshot": "assets/images/screenshot.png",
"supports": [
"banner", "about", "stats",
"testimonials", "advantage", "cta", "channel"
]
}
screenshot — 预览图路径(相对于主题根目录),建议 16:10 比例,推荐 1280×800px
supports — 声明主题支持的首页区块类型
布局文件
layouts/header.php 和 layouts/footer.php 是全站公用的页头和页脚。
header.php 基本结构
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo e($pageTitle ? $pageTitle . ' - ' : '');
echo e(config('site_name')); ?></title>
<link rel="stylesheet" href="/assets/css/tailwind.css">
<!-- 引入主题自定义 CSS -->
<link rel="stylesheet" href="<?php echo theme_asset('css/style.css'); ?>">
<?php do_action('ik_head'); ?>
</head>
<body>
<!-- 导航栏 HTML -->
<?php do_action('ik_header_after'); ?>
footer.php 基本结构
<?php do_action('ik_footer_before'); ?>
<footer>
<!-- 页脚 HTML -->
</footer>
<?php do_action('ik_footer_scripts'); ?>
</body>
</html>
do_action() 调用,否则插件的钩子将无法生效。
首页区块
blocks/ 下的文件对应首页各个区块,由 index.php 按配置顺序加载。每个区块可直接使用以下预定义变量:
| 区块文件 | 可用变量 |
|---|---|
banner.php | $banners — 轮播图数组 |
about.php | 通过 config('home_about_*') 获取设置 |
channel.php | $homeChannels — 首页展示栏目 |
stats.php | 通过 config('home_stat_*') 获取数字 |
advantage.php | 通过 config('home_adv_*') 获取优势项 |
testimonials.php | 通过 config('home_testimonials') 获取 JSON |
cta.php | 通过 config('home_cta_*') 获取文案 |
公共组件 (Partials)
partials/ 下的文件是可复用的 UI 组件,在列表页和详情页中通过 require 加载:
// 加载面包屑(自动从主题目录查找,回退到 includes/partials/)
require theme_path('partials/breadcrumb.php');
// 加载产品卡片
require theme_path('partials/product-card.php');
组件内可使用的变量取决于调用上下文,例如 product-card.php 中可用 $item(单个产品数据)。
常用函数
| 函数 | 说明 |
|---|---|
config($key, $default) | 读取系统设置 |
e($string) | HTML 转义输出 |
__($key) | 多语言翻译 |
theme_path($file) | 获取主题模板绝对路径,自动回退 |
theme_asset($file) | 获取主题静态资源 URL |
currentTheme() | 当前激活的主题 slug |
channelUrl($channel) | 生成栏目 URL |
thumbnail($path, $size) | 生成缩略图 URL |
parseShortcodes($html) | 解析内容中的短码 |
do_action($hook) | 触发钩子(布局中必须保留) |
themes/ 目录,进入后台「主题管理」页面即可预览和启用。
部署配置
YikaiCMS 支持 URL 伪静态,将动态 URL 转换为 .html 后缀的静态格式,提升 SEO 和用户体验。v1.12.9 起内置单入口路由,与 WordPress 的伪静态规则完全通用——绝大多数环境只需一步、甚至零配置。
| 你的环境 | 怎么配 |
|---|---|
| 宝塔 / aaPanel(Nginx) | 伪静态框写一行 include …/deploy/nginx-baota.conf;(见下方 Nginx 一节,不要只选 wordpress 预设) |
| 阿里云 / 万网 云虚拟主机(Nginx) | NGINX 设置框粘 一行 try_files(见下) |
| 易开面板(Windows 本地) | 零配置——Nginx 已内置 YikaiCMS 规则,切换 Apache 时使用自带 .htaccess |
| Apache / 大部分共享主机 | 零配置——自带 .htaccess,装好即用 |
| 自建 Nginx 服务器 | 一行 try_files;追求静态直出用完整规则(见下) |
Nginx 伪静态配置
✓ 推荐:引用随包规则(含安全拦截)
宝塔 / aaPanel 等面板:在网站「伪静态」设置框里只写下面这一行(把路径换成你的站点目录),保存即可。随包的 deploy/nginx-baota.conf 同时包含路由规则和敏感目录拦截,以后升级会自动更新。
include /www/wwwroot/你的站点目录/deploy/nginx-baota.conf;
⚠ 不要只选「wordpress」预设
它只有一条 try_files,会先放行磁盘上真实存在的文件——SQLite 站点的数据库 storage/database.sqlite 就在站点目录内,会被任何人直接下载。配置完请用浏览器访问 /storage/database.sqlite,必须返回 403 或 404。
不用面板、手写 server 块时,加入下面两段(第一段拦截敏感目录,必须放在前面):
# 拦截不应经 HTTP 访问的目录(含 SQLite 数据库所在的 storage/) location ~* ^/(config|storage|vendor|includes|install/sql|bin|migrations|recipes)(/|$) { return 403; } # 其余请求交给 index.php,由程序内置路由分发 location / { try_files $uri $uri/ /index.php?$query_string; }
进阶:完整规则(静态 HTML 直出 + 服务器层拦截,性能更佳)
拥有 root 权限、追求极致性能时可用完整规则:已生成的 .html 静态页由 Nginx 直接返回、不进 PHP。把以下内容合并进 server 块(替代上面的规则):
# YikaiCMS Nginx 完整伪静态规则
location ~ ^/detail/(\d+)\.html$ { rewrite ^ /detail.php?id=$1 last; }
location ~ ^/case/(\d+)\.html$ { rewrite ^ /detail.php?id=$1 last; }
location ~ ^/case/([a-z0-9_-]+)\.html$ { rewrite ^ /detail.php?slug=$1 last; }
location ~ ^/list/(\d+)\.html$ { rewrite ^ /list.php?id=$1 last; }
location ~ ^/list/(\d+)/page/(\d+)\.html$ { rewrite ^ /list.php?id=$1&page=$2 last; }
location ~ ^/([a-z0-9_-]+)/page/(\d+)\.html$ { rewrite ^ /list.php?slug=$1&page=$2 last; }
location ~ ^/product/(\d+)\.html$ { rewrite ^ /product.php?id=$1 last; }
location ~ ^/product/([a-z0-9_-]+)/page/(\d+)\.html$ { rewrite ^ /list.php?slug=product&cat=$1&page=$2 last; }
location ~ ^/product/([a-z0-9_-]+)\.html$ { rewrite ^ /list.php?slug=product&cat=$1 last; }
location ~ ^/job/(\d+)\.html$ { rewrite ^ /job_detail.php?id=$1 last; }
location ~ ^/([a-z0-9_-]+)/([a-z0-9_-]+)\.html$ { rewrite ^ /page.php?parent=$1&slug=$2 last; }
location ~ ^/([a-z0-9_-]+)\.html$ { rewrite ^ /page.php?slug=$1 last; }
location / { try_files $uri $uri/ /index.php?$query_string; }
Apache 伪静态配置
Apache 环境下,YikaiCMS 自带 .htaccess 文件,安装后自动生效,无需额外配置。确保 Apache 已启用 mod_rewrite 模块。
如果伪静态不生效,检查以下几点:
- Apache 配置中
AllowOverride All已开启 mod_rewrite模块已加载(a2enmod rewrite).htaccess文件存在于网站根目录
阿里云增强版云虚拟主机
阿里云增强版 Linux 虚拟主机使用 Nginx 引擎,不支持 .htaccess,需在主机面板中单独配置伪静态规则。
配置步骤
- 登录 云虚拟主机管理页面
- 找到目标主机,点击 管理
- 左侧导航:高级环境设置 → NGINX设置
- 在文本框中粘贴下面的配置内容
- 点击 保存设置
✓ 推荐:极简规则(含敏感目录拦截)
把面板里默认的 location / {} 与 location ~ /\.ht {deny all;} 两段整体替换为下面内容。前两段拦截数据库所在的 storage/ 等目录,不能省略;配置后访问 /storage/database.sqlite 必须返回 403 或 404。更完整的版本(另含上传目录 PHP 拦截等)见程序包 deploy/aliyun-nginx-minimal.txt。
location ~ ^/(config|storage|vendor|includes|install/sql|bin|migrations|recipes)/ {
deny all;
}
location ~ /\.(git|env|htaccess|htpasswd) {
deny all;
}
location / {
if (!-e $request_filename) {
rewrite ^ /index.php last;
}
}
进阶:完整规则(仅在需要静态 HTML 直出时)
该虚拟主机只支持 location 内的 rewrite、try_files 等有限指令,规则需全部写在一个 location / 块中(上面拦截敏感目录的两段必须保留,只替换其中的 location / 段):
location / {
rewrite ^/detail/(\d+)\.html$ /detail.php?id=$1 last;
rewrite ^/case/(\d+)\.html$ /detail.php?id=$1 last;
rewrite ^/case/([a-z0-9_-]+)\.html$ /detail.php?slug=$1 last;
rewrite ^/list/(\d+)\.html$ /list.php?id=$1 last;
rewrite ^/list/(\d+)/page/(\d+)\.html$ /list.php?id=$1&page=$2 last;
rewrite ^/([a-z0-9_-]+)/page/(\d+)\.html$ /list.php?slug=$1&page=$2 last;
rewrite ^/product/(\d+)\.html$ /product.php?id=$1 last;
rewrite ^/product/([a-z0-9_-]+)/page/(\d+)\.html$ /list.php?slug=product&cat=$1&page=$2 last;
rewrite ^/product/([a-z0-9_-]+)\.html$ /list.php?slug=product&cat=$1 last;
rewrite ^/job/(\d+)\.html$ /job_detail.php?id=$1 last;
rewrite ^/([a-z0-9_-]+)/([a-z0-9_-]+)\.html$ /page.php?parent=$1&slug=$2 last;
rewrite ^/([a-z0-9_-]+)\.html$ /page.php?slug=$1 last;
try_files $uri $uri/ /index.php?$args;
}
location / { } 即可恢复。
/works/16.html),在 try_files 之前添加对应的 rewrite 规则即可。更具体的规则要放在通用规则前面。
URL 对照表
| 伪静态 URL | 实际请求 | 说明 |
|---|---|---|
/detail/16.html | /detail.php?id=16 | 内容详情页 |
/case/16.html | /detail.php?id=16 | 案例详情页 |
/about.html | /page.php?slug=about | 单页 |
/news.html | /page.php?slug=news | 栏目列表 |
/news/page/2.html | /list.php?slug=news&page=2 | 列表分页 |
/product/phone.html | /list.php?slug=product&cat=phone | 产品分类 |
/product/16.html | /product.php?id=16 | 产品详情 |
/job/1.html | /job_detail.php?id=1 | 招聘详情 |
/about/team.html | /page.php?parent=about&slug=team | 二级页面 |