require-dev 是明确排除在生产环境之外的依赖,非“可选”;装错位置或部署漏 --no-dev 会导致线上 Class not found 或启动变慢。

require-dev 不是“可选依赖”,而是明确排除在生产环境之外的依赖;装错位置或部署漏掉 --no-dev,线上直接报 Class not found 或启动变慢。
require-dev 里该放什么,不该放什么
判断标准很简单:这个包的类或函数,是否会在 APP_ENV=production 下被 new、use、class_exists() 或 include 调用?
- 该放:
phpunit/phpunit、phpstan/phpstan、friendsofphp/php-cs-fixer、mockery/mockery—— 它们只在测试、CI、本地调试时执行,不参与运行时逻辑 - 不该放:
symfony/var-dumper(如果dump()出现在AppServiceProvider或中间件里)、barryvdh/laravel-debugbar(如果配置没套if (app()->environment('local')))、doctrine/doctrine-bundle(Bundle 启动即注册,必须进require) - 特别注意:
laravel/sail是纯 CLI 工具,只用于本地容器编排,必须进require-dev;但laravel/framework永远只能在require
用 composer require --dev 添加依赖时的坑
命令本身简单,但几个细节不注意就会埋雷:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
-
composer require --dev phpunit/phpunit:^10是正确写法;composer require phpunit/phpunit(漏--dev)会把它塞进require,上线后 autoload 尝试加载失败 -
--dev不能缩写成-d,旧版 Composer 会静默忽略,结果还是写进require - 如果包已存在于
require-dev,再跑require --dev不会升级版本 —— 必须显式加版本号,或改用composer update phpunit/phpunit - 想只改
composer.json不安装?加--no-install:composer require --dev php-cs-fixer --no-install
部署时 --no-dev 不是可选项,是必选项
composer install 默认装 require 和 require-dev 全部内容;生产环境跳过 dev 依赖,唯一可靠方式就是加 --no-dev。
- CI/CD 脚本里漏掉它,会导致
phpunit、debugbar打进生产镜像 —— 体积多出 40–60MB,还可能暴露/_debugbar这类入口 -
APP_ENV=production对 Composer 安装行为完全无影响;它只控制 Laravel 自身配置,不干预依赖安装范围 -
composer install --no-dev不仅跳过安装,还会跳过生成vendor/autoload_dev.php,所以class_exists('PhpCsFixer\Fixer\...')在线上会返回false(不触发 autoload),但前提是你的代码没在config/或bootstrap/里提前require它
require-dev 包在生产环境意外执行的典型场景
最常踩的坑不是装错了,而是“以为没装,其实被 autoload 了”:
- 在
config/app.php里写了'providers' => [Foo\Bar\ServiceProvider::class],而Foo\Bar只在require-dev里 —— 线上容器启动就炸 - 用
class_exists('PHPUnit\Framework\TestCase')做条件判断,而不是interface_exists()或function_exists()—— 这个调用本身就会触发 autoload,线上找不到类就 fatal error -
autoload-dev配置写错路径,比如把tests/映射漏了,导致测试辅助类无法加载,phpunit报错,但错误信息藏在 CI 日志深处,难定位 - 某些包(如
symfony/var-dumper)同时被 dev 工具和调试函数依赖,若只放require-dev,又在production环境启用了APP_DEBUG=true,dump()就会挂掉
真正难排查的从来不是“有没有装”,而是“为什么它被加载了”。require-dev 控制的是安装,autoload 行为由 autoload 和 autoload-dev 两块配置共同决定 —— 这两者写错,比放错区更隐蔽。

















