简介
本设计文档面向 DouPHP 框架的插件系统,聚焦以下目标:
- 插件发现机制:基于 manifest.php 的白名单校验与自动发现。
- 生命周期管理:从发现、注册到实例化、缓存与释放。
- 依赖注入容器集成:通过容器按需解析 Provider 及其依赖。
- 接口规范与扩展点:支付、物流、第三方登录三类 Provider 契约及能力扩展。
- 解耦与服务注册:以分组为边界的聚合器与注册中心,避免主系统耦合。
- 加载流程与配置解析:扫描、校验、映射、实例化的完整链路。
- 隔离机制、内存管理与性能优化:命名空间隔离、惰性加载、实例缓存与最小化 I/O。
项目结构
DouPHP 插件体系由“核心基础设施”和“具体插件实现”两部分组成:
- 核心基础设施位于 core/infra/plugin 与 _'/module/plugin/core/infra 下,提供契约、校验器、注册中心等。
- 具体插件位于 plugin 目录下,每个插件包含 manifest.php 与 Provider/Service 实现。
graph TB
A["应用启动"] --> B["插件发现<br/>扫描 PLUGIN_PATH"]
B --> C["清单校验<br/>ManifestValidator"]
C --> D["注册中心<br/>ConnectPluginRegistry"]
D --> E["依赖注入容器<br/>Container::make()"]
E --> F["Provider 实例<br/>PaypalProvider / OfflinepayProvider"]
F --> G["业务调用<br/>start()/status()/回调处理"]
核心组件
- 清单校验器 ManifestValidator:对 manifest.php 返回值进行白名单键校验、分组校验、Provider 命名空间校验,确保仅允许 payment/connect/shipping 分组且 provider 必须属于 Dou\Plugin\ 命名空间。
- 注册中心 ConnectPluginRegistry:扫描插件目录,读取 manifest.php,校验后建立 pluginId => providerClass 映射,并通过容器懒加载实例,提供 has()/provider() 等查询能力。
- 契约接口族:
- PaymentPluginProviderInterface:支付插件基础契约(如 start、meta、pluginId)。
- PollablePaymentProviderInterface:支持前端轮询查状态(扫码支付场景)。
- ReconcilablePaymentProviderInterface:支持后台对账/主动查询真实状态。
- ShippingPluginProviderInterface:物流插件契约。
- ConnectPluginProviderInterface:第三方登录插件契约。
- 服务契约 PluginServiceContract:面向业务的统一读取入口,屏蔽 plugin 模块是否启用的差异,提供默认值兜底。
架构总览
插件系统采用“约定优于配置 + 白名单校验 + 容器懒加载”的模式:
- 约定:每个插件在 plugin/<slug>/ 下提供 manifest.php,声明 plugin_group 与 provider。
- 校验:ManifestValidator 严格限制可接受字段与 provider 命名空间,防止任意代码执行。
- 发现:ConnectPluginRegistry 扫描 PLUGIN_PATH,收集合法 manifest,构建映射表。
- 实例化:通过 Container::make() 按需创建 Provider 实例,并缓存以避免重复构造。
- 使用:业务侧通过注册中心或 Facade 获取 Provider,调用标准方法完成支付、登录、物流等扩展功能。
classDiagram
class ManifestValidator {
+extractProvider(manifest, expectedGroup) string|null
<<白名单校验>>
}
class ConnectPluginRegistry {
-container Container
-providerClassMap map<string,string>
-providerInstances map<string,object>
+__construct()
+discover() void
+provider(pluginId) object?
+has(pluginId) bool
}
class PaymentPluginProviderInterface {
+pluginId() string
+meta() array
+start(request) string
}
class PollablePaymentProviderInterface {
+status(payload) string
}
class PaypalProvider {
-service
+pluginId() string
+meta() array
+start(request) string
}
class OfflinepayProvider {
-service
+pluginId() string
+meta() array
+start(request) string
}
ConnectPluginRegistry --> ManifestValidator : "校验清单"
ConnectPluginRegistry --> PaymentPluginProviderInterface : "实例化并缓存"
PaypalProvider ..|> PaymentPluginProviderInterface
OfflinepayProvider ..|> PaymentPluginProviderInterface
PollablePaymentProviderInterface <|-- PaymentPluginProviderInterface
详细组件分析
清单校验器 ManifestValidator
- 作用:对 manifest.php 返回数组进行白名单键校验、分组校验、Provider 命名空间校验。
- 关键规则:
- 仅允许 plugin_group、provider 两个顶层键。
- plugin_group 必须在 allowed groups 中(payment/connect/shipping),并与调用方期望分组一致。
- provider 必须是字符串且匹配 Dou\Plugin\ 命名空间模式,避免指向核心或管理端类。
- 失败策略:返回 null,由调用方静默跳过该插件,保证健壮性。
注册中心 ConnectPluginRegistry
- 作用:自动发现 connect 分组下的插件 Provider,建立映射并懒加载实例。
- 发现流程:
- 读取 PLUGIN_PATH,遍历子目录查找 manifest.php。
- 使用 ManifestValidator 校验 manifest,提取 provider FQCN。
- 维护 providerClassMap 与 providerInstances 缓存。
- 实例化:通过 Container::make() 创建实例,并验证其实现契约;若失败则返回 null。
- 查询接口:has(pluginId)、provider(pluginId)。
sequenceDiagram
participant App as "应用"
participant Reg as "ConnectPluginRegistry"
participant Val as "ManifestValidator"
participant FS as "文件系统"
participant Ctn as "容器(Container)"
participant Prov as "Provider实例"
App->>Reg : __construct()
Reg->>FS : 扫描 PLUGIN_PATH
FS-->>Reg : 列出插件目录
loop 每个插件目录
Reg->>FS : include manifest.php
FS-->>Reg : 返回数组
Reg->>Val : extractProvider(数组, "connect")
Val-->>Reg : 返回 provider FQCN 或 null
end
App->>Reg : provider("paypal")
Reg->>Ctn : make(providerFQCN)
Ctn-->>Reg : 返回 Provider 实例
Reg-->>App : 返回 Provider
支付插件 Provider 示例
- PayPal Provider:实现 PaymentPluginProviderInterface,提供 pluginId、meta、start 等方法,内部委托给 Service 处理具体逻辑。
- 离线付款 Provider:同样实现支付契约,提供元数据与开始支付流程。
- 轮询能力:若实现 PollablePaymentProviderInterface,则支持前端轮询查询支付状态(扫码支付场景)。
flowchart TD
Start(["调用 start(request)"]) --> Meta["读取 meta() 配置项"]
Meta --> Delegate["委托 Service 处理请求"]
Delegate --> ReturnURL["返回支付跳转 URL 或参数"]
ReturnURL --> End(["结束"])
插件清单 manifest.php
- 支付宝插件清单:声明 plugin_group 为 payment,provider 为 Dou\Plugin\Alipay\AlipayProvider。
- PayPal 插件清单:声明 plugin_group 为 payment,provider 为 Dou\Plugin\Paypal\PaypalProvider。
- 清单仅暴露必要信息,具体行为由 Provider 与 Service 实现。
业务侧插件服务契约 PluginServiceContract
- 作用:为三端(前台、后台、API)提供统一的插件读取能力,屏蔽 plugin 模块是否开启的差异。
- 关键能力:
- isAvailable():判断 plugin 模块可用。
- hasGroup(group)/valueByGroup()/getBySlug()/existsBySlug():按分组或 slug 读取插件记录。
- getWithConfig(slug):读取插件行并反序列化 config。
- defaultPaymentSlug():小程序/API 默认支付方式标识(offlinepay 优先,否则 wxpay)。
- hasConnect():是否启用第三方登录插件。
- 兜底:当 plugin 模块卸载或 features.plugin 关闭时,由 NullPluginService 提供空实现,保证调用面稳定。
依赖关系分析
- 低耦合:插件通过契约与核心交互,不直接依赖具体实现。
- 强约束:ManifestValidator 限制 manifest 结构与 provider 命名空间,降低安全风险。
- 懒加载:Provider 实例仅在需要时通过容器创建,减少启动开销。
- 分组边界:payment/connect/shipping 三类分组明确职责,便于扩展与维护。
graph LR
MV["ManifestValidator"] --> CR["ConnectPluginRegistry"]
CR --> CTN["Container"]
CTN --> PP["PaypalProvider"]
CTN --> OP["OfflinepayProvider"]
PP --> SVC_P["PaypalService"]
OP --> SVC_O["OfflinepayService"]
性能与内存管理
- 清单校验前置:在发现阶段即拒绝非法 manifest,避免后续无效工作。
- 实例缓存:注册中心维护 providerInstances,避免重复构造 Provider。
- 懒加载:通过容器按需解析 Provider 与其依赖,减少启动时间与内存占用。
- 最小 I/O:扫描 PLUGIN_PATH 仅读取必要的 manifest.php,其他资源按需加载。
- 建议优化:
- 对大型插件集合,可引入清单缓存(如文件哈希或数据库缓存)以减少扫描成本。
- 对高频调用的 Provider,可在容器层面做单例绑定,进一步降低构造开销。
- 对轮询场景(Pollable),建议结合服务端缓存与限流,避免频繁查询第三方网关。
故障排查指南
- 插件未生效:
- 检查 manifest.php 是否存在且返回合法数组。
- 确认 plugin_group 与调用方期望分组一致。
- 确认 provider FQCN 符合 Dou\Plugin\ 命名空间规则。
- 运行时找不到 Provider:
- 检查 ConnectPluginRegistry 的 discover() 是否成功扫描到 manifest。
- 检查 Container::make() 是否能解析类名与依赖。
- 轮询状态异常:
- 确认 Provider 实现了 PollablePaymentProviderInterface。
- 检查 status() 方法的入参与返回值是否符合契约。
- 默认支付方式不正确:
- 检查 PluginServiceContract::defaultPaymentSlug() 的实现逻辑(offlinepay 优先,否则 wxpay)。
结论
DouPHP 插件架构通过“白名单校验 + 自动发现 + 容器懒加载 + 契约驱动”的组合,实现了高内聚、低耦合的扩展机制。支付、物流、第三方登录三类插件遵循统一契约,具备清晰的职责边界与可扩展能力。注册中心与校验器保障了安全性与稳定性,容器与缓存策略提升了性能与可维护性。建议在大规模插件生态中引入清单缓存与单例绑定,进一步优化启动与运行时的表现。