require-dev 与 require 的根本区别在于是否参与生产环境构建:require 是运行时必需依赖,require-dev 仅用于开发测试流程,生产环境通过 --no-dev 完全不安装。

require-dev 里写的包,只在本地开发和测试时安装,线上部署(比如生产环境 composer install --no-dev)时完全不装——它不是“可选依赖”,而是“非生产依赖”。
require-dev 和 require 的根本区别在哪
核心就一条:是否参与生产环境构建。
-
require是运行时必需的,比如"monolog/monolog": "^2.0",没它项目直接报错 -
require-dev是开发链路必需的,比如"phpunit/phpunit": "^9.5",测试跑完就用不上了 - 执行
composer install默认两者都装;加--no-dev就跳过require-dev全部内容 -
composer update默认也只更新require-dev里的包,除非显式加上--with-dependencies
哪些工具该放 require-dev 而不是 require
判断标准:这个包是否在 PHP 运行时被 include 或 new 调用?如果不是,大概率属于 dev。
- 测试类库:
"phpunit/phpunit"、"pestphp/pest" - 代码质量工具:
"phpstan/phpstan"、"friendsofphp/php-cs-fixer" - 本地开发辅助:
"symfony/cli"、"laravel/sail"(注意:Sail 只用于本地容器启动,不进生产) - Mock/stub 工具:
"mockery/mockery"、"prophecy/prophecy" - 别误放:像
"doctrine/doctrine-bundle"这种运行时加载的,哪怕只在 dev 环境用,也得放require(Bundle 启动时就被容器注册)
require-dev 导致的常见问题
最典型的是“本地能跑,线上炸了”,往往因为混淆了依赖层级或 autoloading 行为。
- 写了
"foo/bar": "dev-main"在require-dev,但测试代码里用了new Foo\Bar\Client(),而主项目又没在autoload-dev里声明对应 namespace → 报Class not found - CI 流水线没加
--no-dev,导致把phpunit打进生产镜像,白白增大体积、引入安全风险 - 用
composer dump-autoload --optimize时,默认不处理autoload-dev配置,如果 dev-only 类需要自动加载,得加--classmap-authoritative或手动确保路径正确 - 某些包(如
symfony/var-dumper)既被 dev 工具依赖,又被dump()这类调试函数间接调用——若只放require-dev,线上开 debug 模式会出 fatal error
真正容易被忽略的点是:require-dev 不影响自动加载规则本身,只控制安装行为。autoloading 是否生效,取决于 autoload 和 autoload-dev 两块配置是否覆盖到对应路径——这两者写错,比放错区更难排查。










