require-dev 应仅包含开发时使用、生产环境完全不需要的包,如 PHPUnit、PHP-CS-Fixer、PHPStan 等;误放运行时依赖会导致线上报错,误将 dev 包放入 require 会浪费资源并引入风险。

require-dev 里写什么包?看它是不是「只在开发时用」
不是所有工具都该进 require-dev。核心判断标准就一条:这个包在生产环境(比如线上服务器)里运行项目代码时,是否完全不需要?如果答案是「是」,那它就该放这儿;如果部署后还要靠它启动服务、解析配置或处理请求,就得进 require。
常见该进 require-dev 的类型:
-
phpunit/phpunit—— 测试跑完就退出,上线不跑测试 -
friendsofphp/php-cs-fixer—— 代码格式化只在本地或 CI 阶段执行 -
phpstan/phpstan或psalm/phar—— 静态分析纯属开发/检查环节 -
laravel/pint、infection/infection—— 同理,属于质量门禁工具 -
symfony/var-dumper—— 如果只用于dump()调试,且你确认线上不会调用(或已通过环境变量禁用),可放 dev;但若框架底层依赖它做调试支持(如 Laravel 的 Telescope),就得看实际加载逻辑,不能盲目塞进来
require-dev 和 require 混着写会怎样?
Composer 不阻止你把 phpunit 放进 require,但它会在生产部署时也装上——浪费磁盘、延长部署时间、增加攻击面。更隐蔽的问题是:某些 dev 包可能依赖高版本 PHP 扩展(比如 phpdbg),而你的生产环境没开,导致 composer install --no-dev 看似成功,但 composer install 却报错,排查时容易绕弯。
反过来,如果误把 monolog/monolog 这类运行时日志库放进 require-dev,线上一启动就抛 Class not found,因为 --no-dev 模式下它根本没装。
所以关键动作是:
- 上线前务必用
composer install --no-dev模拟真实环境安装 - CI/CD 流水线里显式加
--no-dev参数,别依赖默认行为 - 用
composer show --dev快速核对当前已声明的 dev 包列表
如何安全地添加一个包到 require-dev?
别直接改 composer.json 手动写,容易格式错、引号漏、逗号多。用命令最稳:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
composer require --dev phpunit/phpunit:^10
这条命令会自动做三件事:下载包、写入 require-dev 字段、更新 composer.lock。注意几个细节:
-
--dev不能缩写成-d,必须完整写 - 如果项目已存在
require-dev但为空对象("require-dev": {}),命令能正确合并;但如果字段压根不存在,旧版 Composer 可能报错,建议先确保字段存在 - 想指定版本约束但又不确定兼容性?先加
--dry-run看预览:composer require --dev --dry-run phpunit/phpunit - 删包也一样:
composer remove --dev phpunit/phpunit,别手删 JSON
require-dev 会影响 autoload 吗?
不影响自动加载逻辑本身,但影响「哪些类会被生成到 autoload map 里」。Composer 默认把 require-dev 中的包也纳入 autoloading(只要它们声明了 autoload 配置),也就是说,你在本地 new PhpCsFixer\Finder() 是能成功的——即使它只在 dev 里。
但这不意味着安全。问题出在「条件加载」上:比如某个包只在 dev 环境下注册服务提供者(Laravel 的 app()->register()),但它的类文件仍被 autoload 加载器扫到。一旦你在线上误触发某段调试代码(比如通过 URL 参数调用了未屏蔽的 dump 工具),就可能因类存在但功能不可用而崩溃。
真正保险的做法是:
- 不在业务代码里直接 new dev 包的类(哪怕只是临时调试)
- 用
class_exists()或interface_exists()做运行时检查再调用 - 把调试逻辑包进
if (app()->environment('local')) { ... }这类环境守卫里
dev 依赖不是「看不见的」,它只是「不该被生产环境执行到」——这个边界得靠代码意识来守,不能只靠 Composer 配置。










