生产环境必须加--no-dev,否则phpunit等dev包必然进入线上镜像,导致体积膨胀、启动变慢、暴露调试入口甚至fatal error;根本原因在于composer.lock是否含dev包记录,需配合composer update --no-dev --lock、dump-autoload --no-dev及严格CI流程确保生效。

生产环境必须加 --no-dev,否则 phpunit、phpstan、xdebug 会进线上镜像——不是“可能出问题”,是必然体积膨胀、启动变慢、暴露调试入口,甚至因扩展缺失直接 fatal error。
为什么 composer install --no-dev 有时没用?
根本原因在 composer.lock 文件本身是否记录了 require-dev 包。如果 lock 文件里有 phpunit/phpunit 的条目,哪怕你加了 --no-dev,Composer 仍会照单安装。
-
composer install不改 lock,只按 lock 装;composer update才生成/更新 lock - CI 构建生产镜像前,必须先跑
composer update --no-dev --lock,再提交新 lock - 本地开发时若执行过
composer update(无参数),lock 就已混入 dev 包,后续部署就会失效 - 检查方式:
grep -A5 '"packages-dev"' composer.lock | head -20,看是否有非空内容
require-dev 里的包真只在开发用吗?
不一定。关键看它是否被运行时代码直接引用。一旦 src/ 下出现 use PHPUnitFrameworkTestCase 或 dump($data),它就不是“开发专用”,而是运行时依赖。
- 用
grep -r "PHPUnit\|Mockery\|var_dump" src/检查 src/ 是否硬依赖 dev 包 -
autoload-dev只影响自动加载路径映射,和包是否安装无关;没装包,路径再配也没用 - 运行
composer dump-autoload --no-dev才会剔除autoload-dev映射;漏掉这步,vendor/autoload.php仍含测试路径,可能引发 autoload 冲突 - 像
symfony/var-dumper这类包,若控制器在 debug 模式下被线上请求调用,缺了就 500
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 测试阶段必须不加
--no-dev,确保phpunit、phpstan等可用 - 禁止用
composer.prod.json替换主配置——会破坏 lock 一致性,导致“在我机器上能跑”类故障
真正容易被忽略的点:锁文件状态、运行时代码对 dev 包的实际调用、以及 dump-autoload --no-dev 是否执行。这三个环节任一遗漏,都会让 --no-dev 形同虚设。


















