生产环境必须用 composer install --no-dev,否则 phpunit 等 dev 包必进 vendor,导致体积膨胀、autoload 冲突、类加载失败甚至 fatal error;该参数仅在 composer.lock 干净(不含 dev 记录)时才真正生效,否则报错或静默失效。

生产环境不加 --no-dev,就等于把 phpunit、symfony/var-dumper、mockery/mockery 全塞进线上 vendor 目录——不是“可能出问题”,是必然体积膨胀、autoload 冲突、类加载失败,甚至因扩展缺失直接 Fatal error。
composer install 默认到底装哪些包?
它只读 composer.lock 里记录的包,照单全收。而 lock 文件里有没有 require-dev 的条目,取决于你上次运行的是 composer update 还是 composer update --no-dev --lock。
-
composer install永远不改 lock,只按它装;composer update才会重写 lock - 如果 lock 文件里存了
phpunit/phpunit的版本记录,那哪怕加了--no-dev,Composer 也会跳过安装——但它仍会尝试加载 autoload-dev 路径,可能引发静默 autoload 冲突 - CI 构建前必须先跑
composer update --no-dev --lock,提交新 lock;否则部署时--no-dev形同虚设
为什么 Docker 和 CI 里总翻车?
高频原因是参数没写死、缓存 key 没区分 dev 模式、或误信环境变量。
- Dockerfile 必须显式写:
RUN composer install --no-dev --optimize-autoloader --classmap-authoritative,不能只写composer install再指望基础镜像设了变量 - GitHub Actions / GitLab CI 缓存 key 必须包含 dev 状态,例如:
composer-${{ hashFiles('**/composer.lock') }}-${{ env.COMPOSER_FLAGS || '--no-dev' }} -
COMPOSER_DEV_MODE=0在 Composer 2.9.6+ 已被移除,完全无效;别在ENV里写这个
怎么验证 dev 包真没进生产环境?
别只看配置,要查实际文件和运行时行为。
- 进容器检查:
ls vendor/ | grep phpunit—— 输出非空说明--no-dev没生效 - 检查 autoloader:
grep -n "PHPUnit" vendor/composer/autoload_classmap.php—— 有匹配说明 dev 类仍参与 classmap 构建 - 运行时验证:
php -r "var_dump(class_exists('PHPUnit\Framework\TestCase'));"—— 生产环境必须返回bool(false) - 如果报
Class 'TestsFooTest' not found,但你确定加了--no-dev,大概率是autoload-dev配了 PSR-4 映射,却没配composer dump-autoload --no-dev清除
最常被忽略的一点:一个包是否属于纯 dev 依赖,不取决于它在 require-dev 里,而取决于它是否被 src/ 或 app/ 下任何 PHP 文件直接 use 或 new。哪怕只在 debug 模式下调用 dump(),它就是运行时依赖——--no-dev 删包,删不掉代码里的调用。











