require-dev 依赖仅在开发环境安装,生产部署必须用 composer install --no-dev 跳过,否则会因体积膨胀、autoload 冲突或 class not found 导致故障。

require-dev 依赖只在开发环境安装
Composer 默认会同时安装 require 和 require-dev 下的包,但生产部署时通常不该加载 dev 依赖——它们可能引入调试工具、测试框架或未加固的辅助类,带来安全或性能风险。
关键判断依据是部署命令是否带 --no-dev 参数:
- 本地开发:运行
composer install或composer update,require-dev自动生效 - CI/CD 或线上部署:必须显式加
--no-dev,例如composer install --no-dev --optimize-autoloader - 检查当前是否已装 dev 包?看
vendor/目录下是否存在phpunit/phpunit、symfony/var-dumper这类典型 dev 包
require-dev 不影响 autoload 的自动加载逻辑
require-dev 只控制「是否下载安装」,不改变 Composer 的自动加载规则。只要包声明了 autoload 或 autoload-dev,且已被安装,其类就能被正常加载。
但要注意两个常见陷阱:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 某些包(如
phpunit/phpunit)自身没注册autoload-dev,它的测试辅助类(如PHPUnit\Framework\MockObject\MockBuilder)仅在测试运行时通过 PHPUnit 的 bootstrap 加载,不能直接在业务代码中new使用 - 如果你在
autoload-dev里加了"tests/": ["Tests"],这些路径下的类只在 dev 环境可用;但若误把核心工具类也放这里,上线后就会报Class not found - 运行
composer dump-autoload时,默认会合并autoload和autoload-dev,除非加--no-dev参数才跳过后者
require-dev 中的版本约束会影响 require 的解析结果
很多人以为 dev 依赖只是“多装几个包”,其实它会参与整个依赖图的版本求解。比如:
{
"require": {
"monolog/monolog": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0",
"monolog/monolog": "^1.25"
}
}
此时 Composer 会因冲突而报错:monolog/monolog 无法同时满足 ^2.0 和 ^1.25。dev 依赖不是隔离沙箱,它和主依赖共享同一套版本约束引擎。
- 解决方法:优先用兼容版本,例如把 dev 里的
monolog/monolog改成"^2.0 || ^3.0" - 临时绕过:用
composer update --with-all-dependencies强制重算,但不推荐长期依赖这种操作 - 更稳妥的做法:把仅用于测试的 monolog 功能抽到独立测试工具类中,避免对主依赖产生反向约束
如何安全地在生产环境验证 require-dev 是否已剔除
光靠记忆或文档不可靠,最直接的方式是检查实际生成的 autoloader 和 vendor 内容。
- 运行
composer show --dev—— 如果返回空,说明当前 lock 文件/安装结果不含 dev 包(注意:该命令本身需要 dev 包存在才能运行,所以更适合在 CI 脚本里配合--no-dev安装后验证) - 检查
vendor/composer/autoload_classmap.php:搜索phpunit或symfony/var-dumper相关路径,若存在,说明--no-dev没生效 - 在生产容器启动脚本里加一行
ls -la vendor/ | grep -E "(phpunit|faker|behat)" || echo "dev packages absent",作为快速哨兵
真正容易被忽略的是:某些 SaaS 部署平台(如 Laravel Envoyer、Fortrabbit)默认不带 --no-dev,或者你封装的部署脚本里漏写了这个参数,结果上线后多了几十 MB 的测试工具,还可能意外暴露 var-dumper 的调试接口。










