require-dev专用于声明开发测试工具(如phpunit、mockery、phpstan),不参与生产环境构建;执行composer install --no-dev可彻底跳过安装,避免线上体积膨胀、安全风险及autoload污染。

composer.json 里用 require-dev 声明测试依赖
测试工具(如 phpunit、mockery、phpstan)不该出现在生产环境,必须和主依赖隔离。直接写进 require-dev 字段,而不是 require:
{
"require": {
"monolog/monolog": "^2.10"
},
"require-dev": {
"phpunit/phpunit": "^10.5",
"mockery/mockery": "^1.6"
}
}
这样做的关键效果是:composer install 默认跳过 require-dev;只有显式加 --dev(或不加任何 flag)才会装它们;而 composer install --no-dev 则彻底排除——这正是部署到线上时该用的命令。
composer install 和 composer update 对 require-dev 的行为差异
很多人误以为 update 总是更新全部依赖,其实它默认也只处理 require-dev ——除非你加了 --no-dev。但要注意:
-
composer install:读composer.lock,按锁文件还原依赖;是否装dev依赖取决于有没有--no-dev -
composer update:重新解析composer.json,生成新composer.lock;它会同时更新require和require-dev区块,哪怕你只改了其中一个 - 想只更新测试依赖?得明确指定包名:
composer update phpunit/phpunit mockery/mockery
CI/CD 部署时漏掉 --no-dev 会导致什么
没加 --no-dev 的 composer install 会在生产环境装上 PHPUnit 等工具,带来三个实际风险:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 体积膨胀:vendor 目录多出几 MB,影响部署速度和镜像大小
- 安全暴露:
phpunit若存在未修复的反序列化漏洞,可能被恶意利用 - 自动加载污染:Composer 生成的 autoloader 会包含测试类路径,增加
class_exists()或反射判断的意外命中概率
所以 CI 脚本里务必写死:composer install --no-dev --optimize-autoloader
想让某些 dev 依赖在生产也能用,但又不想污染 require
极少数场景(比如 Laravel 的 tinker 在预发环境调试用),你确实需要部分 dev 工具“有条件可用”。别挪到 require,而是用 config.platform 或脚本钩子绕过:
- 临时启用:部署时加
--dev+ 条件判断入口文件(例如检查$_ENV['APP_ENV'] === 'staging'再 require 测试类) - 平台模拟:在
composer.json里设"config": { "platform": { "php": "8.2.0" } },避免因本地 PHP 版本高导致某些 dev 包无法安装,但这不改变依赖范围 - 真正干净的做法:把这类“准生产工具”单独拆成一个私有包,用
repositories引入,并在不同环境用不同composer.json变体(通过 Makefile 或部署脚本切换)
多数人卡在“测试依赖该放哪”和“部署时到底删没删”,其实核心就两条:dev 依赖永远进 require-dev,上线命令永远带 --no-dev。其他都是边界情况,先确保主干不出错。

















