Composer不支持多环境依赖白名单,require-dev包只能全装或全不装;需通过CI脚本按需安装并用环境变量控制,避免破坏lock文件一致性。

Composer 本身没有「多环境依赖白名单」这个概念,也没有 whitelist 字段或机制。所谓“白名单”,实际是开发者对 require-dev 中部分包在特定环境(如 CI 或 staging)中“有条件启用”的误称。真正在用的控制方式只有两个:安装时跳过(--no-dev),或运行时按需加载(靠 autoload-dev + 条件判断)。强行套用“白名单”思路,反而容易导致 autoload 失效、类找不到、CI 行为不一致。
require-dev 里的包怎么有选择地装?
不能“白名单式”挑着装几个 dev 包,只能全装或全不装。Composer 的 install 和 update 命令不支持 --only-dev="phpunit,larastan" 这类语法。所有 require-dev 下的包被视为一个原子组:
-
composer install→ 装require+require-dev -
composer install --no-dev→ 只装require,完全跳过require-dev里的所有包(包括其autoload-dev规则) - 不存在中间态:比如只装 phpunit 但不装 php-cs-fixer —— 这会破坏 lock 文件一致性,且 Composer 会直接报错
想让某些 dev 工具只在 CI 里生效?靠脚本 + 环境变量
CI 脚本里不该依赖 Composer 的“白名单”,而应分两步走:先装基础依赖,再按需 require 额外工具。例如 GitHub Actions 中:
steps:
- run: composer install --no-dev
- run: composer require --dev phpunit/phpunit:^9 --no-interaction
if: ${{ github.event_name == 'pull_request' }}
- run: composer require --dev friendsofphp/php-cs-fixer:^3 --no-interaction
if: ${{ startsWith(github.head_ref, 'release/') }}
注意:composer require --dev 会修改 composer.json 和 composer.lock,所以仅限 CI 临时使用;生产部署脚本必须用 --no-dev,否则 phpunit 可能被意外装上并启用。
autoload-dev 不生效?检查是否用了 --no-dev
autoload-dev 是个常见盲区:它只在 require-dev 的包被安装的前提下才注册命名空间。一旦加了 --no-dev,这些规则就彻底不进 autoloader,代码里若硬引用 \Tests\FooTest 就会直接报 Class not found:
- 现象:本地
composer install能跑通测试,线上却报错Class 'Tests\FooTest' not found - 原因:线上用了
--no-dev,但测试类又被非测试代码(如某个命令行脚本)调用了 - 解法不是“白名单加载 autoload-dev”,而是把测试相关逻辑挪到
require-dev包内部,或加运行时判断:if (class_exists('PHPUnit\Framework\TestCase')) { ... }
真正要警惕的是把环境差异塞进 composer.json 结构里——比如拆出 composer.prod.json 或用脚本动态删 require-dev。这会让 lock 文件失去可重现性,也是线上故障最常埋雷的地方。











