require 是运行时必需依赖,require-dev 仅用于开发测试;误放会导致线上崩溃或冗余安装;判断标准是代码是否直接使用该包类/命名空间。

require 和 require-dev 的核心区别在于:前者是项目运行时必需的依赖,后者仅在开发、测试或构建阶段需要。用错会导致线上环境出问题,或者本地开发无法启动。
什么时候该写进 require
项目上线后,代码实际执行时离不开的包,必须放 require。
- 框架核心(如 laravel/framework、symfony/http-kernel)
- 数据库驱动(如 doctrine/dbal、illuminate/database)
- HTTP 客户端(如 guzzlehttp/guzzle,只要你的业务代码调用了它)
- 模板引擎(如 twig/twig,只要控制器渲染了 twig 模板)
判断标准很简单:删掉这个包,项目启动报错 或 关键功能直接失效,就属于 require。
什么时候该放进 require-dev
只在你写代码、跑测试、生成文档或优化构建时才用得上的工具,全归 require-dev。
- 测试工具(如 phpunit/phpunit、pestphp/pest)
- 代码质量工具(如 phpstan/phpstan、friendsofphp/php-cs-fixer)
- 本地开发辅助(如 barryvdh/laravel-debugbar、laravel/sail)
- 文档生成器(如 phpdocumentor/phpdocumentor)
部署到生产环境时,通常会加 --no-dev 参数(例如 composer install --no-dev),这些包就不会被安装,既节省空间又减少安全面。
常见误用场景和后果
把本该在 require 的包错放 require-dev,最典型的就是“本地能跑,上线就炸”:
- 把 monolog/monolog 放 require-dev → 日志功能在生产环境彻底消失,错误无声无息
- 把 ramsey/uuid 放 require-dev → 生成 UUID 的逻辑抛 ClassNotFound 异常
- 反过来,把 phpunit/phpunit 放 require → 生产环境多装几十 MB 无用代码,还可能引入额外漏洞
Composer 不会自动校验语义合理性,一切靠开发者判断。建议每次加新包前,先问一句:这个类/命名空间,我的 app.php 或 index.php 会直接 new 或 use 吗?
协作与 CI/CD 中的注意事项
团队项目里,require-dev 不只是“我本地用”,它也影响 CI 流程:
- GitHub Actions 或 GitLab CI 的测试 job 必须运行
composer install(不带 --no-dev),否则 PHPUnit 找不到 - 但部署 job 必须用
composer install --no-dev --optimize-autoloader - 如果某个 dev 包其实被生产代码间接依赖(比如通过 trait 或接口实现),说明设计已有隐患,应重构或移入 require
可以定期执行 composer show --dev 检查 require-dev 列表,确认没有“混进来”的 runtime 依赖。
基本上就这些。不复杂但容易忽略,关键是分清“运行时”和“开发时”的边界。










