composer install --no-dev不是可选项而是必加项,因为漏用会导致phpunit等dev包误入线上,引发RCE风险、autoload冗余、OPcache干扰及镜像膨胀;它仅跳过安装但不清理已污染的composer.lock或autoload-dev注册,须配合--optimize-autoloader等参数才能确保生产安全。

composer install --no-dev 为什么不是可选项,而是必加项
漏掉 --no-dev,线上就会多出 phpunit/phpunit、symfony/var-dumper、laravel/pint 这类包——不是“多几个文件”那么简单,而是引入 RCE 风险、autoload 冗余、OPcache 编译干扰和镜像体积膨胀。Composer 不会自动识别“这是开发用的”,它只认 require-dev 字段 + 命令参数。没加 --no-dev,就等于默认全装。
为什么加了 --no-dev 还装了 dev 包?锁文件已污染
--no-dev 只跳过安装动作,不改 composer.lock 结构。如果 lock 文件里已有 "packages-dev" 区块或 packages 数组中含 phpunit 等条目,Composer 会报错中断,或(更危险地)静默跳过校验继续装——取决于 Composer 版本和 lock 文件状态。
- 检查方式:
grep -A5 '"packages-dev"' composer.lock | head -20,输出非空即污染 - 本地执行过
composer update(没加--no-dev)是最大污染源 - Docker 构建时复用旧
vendor/或未清理缓存,也会让--no-dev失效 - CI 脚本里先跑
composer install,再补--no-dev,前者装的包不会被自动卸载
autoload-dev 残留比包本身更致命
--no-dev 控制包是否安装,但不管 autoload-dev 是否注册路径。只要 composer.json 里还存在 "autoload-dev": {"psr-4": {"Tests\": "tests/"}},运行 composer dump-autoload 就会把 Tests 映射写进 vendor/autoload.php。结果就是:class_exists('TestsFooTest') 返回 true,但 new TestsFooTest() 直接报错。
- 部署后必须补一句:
composer dump-autoload --no-dev - 或者删掉
composer.json中整个autoload-dev块,再跑composer dump-autoload - 验证:
grep -n "Tests" vendor/autoload.php应无输出
生产部署命令不能只写 --no-dev
单独加 --no-dev 只解决包安装问题,不解决 autoload 性能、构建卡顿和 classmap 权威性。必须组合使用:
-
--optimize-autoloader:触发完整 classmap 扫描,生成vendor/composer/autoload_classmap.php -
--classmap-authoritative:关闭 fallback 查找,要求 classmap 必须完整(新增类需重装 vendor) -
--no-interaction:防止 CI/Docker 因交互提示卡住 -
--no-scripts:跳过post-install-cmd,避免执行测试或 lint 导致构建失败
最终命令应为:composer install --no-dev --optimize-autoloader --classmap-authoritative --no-interaction --no-scripts。少一个参数,就可能在线上暴露调试入口、拖慢请求启动、或让构建在凌晨三点挂住。


















