cms-development.md 19 KB

CMS二次开发

面向基于 PHP+MySQL 的 CMS 二次开发场景,提供从环境探测、兼容性修复、插件开发到安全加固的全链路指引。覆盖 EmpireCMS、WordPress、ThinkPHP、Laravel 等主流 CMS/框架。

输入要求

  1. 开发任务(必填):需要实现的二次开发功能或修复目标
  2. CMS 类型与版本(可选):如未提供,按自动探测流程识别
  3. PHP 版本(可选):如未提供,按默认策略选择
  4. 已有代码(可选):涉及修改的现有代码片段或文件路径

执行流程

第一步:CMS 自动探测

逐项匹配,命中即停:

| 特征文件 | CMS | | - | - | | e/class/connect.php | EmpireCMS | | wp-config.php | WordPress | | thinkphp/base.php 或 vendor/topthink | ThinkPHP | | artisan + bootstrap/app.php | Laravel | | system/core/CodeIgniter.php | CodeIgniter | | data/config.php + simplewind/ | ThinkCMF | | index.php + data/conf/ | DedeCMS | | composer.json 含 slim/slim | Slim | | composer.json 含 hyperf | Hyperf | | composer.json 含 yii | Yii |

未确认 CMS 类型前,禁止生成任何框架特定代码。

✓ CMS探测: [CMS名称] [版本号]
  特征文件: [匹配到的特征文件]
  表前缀: [数据库表前缀]
  配置文件: [配置文件路径]

第二步:环境与版本确认

PHP 版本选择

| CMS | 最低版本 | 推荐版本 | 注意事项 | | - | - | - | - | | EmpireCMS 7.5 | 7.4 | 8.2 | 需 PHP 8 兼容补丁 | | WordPress 6.x | 7.4 | 8.2 | 部分老插件可能不兼容 8.3+ | | ThinkPHP 6 | 7.4 | 8.2 | | | ThinkPHP 8 | 8.0 | 8.2 | | | Laravel 9 | 8.0 | 8.1 | | | Laravel 10 | 8.1 | 8.2 | | | Laravel 11 | 8.2 | 8.3 | | | CodeIgniter 4 | 7.4 | 8.2 | | | DedeCMS v5.7 | 5.6 | 7.4 | 不支持 PHP 8 | | Hyperf | 8.0 | 8.2 | 建议跟随 Swoole 版本 |

版本选择原则:优先匹配 CMS 要求 → 用户指定版本 → 默认 8.2

PHP 可执行文件探测协议

严禁硬编码 PHP 路径(如 F:\BtSoft\php\ 仅是当前用户本地环境,跨用户/跨平台不可用)。LLM 在执行 lint/运行脚本前,必须按以下优先级链探测 PHP_BIN:

| 优先级 | 探测源 | 适用场景 | 示例 | | - | - | - | - | | 1 | 环境变量 PHP_BIN | 用户显式指定 | set PHP_BIN=F:\BtSoft\php\85\php.exe | | 2 | 环境变量 PHP_{VERSION}(如 PHP_82) | 多版本共存时按版本指定 | set PHP_82=F:\BtSoft\php\82\php.exe | | 3 | 系统 PATH 中的 php 命令 | 已加入 PATH 的环境 | php -v 可用 | | 4 | 常见安装位置自动扫描 | 宝塔/XAMPP/Homebrew 等默认安装 | 见下表 | | 5 | 询问用户并写入 PHP_BIN | 以上均未命中时,强制询问 | 用户确认后写入环境变量 |

常见安装位置默认值(优先级 4,按平台扫描):

| 平台 | 扫描路径 | 探测特征 | | - | - | - | | Windows | F:\BtSoft\php\{ver}\php.exe / D:\phpstudy\php\{ver}\php.exe / C:\xampp\php\php.exe / C:\Program Files\php\php.exe | 目录存在且 php.exe 可执行 | | macOS | /opt/homebrew/bin/php@{ver} / /usr/local/bin/php@{ver} / /Applications/MAMP/bin/php/php{ver}/bin/php | 文件存在且可执行 | | Linux | /usr/bin/php{ver} / /usr/local/bin/php{ver} / /opt/php/{ver}/bin/php | 文件存在且可执行 |

