生产环境必须用 composer install --no-dev --optimize-autoloader,否则 phpunit、phpstan 等 dev 包必然混入线上镜像,导致体积膨胀 30%~70%、启动变慢、暴露调试入口、内存溢出甚至 fatal error;其生效前提为 composer.lock 已由 composer update --no-dev --lock 生成,并需补 composer dump-autoload --no-dev 清理 autoload-dev 映射。

生产环境必须加 --no-dev,否则 phpunit、phpstan、debugbar 会直接打进线上镜像——不是“可能出问题”,是必然体积膨胀、启动变慢、暴露调试入口,甚至因扩展缺失直接 fatal error。
require-dev 不是“开发时才装”,而是“运行时不依赖”
很多人误以为把包放进 require-dev 就自动实现环境隔离,其实它只影响安装行为,不改变代码逻辑。一旦你在 src/ 里写了 new TestsFooTest() 或调用了 dump($data)(来自 symfony/var-dumper),而该包又在 require-dev 里,那线上就会报 Class not found 或 Call to undefined function。
- 检查每个
require-dev包是否被src/直接引用:grep -r "use.*Test|new.*Test|dump(" src/ - 真正只用于开发的包:如
phpunit/phpunit、php-cs-fixer、larastan—— 它们只在 CLI 下跑,不参与 HTTP 请求生命周期 - 伪 dev 包(实为运行时依赖):如
symfony/var-dumper、barryvdh/laravel-debugbar,需确认是否在 debug 模式下被控制器或中间件调用
composer install --no-dev 为什么有时没用?
根本原因不是命令写错了,而是 composer.lock 文件本身记录了 require-dev 的包。因为 composer install 只照 lock 文件装,不看 composer.json 里的字段。如果你上次执行的是 composer update(而非 composer update --no-dev),lock 文件就含 dev 包记录,此时加 --no-dev 也无效。
- CI 构建前必须先运行:
composer update --no-dev --lock,再提交更新后的composer.lock - 部署阶段永远只用
composer install --no-dev --optimize-autoloader,禁用composer update - Dockerfile 中应写死:
RUN COMPOSER_DEV_MODE=0 composer install --no-dev --optimize-autoloader
autoload-dev 路径不会自动消失,要手动清理
--no-dev 只跳过安装,不影响 vendor/autoload.php 的生成逻辑。如果某个包在 require-dev 里定义了 autoload-dev(比如把 tests/ 映射进 PSR-4),那即使没装这个包,composer dump-autoload 默认仍会把它的 autoload 规则写进 autoloader —— 导致线上 autoloader 多余加载路径,甚至引发命名冲突。
- 生产环境构建后,务必补上:
composer dump-autoload --no-dev - 该命令会剔除所有
autoload-dev块定义的映射,只保留autoload部分 - 若漏掉这步,
vendor/autoload.php里仍可能看到Tests\命名空间注册,但对应类根本不存在
别搞多份 composer.json,platform 配置才是关键
有人想用 composer.prod.json + 脚本替换主文件,这是反模式。Composer 不支持切换配置文件,强行替换会破坏 composer.lock 与 composer.json 的一致性,导致本地 install 和线上行为不一致。
- 真正可控的环境适配点是
config.platform:比如线上 PHP 是 8.1,就在composer.json里写"php": "8.1.0"(注意必须是精确版本号,不能写^8.1) -
COMPOSER_DEV_MODE=0确实等效于--no-dev,但优先级低于显式参数,且不如后者直白可控 - CI 缓存 key 必须带语义,例如:
composer-${{ hashFiles('**/composer.lock') }}--no-dev,否则缓存复用会混装 dev 包
最容易被忽略的一点:composer install --no-dev 成功不代表线上安全。你还得确认 autoload-dev 被清掉、dump-autoload --no-dev 执行到位、所有 require-dev 包没被 src/ 代码硬依赖——三者缺一不可。


















