Sndow CMS 插件开发 Skill 介绍
Sndow CMS 插件开发 Skill:从目录结构到 API 扩展的完整指南
插件是扩展 Sndow CMS 功能的重要方式。无论是文章目录、图片本地化、SEO 工具,还是后台增强和开放 API,都可以通过插件完成。
为了让开发者更准确地使用系统接口,Sndow CMS 提供了专门的插件开发 Skill,帮助开发者按照当前版本的真实结构编写插件。
什么是 Sndow CMS 插件开发 Skill?
Sndow CMS 插件开发 Skill 是面向插件开发、维护和排错的开发规范,适用于:
sd_content/plugins/目录下的插件项目。
它主要解决以下问题:
- 插件主文件应该放在哪里
- 插件如何注册后台菜单
- 插件安装、停用和卸载有什么区别
- 插件设置应该保存在哪里
- 如何正确处理权限和 CSRF
- 如何调用模型和数据库
- 如何扩展开放 API
- 如何使用全局上传函数
- 如何注册侧边栏小工具
推荐的插件目录结构
一个常见的插件目录如下:
sd_content/plugins/my-plugin/
├── my-plugin.php
├── admin-settings.php
├── options.php
├── includes/
└── assets/
├── css/
└── js/插件主文件建议使用与目录相同的名称:
my-plugin/my-plugin.php这样系统在扫描插件时可以更准确地识别主文件。
正确编写插件头部信息
插件主文件顶部需要包含标准注释:
<?php
/**
* Plugin Name: 我的插件
* Plugin URI: https://www.sndow.com/index/store/detail/id/31.html
* Description: 插件功能说明
* Author: 作者或团队
* Author URI: https://www.sndow.com/index/store/author/id/1.html
* Version: 1.0.0
*/
if (!defined('SD_PATH')) exit;其中:
Plugin Name是后台显示的插件名称Plugin URI是插件详情页地址Author URI是作者主页地址Version是当前插件版本Description是插件功能说明
发布插件时,URI 应填写真实的官网地址,不要复制其他应用的地址,也不要使用不完整的域名或测试链接。
插件生命周期函数必须带前缀
Sndow CMS 当前推荐使用带插件 slug 前缀的生命周期函数。
例如插件目录为:
sd-toc生命周期函数应写成:
function sd_toc_plugin_install() {
// 插件启用时执行
}
function sd_toc_plugin_uninstall() {
// 插件从后台删除时执行
}
function sd_toc_plugin_upgrade() {
// 插件升级时执行
}旧版的:
function plugin_install() {}
function plugin_uninstall() {}
function plugin_upgrade() {}仅用于兼容旧插件,不建议新插件继续使用。通用函数名容易和其他插件冲突,也可能产生 Cannot redeclare 错误。
需要注意的是,停用插件不会自动执行卸载函数。停用时系统触发的是:
do_action('deactivate_sd_toc');插件卸载函数只应该处理插件删除时的清理工作。
使用 sd_add_admin_menu 添加后台入口
插件可以通过 sd_add_admin_menu() 添加后台菜单:
sd_add_admin_menu(
'文章目录',
__FILE__,
'admin-settings',
'bi-list-ul',
'manage_settings',
'extension'
);参数说明:
- 第一个参数是菜单名称
- 第二个参数通常使用
__FILE__ - 第三个参数是插件后台页面文件名
- 第四个参数是 Bootstrap Icons 图标
- 第五个参数是访问所需权限
- 第六个参数可以设置为
extension
插件后台页面通常位于:
sd_content/plugins/my-plugin/admin-settings.php访问地址由系统统一处理,不需要插件自行编写后台路由。
后台设置页必须处理权限和 CSRF
插件后台页面不能只依赖菜单权限,还需要在页面内部再次检查权限:
global $auth;
if (!$auth->can('manage_settings')) {
sd_die('权限不足');
}处理表单提交时,需要验证 CSRF:
if (Request::isPost()) {
sd_csrf_check();
$title = Request::post('title', '');
sd_set_config(
'my_plugin',
'title',
$title
);
}表单中加入:
<?php sd_csrf_field(); ?>如果是 AJAX 请求,可以使用:
sd_json_success('保存成功');或者:
sd_json_error('保存失败');这样可以保持插件和 Sndow CMS 后台的提示和返回格式一致。
使用插件配置和 Meta 存储数据
插件自身的配置,推荐使用 sd_config() 和 sd_set_config():
$enabled = sd_config('my_plugin', 'enabled', '0');
sd_set_config(
'my_plugin',
'enabled',
'1'
);如果需要保存数组,可以自行编码为 JSON:
$settings = [
'enabled' => true,
'title' => '示例标题',
];
sd_set_config(
'my_plugin',
'settings',
json_encode($settings, JSON_UNESCAPED_UNICODE)
);文章、页面和用户扩展数据,则可以使用 Meta:
update_post_meta($post_id, 'my_plugin_data', [
'score' => 10,
]);
$data = get_post_meta($post_id, 'my_plugin_data', true);插件不应该自行修改 active_plugins,插件启用和停用状态应由系统后台统一维护。
优先使用系统模型和数据库接口
Sndow CMS 提供了文章、分类、标签、评论、用户、菜单、附件等模型。
global $post, $category, $tag, $comment, $user;
$item = $post->get($post_id);
$posts = $post->getAll(
"post_status = ? AND post_type = ?",
['publish', 'post'],
'post_date DESC',
10,
0
);
$total = $post->getCount(
"post_type = ?",
['post']
);只有在模型无法满足需求时,才直接使用 $db:
global $db;
$prefix = $db->getPrefix();
$row = $db->get(
"SELECT * FROM {$prefix}my_plugin_items WHERE item_id = ?",
[$item_id]
);数据库操作必须使用参数绑定,不能拼接用户输入。
使用全局上传函数
Sndow CMS 提供了统一的 sd_upload_file():
$result = sd_upload_file($_FILES['file'], [
'allowed_exts' => ['jpg', 'png', 'mp4'],
'allowed_mimes' => ['image/jpeg', 'image/png', 'video/mp4'],
'max_size_mb' => 20,
]);
if (!$result['success']) {
sd_json_error($result['message']);
}
$file_url = $result['url'];
$attachment_id = $result['id'];该函数会统一处理:
- 文件扩展名
- 实际 MIME 类型
- 后台允许的文件类型
- 最大上传大小
- 上传目录
- 附件记录
- 图片缩略图
插件不应该只根据文件名后缀判断文件类型,也不应该重复实现上传目录和附件写入逻辑。
注册插件小工具
插件可以注册自己的小工具类型:
sd_register_widget_type('my_plugin_notice', [
'name' => '插件提示',
'icon' => 'bi-info-circle',
'desc' => '显示插件提示信息',
'render_callback' => function ($config) {
echo '<div>' .
htmlspecialchars($config['text'] ?? '', ENT_QUOTES, 'UTF-8') .
'</div>';
},
]);小工具类型建议使用插件前缀,例如:
my_plugin_notice
sd_toc_widget
image_localizer_status这样可以避免和主题或其他插件的小工具类型重名。
扩展开放 API
插件可以使用 api_custom_action 扩展自定义 API:
add_filter('api_custom_action', function ($handled, $action, $api) {
if ($action !== 'my_plugin/status') {
return $handled;
}
$api->success([
'enabled' => true,
]);
return true;
}, 10, 3);插件还可以通过以下过滤器扩展系统接口:
add_filter('api_home_data', function ($data) {
$data['my_plugin'] = [
'enabled' => true,
];
return $data;
});API 的总开关、Token、请求限流、最大请求数量和跨域来源由后台系统设置控制。插件不应该绕过系统已有的 API 安全检查。
插件资源加载
插件 CSS 和 JavaScript 可以使用系统资源函数:
sd_enqueue_style(
'my-plugin-style',
sd_plugin_url(__FILE__, 'assets/css/plugin.css'),
[],
'1.0.0'
);
sd_enqueue_script(
'my-plugin-script',
sd_plugin_url(__FILE__, 'assets/js/plugin.js'),
[],
'1.0.0',
true
);插件文件路径可以使用:
$path = sd_plugin_path(__FILE__, 'data/cache.json');这样可以避免硬编码服务器绝对路径,也方便插件在不同目录中运行。
插件发布前检查
插件打包前建议检查以下内容:
- 主文件包含完整插件头部信息
- ZIP 内只有一个顶层插件目录
- 主文件名和目录名保持一致
- 生命周期函数使用插件 slug 前缀
- 后台页面有权限检查
- 写操作包含 CSRF 验证
- 数据库查询使用参数绑定
- 上传功能使用
sd_upload_file() - 不修改系统的
active_plugins - 停用插件不会误删数据
- 卸载只清理本插件自己的配置和数据
- 没有调试输出、绝对路径和敏感 Token
- 所有 PHP 文件通过
php -l检查
总结
Sndow CMS 插件开发 Skill 将插件目录、生命周期、后台页面、配置存储、数据库、上传、小工具和 API 扩展整合在一起,为开发者提供了一套更加清晰的开发路径。
使用这套 Skill,可以减少插件开发过程中因生命周期函数冲突、权限遗漏、CSRF 缺失、数据库查询不规范和上传逻辑重复而产生的问题,让插件更容易维护,也更适合发布到 Sndow CMS 应用商店。