多版本共存处理:

  • 当用户规则中声明多版本(如 7.4/8.0/8.1/8.2/8.3/8.4/8.5)时,按「版本选择原则」确定目标版本号
  • 用目标版本号匹配 PHP_{VERSION} 环境变量(如目标 8.2 → 查 PHP_82)
  • 未匹配到精确版本时,回退到 PHP_BIN 或 PATH 中的 php
  • 探测过程必须输出日志:[PHP-PROBE] 目标版本 8.2 → 优先级 X → 命中: {实际路径}

第三步:数据库操作规范

存储引擎

默认 InnoDB(支持事务、行锁、崩溃恢复,与 mysql-database.md 对齐);只有该 CMS 历史项目已大规模使用 MyISAM 且无事务需求时,才显式标注保留 MyISAM。

访问层优先级

  1. CMS 官方数据访问层(WP: $wpdb / TP: Db 类 / Laravel: Eloquent / ECMS: $empire->query())
  2. PDO 预处理(兜底)
  3. 禁止 mysql_* / mysqli_* 原生函数(CMS 内核已封装除外)

查询安全红线

| 场景 | 禁止 | 强制 | | - | - | - | | 条件拼接 | "WHERE id=$id" | Prepared Statements | | LIKE | LIKE "%$kw%" | LIKE CONCAT('%', ?, '%') | | IN 子句 | 手动拼串 IN(1,2,3) | 动态占位符 IN(?,?,?) | | 批量写入 | 循环单条 INSERT | 事务 + VALUES 批量 | | 结果集 | 无限制全量拉取 | LIMIT/OFFSET 或游标 |

类型映射

| MySQL 类型 | PHP 类型 | 说明 | | - | - | - | | INT/BIGINT | int | | | DECIMAL | string | 禁止 float,防金额精度丢失 | | DATETIME | DateTimeImmutable | 或 CMS 原生时间类 | | JSON | array | MySQL 5.7+ 原生类型 | | NULL | null | 禁止用空字符串替代 |

会话设置

SET NAMES utf8mb4;
SET time_zone = '+08:00';
SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';

CMS 数据库差异

| CMS | 表前缀 | 配置文件 | 特殊字段 | | - | - | - | - | | EmpireCMS | phome_ 可自定义 | e/config/config.php | 信息表分主表+副表+索引表 | | WordPress | wp_ 可自定义 | wp-config.php | wp_options 存序列化数据 | | ThinkPHP | 无默认前缀 | config/database.php | 遵循 ORM 定义 | | Laravel | 无默认前缀 | .env | Migration 管理结构 | | DedeCMS | dede_ | data/common.inc.php | 旧式 mediumint 主键 |

第四步:PHP 8.x 兼容性检查

对现有代码执行兼容性扫描:

| 规则 | 严重度 | 修复 | | - | - | - | | 数组键加引号 | 致命 | $arr[key] → $arr['key'] | | 可选参数不可先于必选参数 | 弃用 | 交换参数顺序 | | each() | 已移除 | 改用 foreach() | | create_function() | 已移除 | 匿名函数 | | $HTTP_RAW_POST_DATA | 已移除 | php://input | | (real) 类型转换 | 已移除 | (float) | | get_magic_quotes_gpc() | 已移除 | function_exists() 包裹 | | strftime() | 已弃用 | date() 或 DateTime::format() | | 双引号内 ${var} | 已弃用 | {$var} | | # 注释中的 #[ | 属性冲突 | 改用 // 注释 |

兼容性验证命令(路径由 PHP 探测协议动态获取,以下用 {PHP_BIN} 和 {PHP_BIN_LOW} 占位):

# 目标版本 lint
{PHP_BIN} -l file.php
# 低版本兼容检查(用 PHP_74 环境变量或探测到的 7.4 路径)
{PHP_BIN_LOW} -l file.php

第五步:安全红线检查

| 漏洞 | 最低防护 | | - | - | | SQL 注入 | 内部接口也必须参数绑定 | | XSS | 输出必须 htmlspecialchars(..., ENT_QUOTES, 'UTF-8') 或 CMS 等效函数 | | CSRF | 状态变更操作必须验证 Token | | 文件上传 | MIME 校验 + 扩展名白名单 + 重命名 + 非执行目录 | | 反序列化 | 禁止对不可信数据使用 unserialize(),改用 JSON | | 密码 | password_hash() / password_verify(),禁止 MD5/SHA1 | | Include | 禁止 include $user_input,路径必须白名单或固定 |

第六步:插件/模块开发

