简介
本文件聚焦 DouPHP 框架的"模块系统核心机制",围绕以下目标展开:
- 模块发现机制:模块目录扫描、配置文件解析、依赖关系检测。
- 模块生命周期管理:从加载到卸载的完整流程。
- 模块配置管理:column_module、single_module 等配置项的作用与用途。
- 模块间通信与资源共享策略。
- 模块开发最佳实践与常见问题解决方案。
- 扩展性与可维护性设计说明。
- 新增:模块文件抽取脚本对admin/nav目录声明式导航文件的支持。
项目结构
DouPHP 将模块系统能力拆分为"配置读取—特性开关—路由注册—运行时解析—后台安装/卸载"五层,关键路径如下:
- 启动阶段统一载入并缓存模块配置,构建全局映射常量。
- 服务层提供"原始数据读取""核心设定构建""功能闸"等读模型。
- 路由层提供模块分类判定与父模块启用检查。
- 运行期通过可选模块解析器按需获取 Service 实例。
- 后台控制器与服务负责在线/本地安装与卸载流程。
- 新增:模块文件抽取脚本支持admin/nav目录的声明式导航文件处理。
graph TB
A["启动引导<br/>core/bootstrap.php"] --> B["模块配置读取<br/>ModuleSettingReader"]
B --> C["核心模块设定<br/>CoreModuleSettings"]
C --> D["功能开关计算<br/>ModuleFeatureGate"]
D --> E["路由模块注册表<br/>ModuleRegistry"]
E --> F["运行时模块解析<br/>Extension.Module"]
G["后台模块管理<br/>ModuleController + ModuleService"] --> H["云端/本地安装与卸载"]
H --> I["存储标记与更新记录<br/>storage/installed/*.installed.php"]
J["模块文件抽取脚本<br/>_'/tool/index.php"] --> K["admin/nav目录处理<br/>声明式导航文件"]
K --> L["AdminMenuRegistry<br/>菜单注册表"]
核心组件
- 模块配置读取器:负责从常量或文件中读取 module.php 的原始数组。
- 核心模块设定:过滤并合并 column_module、singlemodule、link、noshow,并生成 all_module。
- 功能开关闸:基于已启用模块列表生成 features 数组,供运行时按 feature 开关控制。
- 路由模块注册表:提供 isColumn/isSingle/isFixedFront/isSystemReserved/isParentModuleEnabled 等判定。
- 运行时模块解析:按短名或 dot 键探测 Service 类,结合 features 开关决定是否可用。
- 后台模块管理:在线/本地安装与卸载流程,含校验、确认、清理与审计日志。
- 新增:模块文件抽取脚本:支持admin/nav目录的声明式导航文件处理,确保模块打包时正确包含导航配置。
- 新增:AdminMenuRegistry:后台菜单注册表,支持模块声明文件的动态装配。
架构总览
下图展示从启动到请求处理的模块系统调用链路与职责边界。
sequenceDiagram
participant Boot as "启动引导"
participant Reader as "配置读取器"
participant CoreCfg as "核心设定"
participant Gate as "功能闸"
participant Reg as "路由注册表"
participant Ext as "运行时解析"
participant Admin as "后台模块管理"
participant Tool as "模块文件抽取脚本"
participant MenuReg as "AdminMenuRegistry"
Boot->>Reader : 读取 module.php / DOU_MODULE_SETTING
Reader-->>Boot : 原始数组
Boot->>CoreCfg : 构建 column/single/all_module 等
CoreCfg-->>Boot : 核心设定
Boot->>Gate : 基于 all_module 生成 features
Gate-->>Boot : features 数组
Boot->>Reg : 注入 Config(module.*)
Note over Reg : 三端路由解析使用 isColumn/isSingle/父模块启用判定
Ext->>Ext : 按短名/dot键探测 Service 类
Ext->>Gate : 检查 features.{key}
Ext-->>Ext : 返回实例或 null
Admin->>Admin : 在线/本地安装与卸载
Admin-->>Boot : 更新 storage/installed 与云端记录
Tool->>Tool : 抽取 admin/nav 目录声明文件
Tool->>MenuReg : 模块包包含导航配置
MenuReg->>MenuReg : glob 合并模块声明文件
详细组件分析
模块发现机制
- 模块目录扫描:框架不强制要求固定目录结构,而是通过命名空间与约定(core/front/admin/api)+ 类存在性探测来发现模块实现。
- 配置文件解析:启动时统一读取 config/module.php,并序列化为常量 DOU_MODULE_SETTING;同时构建 DOU_MODULE_MAP(column_module、single_module、all_module)。
- 依赖关系检测:通过 Module::requiresUser 与 assertUserAvailable 在路由级阻断未启用 user 模块时的衍生模块访问;ModuleRegistry::isParentModuleEnabled 用于判断父模块是否启用。
- 新增:admin/nav目录扫描:模块文件抽取脚本现在支持扫描和复制admin/nav目录下的声明式导航文件。
flowchart TD
Start(["启动"]) --> ReadCfg["读取 module.php / DOU_MODULE_SETTING"]
ReadCfg --> BuildMap["构建 column/single/all_module"]
BuildMap --> FeatureGate["生成 features 数组"]
FeatureGate --> RouteCheck{"路由命中衍生模块?"}
RouteCheck --> |是| AssertUser["断言 user 模块已启用"]
AssertUser --> |失败| Block["抛出域异常并跳转"]
AssertUser --> |成功| Allow["允许继续调度"]
RouteCheck --> |否| Allow
Allow --> End(["完成"])
NavScan["admin/nav目录扫描"] --> NavCopy["复制声明式导航文件"]
NavCopy --> Package["纳入模块包"]
模块生命周期管理
- 加载:启动阶段读取配置并构建 features;路由解析时根据 ModuleRegistry 判定模块类型与父模块状态;运行时按需解析 Service 实例。
- 运行:通过 Extension.Module 的 has/make 进行"特性开关 + 类存在性"双重校验,避免未安装模块导致构造失败。
- 卸载:后台触发卸载流程,校验 extend_id、检查是否存在数据、删除 installed 标记文件、清理云端记录并写入审计日志。
- 新增:导航文件生命周期:admin/nav目录下的声明式导航文件随模块安装就位,卸载时自动清理。
sequenceDiagram
participant Admin as "后台控制器"
participant Svc as "模块服务"
participant Store as "存储与云端"
participant Nav as "导航文件管理"
Admin->>Svc : destroy(extend_id, token)
Svc->>Svc : buildUninstallConfirm()
Admin->>Svc : performUninstall(extend_id)
Svc->>Store : 校验表数据/installed 文件
Svc->>Store : 清理云端记录/更新更新时间
Svc->>Store : 删除 *.installed.php
Nav->>Nav : 清理 admin/nav/<module>.php
Svc-->>Admin : 审计日志记录
模块配置管理机制
- column_module:栏目型模块集合,常用于有分类/列表能力的业务模块。
- single_module:单表/单页型模块集合,通常表示无分类或单一实体的业务模块。
- link_*:用户中心、工作台、订单关联等跨模块导航映射。
- no_show_menu/no_show_nav:隐藏菜单/导航的模块白名单。
- all_module:由 column_module 与 single_module 合并而来,作为"已启用模块全集"。
这些配置在启动时被读取、序列化并注入到 Config,供 ModuleRegistry、FeatureGate 等组件使用。
新增:admin/nav目录配置:模块文件抽取脚本现在将admin/nav目录添加到routeRoots配置中,确保导航文件被正确处理和复制。
模块间通信与资源共享策略
- 运行时解析:通过 Extension.Module 以短名或 dot 键解析 Service,结合 features 开关决定可用性,避免硬耦合。
- 依赖声明:对强依赖 user 的模块(如 order、vip、point 等),在路由级进行断言,确保依赖满足后再调度。
- 共享资源:通过容器(Container)与 Facade(DB、Session、Storage 等)共享基础设施;模块通过约定命名空间与类存在性被动态发现。
- 新增:导航资源共享:AdminMenuRegistry通过glob模式动态加载各模块的admin/nav/<module>.php声明文件,实现导航配置的模块化。
模块文件抽取脚本增强
新增:模块文件抽取脚本现已增强支持admin/nav目录的声明式导航文件处理:
- 配置增强:在routeRoots数组中添加'admin/nav',使其与其他声明式路由目录(admin/route、front/route、api/route)并列处理。
- 文件复制逻辑:在第1b步中,脚本会扫描每个模块对应的admin/nav/<module>.php文件,如果存在则复制到模块包的对应位置。
- 导航文件结构:每个模块的导航文件采用统一的PHP数组格式,包含title、icon、items等字段定义。
- 菜单注册集成:AdminMenuRegistry在启动时通过glob模式加载所有已安装的模块导航文件,实现动态菜单装配。
flowchart TD
Module["模块 <module>"] --> CheckNav["检查 admin/nav/<module>.php"]
CheckNav --> |存在| CopyNav["复制导航文件到模块包"]
CheckNav --> |不存在| SkipNav["跳过导航文件"]
CopyNav --> Install["安装时注册到菜单系统"]
SkipNav --> Next["处理下一个模块"]
Install --> Registry["AdminMenuRegistry 加载"]
Registry --> Glob["glob 匹配所有导航文件"]
Glob --> Merge["合并到菜单注册表"]
模块开发最佳实践
- 遵循命名规范:Service 类位于对应 layer 的 Service/{模块}/{模块}Service,便于自动探测。
- 明确模块类型:在配置中正确归类到 column_module 或 single_module,以便路由与前端呈现一致。
- 谨慎声明依赖:如需 user 模块能力,确保 features.user 开启或在路由层做好断言。
- 安全卸载:卸载前确保无数据残留,避免误删;利用后台提供的二次确认与审计日志。
- 增量升级:通过云端/本地安装包管理,保持 installed 标记与云端更新记录一致。
- 新增:导航文件开发:为模块创建admin/nav/<module>.php文件,定义清晰的子菜单结构和路由匹配规则。
扩展性与可维护性设计
- 解耦配置与逻辑:配置读取、核心设定、功能闸、路由判定分层清晰,便于独立演进。
- 约定优于配置:通过命名空间与类存在性探测减少显式注册成本,降低维护负担。
- 运行时门控:features 开关与路由级断言保证在不安装/关闭模块时不会引发致命错误。
- 可观测性:卸载流程记录审计日志,便于问题回溯。
- 新增:声明式导航:通过admin/nav目录的声明式文件替代硬编码菜单,提升模块的可移植性和可维护性。
依赖关系分析
- 启动引导依赖配置读取器与核心设定构建器,形成稳定的初始化链路。
- 路由注册表依赖 Config(module.*),为三端路由解析提供统一判定。
- 运行时模块解析依赖 features 开关与类存在性,避免未安装模块导致的构造失败。
- 后台模块管理依赖云端服务与存储标记,完成安装/卸载闭环。
- 新增:模块文件抽取脚本依赖admin/nav目录结构,确保导航文件被正确处理和包含。
- 新增:AdminMenuRegistry依赖glob模式扫描admin/nav目录,动态加载模块导航声明。
graph LR
Boot["启动引导"] --> Reader["配置读取器"]
Reader --> CoreCfg["核心设定"]
CoreCfg --> Gate["功能闸"]
Gate --> Reg["路由注册表"]
Reg --> Ext["运行时解析"]
Admin["后台模块管理"] --> Cloud["云端服务"]
Admin --> Store["存储标记"]
Tool["模块文件抽取脚本"] --> NavDir["admin/nav目录"]
NavDir --> MenuReg["AdminMenuRegistry"]
性能考量
- 启动阶段一次性读取并序列化模块配置,避免重复 IO。
- 运行时模块解析具备请求内缓存,减少重复探测开销。
- 路由判定基于内存中的 Config 数组,避免额外查询。
- 卸载流程前置校验(表数据、installed 文件)减少无效操作。
- 新增:导航文件加载缓存:AdminMenuRegistry对菜单注册结果进行请求级缓存,避免重复glob扫描。
故障排查指南
- 模块不可用:检查 features 开关与类是否存在;确认短名或 dot 键是否正确。
- 路由报错:确认父模块是否在 all_module 中启用;若命中衍生模块,确保 user 模块已启用。
- 卸载失败:检查 extend_id 合法性、是否存在数据、installed 文件是否存在;查看审计日志定位问题。
- 云端连接失败:确认云端服务可用性与 localsite 载荷;必要时重试或切换本地安装。
- 新增:导航文件问题:检查admin/nav/<module>.php文件语法是否正确;确认文件已被复制到模块包中;验证AdminMenuRegistry能否正确加载该文件。
结论
DouPHP 的模块系统通过"配置驱动 + 约定探测 + 运行时门控"的组合,实现了高内聚、低耦合的模块化架构。启动阶段集中处理配置与特性开关,路由层提供统一的模块判定,运行期按需解析模块服务,后台提供完整的安装/卸载闭环。新增的admin/nav目录声明式导航文件支持进一步提升了系统的可扩展性和可维护性,使模块能够自包含其后台菜单配置,减少了核心系统的硬编码依赖。该设计在保证扩展性的同时,兼顾了可维护性与安全性。
附录
- 常用配置项说明:
- column_module:栏目型模块清单。
- single_module:单表/单页模块清单。
- link_user_center/link_work_center/link_order_item:跨模块导航映射。
- no_show_menu/no_show_nav:隐藏菜单/导航的模块清单。
- 新增:routeRoots:包含admin/nav在内的声明式文件根目录清单。
- 推荐实践:
- 严格遵循命名规范,便于自动探测。
- 合理划分模块类型,确保前后端一致性。
- 在路由层做好依赖断言,避免未启用模块引发的错误。
- 使用后台提供的安装/卸载流程,配合审计日志追踪变更。
- 新增:为每个模块创建admin/nav/<module>.php文件,定义清晰的后台菜单结构。
- 新增:使用模块文件抽取脚本自动生成包含导航文件的模块包,确保部署一致性。