生产环境必须加 --no-dev,否则会安装PHPUnit等测试调试包;它跳过require-dev及其传递依赖、autoload-dev命名空间和@dev钩子;若composer.lock含dev包,需先用composer update --no-dev --lock生成合规lock文件。

生产环境必须加 --no-dev,否则会装 PHPUnit、PHPStan、xdebug 等测试和调试包——这不是可选项,是部署底线。
为什么 composer install 默认会装测试依赖?
Composer 不识别“生产环境”这个概念。它只认 composer.json 里写的什么就装什么:require 和 require-dev 全部照单全收。你服务器上跑着 Laravel,但没加 --no-dev,barryvdh/laravel-debugbar 就真会被装进去,还可能因 PHP 版本不兼容直接报错。
- 常见错误现象:
vendor/体积暴涨 30–50MB,部署超时;线上突然报Class 'PHPUnitFrameworkTestCase' not found(说明代码误引用了 dev 类) - 根本原因:本地开发用
composer install没问题,但线上脚本漏掉参数,或 CI 流程里用了封装命令(如make deploy)却没透传--no-dev - 不是 Composer “智能判断”,而是你没告诉它“别装 dev 的”——它就默认全装
composer install --no-dev 到底跳过哪些东西?
它不只是删掉 require-dev 列表里的包,而是一整套连锁过滤:
- 跳过
require-dev中所有包及其传递依赖(比如你装了phpunit/phpunit,它带的sebastian/exporter也不会进 vendor) - 跳过
autoload-dev配置——那些测试用的 PSR-4 命名空间(如"Tests\": "tests/")不会注册进 autoloader,new TestsFooTest()必然报错 - 跳过
scripts中标记为@dev的钩子,比如post-autoload-dump里调了phpstan,这条命令就静默跳过,不会中断安装
为什么有时候加了 --no-dev 还是装了 dev 包?
因为 composer.lock 文件本身“记错了”。--no-dev 只在 lock 文件已按生产规则生成的前提下才生效。
- 常见错误现象:执行
composer install --no-dev后,friendsofphp/php-cs-fixer依然出现在vendor/里 - 真正原因:
composer.lock是上次用composer update(没加--no-dev)生成的,里面已经存了 dev 包的版本记录;install只是照 lock 装,--no-dev此时被忽略 - 正确做法:CI 首次部署前,先确保 lock 文件合规——在干净环境运行
composer update --no-dev --lock,提交新 lock;后续部署再用composer install --no-dev --optimize-autoloader
Docker 构建和 CI 脚本里怎么写才靠谱?
别信环境变量(如 COMPOSER_DEV_MODE=0),也别写条件判断。最稳的方式是把参数写死在命令里。
- 推荐写法:
composer install --no-dev --optimize-autoloader --prefer-dist -
--optimize-autoloader让 autoloader 生成 classmap,减少文件查找开销;--prefer-dist强制走压缩包安装,比源码快 - Dockerfile 示例:
RUN composer install --no-dev --optimize-autoloader --prefer-dist --no-scripts
(加--no-scripts防止本地注册的post-install-cmd在镜像里意外执行)
最容易被忽略的不是命令怎么写,而是 composer.lock 的生成源头——它不像代码改了就能立刻生效,而是由某次 update 命令决定内容。只要 lock 里存着 dev 包,--no-dev 就只是个摆设。