开发标准

  • 一功能一文件:独立功能一个 PHP 文件,清晰分明
  • 命名:action_module_function.php
  • 目录:按功能模块分子目录
  • 入口:单一入口,不暴露内部文件
  • 依赖:通过 CMS 标准 API 调用,不跨插件直接 include

IDE 通用排除目录

CMS 缓存(data/dbcache/、data/fc/、runtime/、e/tmp/)、第三方依赖(vendor/、node_modules/)、上传附件(uploads/、d/file/)、备份导出(backup/、sql_dump/、back/)、版本控制(.git/、.svn/)应加入 IDE 排除列表,避免索引和监视。

AJAX 渐进式防卡死架构

CMS 后台长任务必须优先采用 Init → Step → Poll 架构,禁止单请求同步执行到底。

强制适用场景:

  • 批量导入/导出、批量更新、批量删除
  • 生成静态页、重建索引、清理缓存、图片压缩
  • 内容采集、远程同步、第三方接口批量拉取
  • 预计执行时间超过 3 秒,或处理数据量超过 100 条

端点职责:

| 端点 | 职责 | 必须返回 | | - | - | - | | Init | 校验权限/CSRF/参数,创建任务记录,计算总量 | task_id, total, status=queued | | Step | 每次只处理一小批数据,更新进度和错误列表 | processed, total, percent, has_more | | Poll | 前端轮询任务状态,不执行重业务逻辑 | status, percent, message, errors | | Cancel | 标记任务取消,不立即杀进程 | status=cancelled,正在执行的 Step 在下一批次检测取消标记后退出 |

实现约束:

  • 每个 Step 必须限制批量大小,例如 20-100 条,避免 PHP 超时
  • 任务状态必须持久化到数据库、缓存或任务文件,不能只依赖 PHP 内存
  • Step 必须可重复调用,使用游标/offset/last_id 保证幂等
  • 状态变更必须校验登录态、权限和 CSRF Token
  • 错误必须记录到任务错误列表,允许部分失败后继续处理
  • 前端必须显示进度、当前批次、失败数、重试/取消入口
  • Step 每批次开始必须检测取消标记,若已取消则停止处理并标记 status=cancelled,不允许继续执行剩余批次
  • 僵尸检测:Poll 发现 status=running 且 updated_at 超过阈值(建议 2× 单批预估耗时或固定如 30 分钟)无更新,标记为 failed(僵尸)或允许客户端 resume 重跑未完成批次;前端据此展示"任务疑似中断,可重试"
  • Poll 间隔建议 800-2000ms,连续失败 3 次后停止并提示

最小响应示例:

{
  "task_id": "build_20260702_001",
  "status": "running",
  "processed": 120,
  "total": 500,
  "percent": 24,
  "has_more": true,
  "message": "正在处理第 120/500 条",
  "errors": []
}

第七步:代码风格与质量

核心原则:二开/插件生成的代码风格必须与原项目已有实际代码一致,而不是 Agent 想象或技能示例风格。

强制步骤(生成/修改前):按 style-alignment.md「风格嗅探协议」先取样目标项目 1-2 个代表性已有文件(同目录/同模块优先),提取缩进、开标签、括号位置、数组键引号、模板变量命名(如 $navinfor/$empire)、PHP/HTML 混编方式等标记,产出「风格基线」块,逐行对齐后再写。

风格套用优先级(与风格基线冲突时以基线为准;仅当项目无任何可参照文件时回退到下表): CMS 官方规范 > .editorconfig/phpcs.xml > 风格基线(项目现有风格) > 下表内置约定(兜底) > PSR-12

| 类型 | 约定(兜底默认值) | 示例 | | - | - | - | | 类名 | PascalCase | UserController | | 方法/函数 | camelCase | getUserById() | | 变量 | camelCase | $userId | | 常量 | UPPER_SNAKE | MAX_ATTEMPTS | | 字段/表名 | snake_case | created_at | | 注释 | 中文 | // 校验登录态 |

五条自审

| # | 规则 | 判定标准 | | - | - | - | | 1 | 单一职责 | 每个函数只做一件事,不超过 40 行 | | 2 | 早返回 | 异常先 return/throw,主逻辑不被 if 嵌套包裹超过 2 层 | | 3 | 无魔法数字 | 硬编码数字/字符串提取为常量或配置 | | 4 | 外部输入必校验 | $_GET/$_POST 在使用前校验类型和范围 | | 5 | 错误处理闭环 | 每个 try 有 catch,每个 catch 有日志或提示,不吞异常 |

技术债禁令

