Composer 的 config 不支持合并,项目级 config 会整块替换全局同名键;repositories 可按顺序分流;多环境配置应通过环境变量、占位符或私有包实现,而非 JSON 合并。

Composer 不支持配置合并,所谓“局部覆盖”只是错觉;项目级 config 字段一旦出现任何同名键,就会整块替换全局值,不是补充,也不是优先级叠加。
为什么 composer.json 里的 config 一写就丢掉全局设置
Composer 的配置模型是“全有或全无”:项目根目录的 composer.json 中只要定义了 "config" 对象,它内部的每个键(如 bin-dir、cache-dir、process-timeout)都会直接覆盖全局同名项。全局配置里设了 "process-timeout": 600,项目里只写了 "notify-on-install": false,那 process-timeout 就彻底消失,不会保留为 600。
常见现象包括:
-
composer dump-autoload后vendor/bin没脚本 → 很可能是bin-dir被项目config错误覆盖,导致路径指向无效位置 - CI 中
process-timeout不生效 → 全局设了 600,但项目composer.json里没删掉默认的"process-timeout": 300,结果 300 覆盖了全局 - 改了全局
cache-dir,某些项目仍往~/.composer/cache写 → 因为那些项目显式写了"cache-dir",直接屏蔽了全局值
repositories 是唯一能靠顺序“分流”的配置项
和 config 不同,repositories 数组支持按顺序查找,这才是实现多环境包源控制的可靠方式。Composer 从上到下逐个尝试匹配包名,第一个返回有效元数据的仓库就中止搜索。
实操要点:
- 私有源必须放在
repositories数组最前面,否则会被 packagist 镜像“截胡” - 禁用官方源要显式加
{"packagist.org": false},且必须是数组里的一个独立对象,不能嵌在其他仓库定义里 -
{"packagist.org": false}必须放在所有私有源之后、数组末尾;顺序反了会导致前面的私有源被跳过 - 禁用后若还需公共包,得手动加回官方源:
{"type": "composer", "url": "https://packagist.org"},且必须放在{"packagist.org": false}之后
验证当前生效源:运行 composer config repositories,确认输出和你写的完全一致,没有被全局配置意外覆盖。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
真正可用的多环境方案只有三类
Composer 原生不支持 composer.prod.json 这类多环境文件,也不应试图用脚本合并多个 JSON——这会导致 autoload 丢失、require 冲突、scripts 静默清空等问题。
可行路径只有:
-
COMPOSER_AUTH环境变量:可完全替代磁盘上的auth.json,接受 JSON 字符串,适合 CI 注入私仓凭据,例如:export COMPOSER_AUTH='{"http-basic":{"repo.example.com":{"username":"$USER","password":"$TOKEN"}}}' -
${VAR_NAME}占位符:仅限字符串字段(如fxp-asset.repo、process-timeout),布尔或数字字段(如"notify-on-install": "${CI}")会解析失败;fallback 必须由 shell 层展开(如${COMPOSER_TIMEOUT:-300}),Composer 自身不处理 - 外部脚本动态生成:用 PHP 或 Shell 读取
common.json和prod.json,用array_merge_recursive合并后写入临时composer.json,再执行composer install
想复用配置?别拼 JSON,用私有包
如果你在多个项目里重复写相同的 autoload 规则、scripts 或 config,正确做法是抽成一个独立的私有包,比如 myorg/composer-config-base,其 composer.json 中定义 "autoload": {"psr-4": {"App\": "src/"}},主项目通过 "repositories" 引入并 require 它(类型设为 "metapackage")。
这样做的好处是:
- 避免
autoload被覆盖或丢失 - 不依赖文件合并逻辑,不受 Composer 版本升级影响
- 版本可控,更新时只需 bump 包版本,无需同步修改所有项目
composer.json
最容易被忽略的一点:所有这些“环境差异化”操作,最终都必须收敛为一个有效的、语法合法的 composer.json 文件;Composer 本身只认这个,不多看一眼别的。

















