简介
本文件面向 DouPHP 框架的“模块化架构”,围绕以下目标展开:
- 模块发现机制、依赖管理与生命周期管理
- 前台、后台、API 三端应用的模块化实现方式
- 模块间通信机制、资源共享策略、版本兼容性处理
- 路由解析器的模块化设计与模块配置管理机制
- 模块开发指南、最佳实践与常见问题解决方案
项目结构
DouPHP 采用“核心 + 多应用入口”的分层组织:
- 核心层 core:提供容器、路由、中间件、扩展点、ORM、文件系统、事件等基础设施
- 应用层 front/admin/api:各自拥有独立的入口、路由解析器、中间件栈、控制器与服务
- 配置层 config:集中管理站点、路由风格、模块清单等
- 运行时 storage:安装锁、状态缓存等
graph TB
A["入口 index.php"] --> B["核心引导 bootstrap.php"]
B --> C["配置加载 module.php"]
B --> D["DI 容器 & 门面注册"]
A --> E["前台 Router薄壳"]
A --> F["后台 Router薄壳"]
A --> G["API Router薄壳"]
E --> H["FrontResolver"]
F --> I["AdminResolver"]
G --> J["ApiResolver"]
H --> K["Dispatcher执行控制器/中间件"]
I --> K
J --> K
核心组件
- 模块配置与发现
- 通过 config/module.php 声明 column_module/single_module 等模块清单,并在 bootstrap 阶段统一读取并序列化为常量,供后续复用。
- Module 类负责按约定命名跨层探测 Service 类,结合 features 开关决定是否可用,并提供 make/has/className/register 等能力。
- 路由解析器
- 前台:FrontResolver 基于 PrettyRouteMatcher 匹配 URL,产出 DispatchPlan,组装默认中间件链,注入路由信息到 Request。
- 后台:AdminResolver 基于 BackendDeclaredMatcher 匹配 admin/route/*.php 声明条目,组装安全默认中间件链。
- API:ApiResolver 同样基于 BackendDeclaredMatcher,输出 JSON 错误响应,并组合 API 默认中间件链。
- 生命周期与依赖
- bootstrap 阶段完成常量定义、自动加载、别名、容器实例化、Request/Router 早绑定;各应用入口在 Init::boot 前设置路由语言与字符串,再进入 Init 初始化业务上下文。
- 模块间通信与共享
- 通过 DI 容器与 Module 门面按需解析服务;通过 Config 暴露 features 开关;通过 Request 传递路由元数据;通过 View 全局变量进行视图级共享。
架构总览
下图展示请求从入口到控制器执行的完整流程,以及三端路由解析器的职责边界。
sequenceDiagram
participant Client as "客户端"
participant Entry as "入口 index.php"
participant Boot as "核心引导 bootstrap.php"
participant FR as "前台 Router"
participant AR as "后台 Router"
participant AAR as "API Router"
participant Resolver as "ResolverFront/Admin/API"
participant Disp as "Dispatcher"
participant Ctrl as "控制器/服务"
Client->>Entry : HTTP 请求
Entry->>Boot : 加载核心引导
Boot-->>Entry : 容器/Request/路由已就绪
alt 前台请求
Entry->>FR : dispatch()
FR->>Resolver : FrontResolver : : resolve()
Resolver-->>FR : DispatchPlan
FR->>Disp : run(plan, container)
Disp->>Ctrl : 执行控制器/中间件
Ctrl-->>Client : Response
else 后台请求
Entry->>AR : dispatch()
AR->>Resolver : AdminResolver : : resolve()
Resolver-->>AR : DispatchPlan
AR->>Disp : run(plan, container)
Disp->>Ctrl : 执行控制器/中间件
Ctrl-->>Client : Response
else API 请求
Entry->>AAR : dispatch()
AAR->>Resolver : ApiResolver : : resolve()
Resolver-->>AAR : DispatchPlan
AAR->>Disp : run(plan, container)
Disp->>Ctrl : 执行控制器/中间件
Ctrl-->>Client : JSON Response
end
详细组件分析
模块发现与依赖管理(Module)
- 设计要点
- 支持短名键(如 comment)与 dot 键(如 user.user_level_option_builder)两种解析模式
- 按 core → front → admin → api 顺序探测 Service 类是否存在
- 通过 features.{key} 控制模块可用性;对强依赖 user 的衍生模块(order/vip/point/money/withdraw/share/favorites)提供 assertUserAvailable 闸控
- 提供 register 覆盖默认探测规则,便于非标准命名或 Infra 类接入
- 复杂度与性能
- has/make 包含 class_exists 探测与容器构造,建议单请求内复用结果(内部有 $resolved 缓存)
- 建议在入口或 Init 阶段预注册关键模块映射,减少运行期探测开销
classDiagram
class Module {
+static register(key, definition) void
+static has(key) bool
+static make(key) object?
+static className(key) string?
+static requiresUser(module) bool
+static assertUserAvailable(module) void
-static getDefinition(key) array?
-static resolveSingleDefinition(key, override) array?
-static resolveDotDefinition(key, override) array?
-static resolveAutoClass(studly) string?
}
前台应用模块化(Front)
- 路由解析
- 入口剥离语言前缀后交由 FrontResolver,使用 PrettyRouteMatcher 匹配声明式路由,产出 DispatchPlan
- 组装默认中间件链:安全头、可信代理、限流、可选用户认证、CSRF;可通过命中条目的 mw_* 字段细化
- 将 cur、form_action/form_method 等视图变量注入,便于模板渲染
- 生命周期
- 入口在 Init::boot 之前设置 routeString 与语言标识;Init 完成后进入 Route::dispatch
- 模块门禁
- 命中模块若为 user 衍生且 features.user 关闭,直接抛出 DomainException,由入口统一捕获渲染
flowchart TD
Start(["前台请求"]) --> Parse["LangPrefixParser 剥语言前缀"]
Parse --> Resolve["FrontResolver::normalize 匹配路由"]
Resolve --> Matched{"是否匹配?"}
Matched -- 否 --> NotFound["返回 notFound 计划"]
Matched -- 是 --> Gate["Module::assertUserAvailable(模块)"]
Gate --> MW["组装中间件链默认+路由级细化"]
MW --> Inject["注入路由信息到 Request/View"]
Inject --> Dispatch["Dispatcher 执行控制器"]
Dispatch --> End(["响应"])
后台应用模块化(Admin)
- 路由解析
- 基于 BackendDeclaredMatcher 匹配 admin/route/*.php 中的 declared 条目,确定控制器、方法、路径参数
- 默认中间件链:安全头、可信代理、认证、权限、CSRF、工作区;未匹配返回 notFound,方法不匹配返回 methodNotAllowed
- 表单目标装配
- create/edit 动作时统一计算 form_action 与 form_method,模板无需关心具体路由生成细节
sequenceDiagram
participant Entry as "后台入口"
participant R as "Admin Router"
participant Res as "AdminResolver"
participant Reg as "MiddlewareRegistry"
participant Disp as "Dispatcher"
Entry->>R : dispatch()
R->>Res : resolve(request, container)
Res->>Reg : compose(默认别名, 命中条目)
Reg-->>Res : 中间件链
Res-->>R : DispatchPlan
R->>Disp : run(plan, container)
Disp-->>Entry : Response
API 应用模块化(Api)
- 路由解析
- 与后台类似,但针对 JSON 响应:未匹配返回 404 JSON,方法不匹配返回 405 JSON 并附带 Allow 头
- 默认中间件链:安全头、可信代理、限流、可选用户认证(features.user)
- 模块门禁
- 同前台/后台,user 衍生模块在未启用 user 特性时拒绝访问
sequenceDiagram
participant Entry as "API 入口"
participant R as "Api Router"
participant Res as "ApiResolver"
participant Disp as "Dispatcher"
Entry->>R : dispatch()
R->>Res : resolve(request, container)
Res-->>R : DispatchPlan
R->>Disp : run(plan, container)
Disp-->>Entry : JSON Response
路由规则与配置管理
- 路由规则集中读取
- RouteRules 合并 config/route.php 与自定义规则,按 site.route_* 选择风格,供 UrlBuilder/PrettyRouteMatcher/RouteIdValidator 共用,避免重复 include
- 模块配置
- config/module.php 维护模块清单与显示策略(如 no_show_menu/no_show_nav),在 bootstrap 阶段序列化并定义为常量,供全系统复用
依赖关系分析
- 松耦合
- 三端 Router 仅依赖各自的 Resolver;Resolver 依赖匹配器与中间件注册表;控制器通过 DI 容器获取服务
- 模块依赖
- Module 类集中管理 features 开关与类探测;对 user 衍生模块提供强制前置校验
- 外部依赖
- 数据库连接、存储、日志等通过 Facade 与容器解耦;路由规则集中管理降低重复配置
graph LR
M["Module 门面"] --> Cfg["Config(features)"]
M --> Cont["DI 容器"]
FR["FrontResolver"] --> MR["PrettyRouteMatcher"]
AR["AdminResolver"] --> BR["BackendDeclaredMatcher"]
AAR["ApiResolver"] --> BR
FR --> MW["MiddlewareRegistry"]
AR --> MW
AAR --> MW
性能考量
- 启动期一次性加载模块配置并序列化为常量,避免多次 include
- 模块解析结果在请求内缓存($resolved),减少重复 class_exists 与容器构造
- 中间件链声明式组合,按需跳过或追加,避免无效检查
- 路由规则集中读取,减少重复解析成本
故障排查指南
- 404/未匹配
- 前台:FrontResolver 记录 unmatched 警告并返回 notFound;检查路由声明与 URL 格式
- 后台/API:BackendDeclaredMatcher 未命中返回 notFound;确认 route/*.php 中 declared 条目与方法限制
- 405 方法不允许
- 后台/API:返回 methodNotAllowed,并携带允许的方法列表;核对路由声明的 HTTP 方法
- 模块不可用
- Module::has 返回 false:检查 features.{key} 开关与类是否存在;必要时使用 Module::register 覆盖探测
- 用户模块依赖缺失
- 命中 user 衍生模块但 features.user 关闭:会抛出 DomainException;开启 user 特性或调整路由访问策略
结论
DouPHP 的模块化架构以“声明式路由 + 分层中间件 + 门面化模块解析”为核心,实现了前台、后台、API 三端的清晰分离与统一调度。通过 Module 门面与 features 开关,模块可按需启用与禁用;通过 DI 容器与 Request/View 共享,模块间通信与资源利用高效可控。该设计兼顾了可维护性、可扩展性与安全性,适合复杂业务的持续演进。
附录:模块开发指南与最佳实践
- 模块发现与命名
- 遵循 Service{Studly}{Studly}Service 命名规约;如需非标准命名,使用 Module::register 显式注册
- 使用 dot 键(feature.classBase)指向非默认命名的子能力
- 依赖与开关
- 通过 features.{key} 控制模块可用性;对 user 衍生模块,确保 features.user 开启或在入口处规避
- 路由与中间件
- 前台:在 front/route/*.php 中声明路由,并通过 mw_without/mw_append/mw_params 精细化中间件行为
- 后台/API:在对应 route/*.php 中声明 declared 条目,注意 HTTP 方法与优先级
- 资源共享
- 使用 Request 传递路由元数据;使用 View 全局变量进行视图级共享;通过 DI 容器获取服务实例
- 版本兼容
- 通过 features 开关平滑降级;前端/小程序侧根据 features 动态隐藏功能入口
- 常见问题
- 模块未生效:检查 features 开关与类是否存在;必要时清理请求内缓存 Module::forgetResolved
- 路由冲突:调整 declared 条目顺序与 pattern;确认方法限制
- 中间件异常:检查别名映射与类存在性;使用 withoutMiddleware 豁免特定路由