| # | 模式 | 正确做法 | | - | - | - | | 1 | if($a = func()) 赋值当判断 | 拆两行:$a = func(); if($a !== null) | | 2 | 函数返两种类型 array\ | false | 统一返回类型,空用 [],异常用 throw | | 3 | global $var 在函数内 | 改为参数传入或依赖注入 | | 4 | switch(true) | 用 match 或 if-elseif 显式表达 | | 5 | 注释掉的代码块留着 | 直接删除,Git 历史可恢复 | | 6 | else 后紧跟 if 不合并 | 用 elseif 或提前 return 消除 else | | 7 | 函数参数超过 5 个 | 封装为对象/数组或拆分子函数 | | 8 | 循环内 .= 拼接大字符串 | 压入数组最后 implode() |

第八步:交付检查清单

| # | 检查项 | 方法 | | - | - | - | | 1 | 数组键加引号 | grep -Pn '\$[a-z_]+\s*\[[a-z_]' *.php | | 2 | 无裸 SQL 拼接 | 人工审查 $_GET/$_POST 直拼 | | 3 | 输出已转义 | echo/print 后有无 htmlspecialchars | | 4 | PHP Lint | php -l file.php | | 5 | 错误日志已清空 | > {PHP_ERROR_LOG}(路径由 PHP 探测协议推断,通常为 {PHP_DIR}/logs/php_errors.log 或 sys_temp_dir('php')/errors.log) | | 6 | 事务边界正确 | 写操作路径 try/catch + rollback | | 7 | 长任务防卡死 | 批量任务采用 Init → Step → Poll,Step 有批量大小和进度持久化 | | 8 | 依赖锁定 | 检查 composer.lock 是否存在并提交;composer.json 中无 latest/*/dev-main 浮动版本 |

验证证据类型声明(对齐主流程 Step 4,CMS 场景必填):

| 证据类型 | 适用场景 | 最小字段 | | - | - | - | | 命令+输出 | PHP lint、PHP8 兼容扫描、grep 检查(数组键引号/裸 SQL/输出转义) | 命令文本 + 退出码 + 关键输出片段 | | API 响应 | Init/Step/Poll 长任务接口联调(批量同步/导入导出/生成静态页) | Status Code + Response Body + 请求参数 | | 测试报告 | 插件功能测试、批量任务回归、事务回滚验证 | 测试用例数、通过数、失败用例清单 | | 截图+步骤 | 后台管理页面、CMS 模板渲染、H5 页面 | 截图 + 操作步骤 + 浏览器/PHP 版本 |

未声明证据类型的验证视为未完成(见 SKILL.md Step 4)。

第九步:记录到项目记忆

开发完成后,将关键决策记录到项目记忆(参见 project-memory-management.md):

  • 记录 CMS 类型、版本和 PHP 版本选择理由(Decision Record 模式)
  • 记录数据库表结构和前缀约定(Convention Capture 模式)
  • 记录安全加固措施和已知兼容性问题
  • 记录插件/模块的目录结构和命名约定

实战请求示例

示例一:后台管理功能

帮我给 WordPress 后台加一个订单备注管理页。
环境:WordPress 6.x,PHP 8.2,MySQL 8.0。
功能:按订单号搜索备注;新增/编辑备注;记录操作人和时间。
安全:使用 $wpdb prepare;后台权限校验;状态变更加 nonce;输出转义。
验证:给出数据库表结构、后台菜单入口、手动测试步骤和 PHP lint 命令。

优先加载:cms-development.md、mysql-database.md、code-generation.md。

示例二:帝国CMS 批量处理

帮我给帝国CMS 7.5 做一个批量同步文章状态的后台工具。
环境:PHP 8.2,表前缀 phome_。
要求:先探测 CMS 和 PHP 版本;不能单请求跑完;必须 Init-Step-Poll;每批 100 条。
安全:后台登录态、权限、CSRF、输出转义;禁止直接改缓存文件。
验证:给出 Init/Step/Poll 响应示例、错误日志检查路径和清缓存说明。

优先加载:cms-development.md、api-design.md、test-generation.md。

输出格式

## CMS 二次开发交付说明

### 1. 环境信息

- **CMS**: [名称] [版本]
- **PHP**: [版本]
- **数据库**: [类型] [版本]
- **表前缀**: [前缀]
- **配置文件**: [路径]

### 2. 变更文件清单

| 文件 | 操作 | 说明 |
| ------ | -------------- | ---------- |
| [路径] | 新增/修改/删除 | [功能说明] |

