服务接管
2026/10/26大约 2 分钟
服务接管(ServiceRegistry)
L3 能力扩展:把基座某个"可接管点"的实现换成插件自己的,或在其外面包一层。app\basic\ServiceRegistry 按接管点 key 维护优先级链,基座照常调用 make($key) 拿到的就是被接管/增强后的实例,调用方无感。
与事件的区别
事件是"旁路通知、不改结果";服务接管是"替换或装饰这个能力本身、会影响返回"。要改行为结果用它,只做附带动作交给事件总线。
接管点从哪来
接管点(key => 默认实现类)统一声明在 config/plugin_extend.php 的 providers 列表,首次使用时懒加载登记。每个默认实现类须实现 app\interface\ServiceProviderInterface 并覆写 KEY 常量(点分命名,如 upload.config)。基座内置的接管点示例:app\service\upload\DefaultUploadConfigResolver。
插件不需要声明接管点也能挂上去——只要目标 key 已由基座声明,直接 bind/extend 即可。要新增一个全新接管点,才提供自己的默认实现类并让基座在 providers 登记。
覆盖实现 bind
整体换掉某个接管点的实现,优先级高者胜出(基座默认实现视为优先级 0,插件覆盖须 > 0):
use app\basic\ServiceRegistry;
// 写在 boot(),传 owner=插件名
ServiceRegistry::bind(
'upload.config', // 接管点 key
MyUploadConfigResolver::class, // 实现类 FQN
priority: 10,
owner: 'am_demo'
);装饰增强 extend
不换实现,只在原实例外面包一层,签名 fn(object $instance, array $params): object,返回装饰后的对象。多个装饰器按优先级升序应用,高优先级位于外层:
ServiceRegistry::extend(
'upload.config',
fn(object $instance, array $params): object => new WatermarkWrap($instance),
priority: 10,
owner: 'am_demo'
);解析 make(基座侧)
$resolver = ServiceRegistry::make('upload.config', $params);
$result = $resolver->resolve($config);make返回最高优先级的bind实现,再依次套用extend装饰器。- 没有任何接管、或接管实现实例化抛异常时,自动回退默认实现并记日志——保证基座行为不被插件搞挂。
- 开关
service_enabled=false时忽略一切 bind/extend,直接回默认实现。
判定与清理
| 方法 | 作用 |
|---|---|
provide($class) | 声明接管点(基座用,幂等) |
has($key) | 该接管点是否已有覆盖/增强 |
forgetByOwner($owner) | 禁用/卸载时按归属清理(基座自动调用) |
进程级
bind/extend 是进程内静态状态,一律写在 boot()。详见生命周期。
