ServiceProvider,整理如何将配置作为包的公开 API 进行维护。本页以Laravel 包开发为前提,并使用 laravel/framework 的 v13.34.0 来确认实现。
发布与合并是不同的处理
publishes() 注册复制的源路径和目标路径。在 vendor:publish 复制文件之前,用户的 config 目录不会发生变化。而 mergeConfigFrom() 则是在启动时更新配置仓库,并不会修改文件本身。
register() 中的合并使用默认值。
mergeConfigFrom() 只合并顶层
ServiceProvider::mergeConfigFrom() 会先传入包的配置,再传入应用的现有配置,然后执行 array_merge()。对于相同的字符串键,应用侧的值优先。
以下示例仅使用数组重现了框架的合并处理。
enabled 会被补充,但 transport 整个数组会被替换,transport.retries 不会保留。关键在于:如果用户已经发布了旧的 transport 数组,即使你在同一个数组中添加新键,这些键也不会被补充。
使用 replaceConfigRecursivelyFrom() 补充嵌套配置
Laravel 13 的ServiceProvider 中还有一个 protected 方法 replaceConfigRecursivelyFrom()。它以相同的顺序执行 array_replace_recursive()。
如果你希望配置 API 能够单独覆盖嵌套的字符串键,可以按如下方式修改提供者的 register()。对于同一个配置键,无需与前面的 mergeConfigFrom() 同时使用。
$defaults 和 $overrides,结果如下。
replaceConfigRecursivelyFrom() 是本页所确认的 Laravel 13 源代码中存在的方法。请将其与官方包开发文档介绍的 mergeConfigFrom() 区分开来,并在确认对应框架版本的实现后再使用。数字键列表不会被整体替换
递归替换并不是”将数组全部替换为用户的值”的处理。对于数字键,它同样替换相同键的值,并保留用户未指定的键。['slack'],database 仍会保留。另外,即使传入 ['channels' => []],默认列表也不会变为空。对于通知渠道或中间件等整体指定列表才有意义的配置,请特别注意。
如果你的包包含此类配置,请从配置结构入手考虑,例如将列表和可部分覆盖的关联数组拆分到不同的顶层键中,并使用浅层合并。对于已经发布的包,更改合并方式会导致同一个配置文件的行为发生变化,因此请不要将其仅仅视为实现层面的替换。
配置缓存中保存的是合并后的值
当应用实现了CachesConfiguration 且 configurationIsCached() 为 true 时,这两个方法都会跳过合并处理。在常规的 Laravel 应用中,存在配置缓存的启动就属于这种情况。
ConfigCacheCommand 会删除旧的配置缓存,启动一个新的应用并获取整个配置仓库。在这次启动中,提供者的配置会被合并,结果保存到缓存文件中。之后的启动中,LoadConfiguration 会读取这些值。
因此,即使更新包后默认值或合并方式发生了变化,继续使用旧缓存的应用也不会反映这些变化。在使用配置缓存的部署中,请使用更新后的代码重新构建缓存。
php artisan config:clear。不要自行决定缓存的生成位置,请交由 Laravel 的命令进行管理。
发布配置变更前的检查
在包的测试中,不仅要处理未发布的配置,还要将旧版本遗留下来的配置作为输入。- 即使未发布配置,也能获取所需的默认值。
- 对于旧的已发布配置,用户的值优先,且新项按设计被补充。
- 对于嵌套数组、数字键列表和空数组,覆盖约定保持不变。
- 在使用该包的应用中
config:cache能够成功执行,并且在使用缓存的另一次启动中得到相同的配置。
相关页面
包的测试
注册服务提供者,验证配置和服务的行为。
版本兼容性管理
将公开 API 的变更与发布策略和持续维护联系起来。