### 3. 数据库变更

| 类型 | SQL | 说明 |
| ------- | --------- | ---------- |
| DDL/DDL | [SQL语句] | [变更说明] |

### 4. 安全检查

| 检查项 | 状态 | 说明 |
| ------------ | ----------- | ------ |
| SQL 注入防护 | 通过/未通过 | [说明] |
| XSS 防护 | 通过/未通过 | [说明] |
| CSRF 防护 | 通过/未通过 | [说明] |
| 文件上传安全 | 通过/未通过 | [说明] |

### 5. 兼容性检查

| 检查项 | PHP [版本] | 说明 |
| -------- | ----------- | ------ |
| 语法兼容 | 通过/未通过 | [说明] |

### 6. PHP Lint 结果

| 文件 | 状态 |
| -------- | --------- |
| [文件名] | 通过/失败 |

### 7. 已知限制与后续建议

- [限制1]
- [建议1]

质量标准

  • 未确认 CMS 类型前禁止生成框架特定代码
  • 建表必须使用 InnoDB 引擎,MyISAM 仅在有明确 CMS 历史约束时保留
  • 必须使用 CMS 官方数据访问层,禁止原生 SQL 拼接
  • DECIMAL 金额字段禁止用 PHP float
  • 输出必须转义(XSS 防护)
  • 状态变更操作必须验证 CSRF Token
  • PHP 8.x 兼容性必须通过 lint 检查
  • 插件必须遵循一功能一文件、单一入口原则
  • 代码风格必须遵循 CMS 官方规范 > PSR-12
  • 禁止使用 ==,统一 ===

失败回退机制

| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 | | - | - | - | - | - | | 第一步:CMS 自动探测 | 无特征文件匹配 | 列出最接近的 CMS 类型,要求用户确认 | 1 | 输出 CMS 特征检查清单,由用户手动确认 | | 第二步:环境与版本确认 | 目标 PHP 版本与 CMS 不兼容 | 降级到 CMS 推荐的最低稳定版本 | 1 | 输出版本兼容矩阵,由用户决策 | | 第三步:数据库操作规范 | CMS 数据访问层文档缺失 | 降级到 PDO 预处理,标注"未使用 CMS 原生 API" | 1 | 输出数据库操作建议,建议查阅 CMS 官方文档 | | 第四步:PHP 8.x 兼容性检查 | 兼容性问题数量超出单次修复能力 | 按致命/弃用分级,优先修复致命项 | 2 | 输出兼容性问题清单,建议分批修复 | | 第五步:安全红线检查 | 发现安全漏洞 | 立即修复,标注修复影响范围 | 0 | 安全问题零容忍,不发布含已知漏洞的代码 | | 第六步:插件/模块开发 | CMS 插件机制文档不可用 | 按通用单一入口模式开发,标注"未遵循 CMS 插件规范" | 1 | 建议查阅 CMS 官方插件开发文档后补充 | | 第七步:代码风格与质量 | 代码风格与 CMS 内核不一致 | 优先匹配 CMS 内核风格,标注风格差异 | 1 | 输出风格差异清单,建议统一 | | 第八步:交付检查清单 | Lint 检查未通过 | 修复语法错误后重新检查 | 3 | 输出未通过文件清单,建议人工修复 | | 第九步:记录到项目记忆 | 项目记忆系统不可用 | 输出 CMS 开发配置到本地文件 | 1 | 标注"开发配置未沉淀",提示用户手动保存 |

关联 reference

  • frontend-design(前端设计)— CMS 模板页面、后台管理页面、H5 页面需参考 frontend-design.md 的视觉、交互、响应式和浏览器验证规范
  • code-generation(代码生成)— CMS 代码生成时参考本 Skill 的数据访问层和安全规范
  • bug-diagnosis(Bug诊断)— CMS Bug 诊断时参考本 Skill 的 PHP 8.x 兼容性检查和常见模式
  • code-review(代码审查)— CMS 代码审查时参考本 Skill 的安全红线和技术债禁令
  • tech-selection(技术选型)— CMS 版本选型时参考本 Skill 的 PHP 版本推荐矩阵
  • mysql-database(MySQL数据库)— CMS 表结构、索引、SQL、事务、慢查询和迁移回滚参考 mysql-database.md
  • project-memory-management(项目记忆管理)— 开发配置和约定沉淀时参考本 Skill 的记录模板