require-dev是安装标记而非环境开关,生产环境必须显式执行composer install --no-dev才能跳过安装,否则phpunit等包必入vendor并可能引发class not found错误。

require-dev 不是“开发环境开关”,而是明确告诉 Composer:这个包只在你手动执行命令时才需要,生产环境必须跳过它——但跳过与否,全看你部署时有没有加 --no-dev。
require-dev 本质是安装策略,不是运行时隔离
它只影响 composer install 和 composer update 是否把包拉进 vendor/,不随 APP_ENV 或服务器类型自动生效。哪怕你在生产服务器上跑 composer install(没加 --no-dev),phpunit/phpunit 一样会装进去,镜像体积多出几十 MB,还可能因 xdebug、var-dumper 等引发启动失败。
- 本地开发默认装
require-dev,方便跑测试、格式化、静态分析 - CI 流水线通常也装(比如用
./vendor/bin/phpunit),所以一般不加--no-dev - 生产部署必须显式写
composer install --no-dev --optimize-autoloader --no-interaction,否则composer.lock里出现phpunit/phpunit就说明上次构建漏了
哪些包该放 require-dev?看它是否参与运行时逻辑
判断标准只有一个:删掉这个包,项目还能不能正常响应 HTTP 请求、处理业务逻辑?能——放 require-dev;不能——必须进 require。
- 纯工具类:如
phpunit/phpunit、phpstan/phpstan、friendsofphp/php-cs-fixer,只在命令行调用,业务代码从不use它们的类 - 调试辅助:如
symfony/var-dumper,若只在dd()中用,线上禁用即可;但若src/里直接new SymfonyComponentVarDumperClonerVarCloner(),就得挪到require - 本地开发容器:如
laravel/sail,只用于./artisan sail:up,绝不进生产镜像 - 误放高危区:
doctrine/doctrine-bundle看似“开发用”,实则启动时就被 Symfony 容器注册,必须放require
require-dev 写错位置的典型现象和查法
最常见错误不是“装不上”,而是“本地能跑,线上炸了”——因为类被硬编码进了业务逻辑。
- 现象:
Class 'FakerGenerator' not found,但composer show --dev显示fakerphp/faker确实在require-dev - 查法:
grep -r "new Faker\" src/ app/或grep -r "use Faker\" src/ app/,定位到实体构造函数或服务中误用了假数据生成器 - 修复路径二选一:要么把
fakerphp/faker移进require(仅当它真是运行时依赖),要么把 fake 逻辑抽到tests/下的 factory 类里,用接口隔离 - 另一个坑:
autoload-dev声明的路径(如"Tests\": "tests/")不会进生产 autoloader,但如果你在src/里写了new TestsFooTest(),哪怕没装包,照样报错
Dockerfile 和 CI 脚本里最容易漏掉 --no-dev
很多线上故障不是代码问题,而是部署脚本没写全参数。
- Dockerfile 错误写法:
RUN composer install→ 危险,phpunit、phpstan全部打进镜像 - 正确写法:
RUN composer install --no-dev --optimize-autoloader --no-interaction - CI 脚本常见漏配:
composer install后没加--no-dev,导致测试通过但线上启动失败(比如因xdebug扩展缺失) - 注意:
composer dump-autoload --optimize默认不处理autoload-dev,如果 dev-only 类需要加载(比如某些测试基类),得加--classmap-authoritative或确保路径在autoload而非autoload-dev里
真正决定一个包是否出现在生产环境的,从来不是它写在哪一行 JSON 里,而是你部署时敲下的那条命令里有没有 --no-dev——这个参数漏掉一次,就可能让测试工具混进线上进程,而 class not found 的错误往往要等到用户访问首页才暴露出来。











