require 中的包是项目运行时必需的,删掉会导致 http 请求或业务逻辑失败;require-dev 中的包仅用于开发、测试、构建等阶段,上线后不参与运行,执行 composer install –no-dev 时会被跳过。

require-dev 和 require 的根本区别在哪
区别不在“要不要装”,而在“什么时候装、给谁用”。require 里的包是项目运行时必需的,比如 monolog/monolog 用于日志;require-dev 里的包只在开发阶段需要,比如 phpunit/phpunit 或 friendsofphp/php-cs-fixer,上线后完全不参与运行。
关键判断标准:删掉这个包,项目还能不能正常响应 HTTP 请求、处理业务逻辑?能——就该放 require-dev;不能——必须进 require。
- 执行
composer install --no-dev时,require-dev内容会被跳过,生成的vendor/更轻量 -
composer update默认同时更新require和require-dev,但 CI 流水线常加--no-dev防止测试工具污染生产环境 - 如果把
phpunit放进require,部署时会多下几十 MB 无用代码,还可能引入非预期的 autoloader 冲突
哪些工具必须塞进 require-dev
不是“开发者用的”就自动归类,而是看它是否参与构建、测试、检查流程,且不暴露给运行时代码。常见误放点:把 doctrine/orm 的命令行工具当成 dev 工具,其实它是运行时 ORM 的一部分,必须放 require。
-
phpunit/phpunit、pestphp/pest:纯测试执行器,不被业务代码use,只在./vendor/bin/phpunit调用 -
phpstan/phpstan、psalm/phar:静态分析工具,仅本地或 CI 中执行,不 autoload 到应用中 -
symfony/var-dumper:虽然常被dump()调用,但它本身是调试辅助,线上应禁用;若项目依赖其Cloner类做序列化,就得挪到require -
laravel/pint、php-cs-fixer:代码格式化工具,只在 pre-commit 或 CI 中触发,和运行时零耦合
require-dev 里混进 runtime 依赖的典型错误现象
最常踩的坑:某个包在 require-dev 里,但你的 src/ 文件直接 use 它的类,甚至调用它的方法——上线跑 composer install --no-dev 后,直接 class not found。
错误示例:require-dev 里有 fakerphp/faker,但你在 Entity 构造函数里写了 new FakerGenerator()。这等于把假数据生成逻辑写进了业务模型,违反了依赖边界。
- 现象:本地
php artisan test正常,但部署后访问首页报Class 'FakerGenerator' not found - 查法:用
composer show --dev确认包是否真在require-dev;再 grep 全局搜索该类名是否出现在src/或app/下 - 修复:要么把包移到
require(仅当它确属运行时依赖),要么重构代码——把 fake 数据逻辑抽到测试专用 factory 类里,业务代码不碰它 - 注意:
autoload-dev字段只影响自动加载路径,不改变依赖安装时机;它不能“救活”一个本该在require的包
CI/CD 中 require-dev 的实际开关策略
CI 脚本里别无脑写 composer install。不同阶段要明确意图:单元测试需完整 require-dev,打包镜像则必须剔除。
- github Actions 示例:
run: composer install --no-interaction --prefer-dist --no-suggest→ 缺少
--no-dev,导致测试工具打进生产镜像 - 推荐分两步:
测试阶段:用composer install(默认含 dev)
构建阶段:用composer install --no-dev --optimize-autoloader - 如果你用
composer.lock提交,要注意:即使某包只在require-dev,它仍会记录在 lock 文件里;但--no-dev会跳过安装,lock 中的版本不会生效 - 小陷阱:
composer dump-autoload --optimize在 dev 模式下会包含autoload-dev的路径,但线上运行时若没装 dev 包,这些路径就是无效的——所以线上务必用--no-dev+--optimize-autoloader一起跑
真正难的是厘清“这个类到底算不算运行时依赖”。很多人靠直觉放错位置,结果上线才暴露。与其反复修,不如每次加包前问一句:它会不会被 index.php 或控制器直接调用?