composer install --no-dev是唯一正解,它硬性跳过require-dev包及其传递依赖、autoload-dev注册和@dev脚本,必须配合--optimize-autoloader和--classmap-authoritative使用,且依赖干净的composer.lock文件。

composer install --no-dev 是唯一正解,不是可选项
生产环境执行 composer install 必须加 --no-dev,否则 phpunit、laravel/pint、roave/security-advisories 这类包会照装不误——它们不参与运行,但会增大镜像体积、引入 autoload 冲突、甚至因扩展缺失直接报错。
这个参数的作用不是“建议跳过”,而是硬性过滤:
- 跳过
require-dev中所有包及其传递依赖(比如phpunit/phpunit带的sebastian/exporter也不会进vendor/) - 忽略
autoload-dev配置,不注册"Tests\": "tests/"这类命名空间 - 跳过
@dev标记的脚本(如post-autoload-dump里调用phpstan的命令)
注意:--optimize-autoloader(或 -o)只优化加载速度,它不会跳过任何包。两者功能不重叠,必须同时用。
为什么加了 --no-dev 还装了 dev 包?锁文件脏了
--no-dev 只在 composer.lock 文件本身不含 require-dev 相关记录时才真正生效。如果 lock 文件是上次用 composer update(没加 --no-dev)生成的,那里面已经存了 dev 包的版本和哈希值,install 就会照单全收。
检查方法:
- 运行
grep -A 5 "require-dev" composer.lock,输出为空才表示干净 - 若发现内容,说明 lock 文件已污染,需先在干净环境执行
composer update --no-dev --lock并提交新 lock - CI 流水线中应把这步作为部署前必检项,不能只靠安装命令“补救”
常见表现:执行 composer install --no-dev 后,vendor/bin/phpcs 依然存在,composer show 列出一堆未声明在 require 中的包。
生产部署推荐参数组合:--no-dev -o --classmap-authoritative
这三个参数配合使用,是当前最稳妥的生产安装方案:
-
--no-dev:过滤依赖源头 -
--optimize-autoloader(-o):生成完整类映射,减少运行时文件查找 -
--classmap-authoritative:告诉 autoloader “所有类都在 classmap 里,别再去文件系统扫描”,这对无autoload-dev的场景尤其关键,能避免意外加载测试类
完整命令示例:
composer install --no-interaction --no-dev --optimize-autoloader --classmap-authoritative
其中 --no-interaction 防止自动化流程卡住,但它不控制依赖范围——少写 --no-dev,哪怕其他参数全齐也没用。
autoload-dev 和 require-dev 不是一回事,别混淆
autoload-dev 是自动加载规则,require-dev 是包声明,两者独立。一个包在 require-dev 里,不代表它的类就一定只用于测试;反之,autoload-dev 里注册了 "Tests\": "tests/",也不代表你装了 phpunit 才生效。
典型陷阱:
- 某处代码写了
new Tests\FooTest()或use Tests\TestCase,即使加了--no-dev,运行时仍会报Class not found -
composer dump-autoload没加--no-dev,生成的vendor/autoload.php仍含测试路径映射,autoload 行为不受install参数影响 -
symfony/var-dumper放在require-dev很安全,但若控制器里用了dump($data),它就成了运行时依赖
真正要清理的,是代码里对 dev-only 类的硬引用,不是只靠命令参数就能兜底的。


















