生产环境必须加--no-dev,否则phpunit等dev包会因composer.lock含其记录而被强制安装,导致体积膨胀、启动变慢、autoload冲突或Fatal error;根本原因是install只读lock文件,需先用composer update --no-dev --lock生成洁净lock。

生产环境必须加 --no-dev,否则 phpunit、mockery/mockery、symfony/var-dumper 这类包会照常装进 vendor/,不是“可能出问题”,是必然导致体积膨胀、启动变慢、autoload 冲突,甚至直接 Fatal error。
为什么 composer install --no-dev 有时没用?
根本原因不是命令写错了,而是 composer.lock 文件里本来就存着 require-dev 包的记录。只要 lock 文件含 phpunit/phpunit,哪怕加了 --no-dev,它照样被装进去。
-
composer install只读 lock 文件,不看composer.json里的require-dev是否存在 -
--no-dev的作用是「跳过 lock 中 dev 包的安装」,但前提是 lock 本身不含 dev 包 - 如果你上次运行的是
composer update(没带--no-dev),那 lock 就已经污染了 - 修复方式:CI 构建前先跑
composer update --no-dev --lock,再提交新 lock
require-dev 里的包到底算不算“运行时依赖”?
不能只看它在哪个字段里,得看代码里有没有真用到。一个包放在 require-dev,不代表它不会在线上崩掉。
- 如果
src/Controller.php里写了dump($data),而symfony/var-dumper在require-dev,那线上 debug 模式一开就报Function already defined或直接 500 - 执行
grep -r "symfony/var-dumper" src/,只要命中,它就不是纯开发工具 -
autoload-dev配置只影响自动加载路径,和包是否安装无关;没装包,路径再配也没用 - 真正要清理 autoload 映射,得补一句
composer dump-autoload --no-dev
Docker 和 CI 里怎么写才不翻车?
参数必须显式、不可省略、不能靠环境变量“默认生效”。靠 COMPOSER_DEV_MODE=0 不如直接写死 --no-dev。
- Dockerfile 中应写:
RUN COMPOSER_DEV_MODE=0 composer install --no-dev --optimize-autoloader
- GitHub Actions 缓存 key 必须带语义:
composer-${{ hashFiles('**/composer.lock') }}--no-dev,否则缓存复用会导致 dev 包漏装或误装 - CI 测试阶段要用完整依赖,但必须和部署阶段隔离:测试用
composer install,部署用composer install --no-dev - 禁止在生产服务器上运行
composer update,哪怕加了--with-dependencies—— 这违反不可变部署原则
最常被忽略的一点:composer.lock 是环境敏感的。PHP 版本升级(比如从 8.1 到 8.2)后,某些包的平台约束会失效,lock 文件实际已不适用,但 composer install 不报错,直到运行时报 Class not found 或扩展缺失。











