require 中的包参与运行时,缺则报错;require-dev 中的包仅用于开发命令,不参与线上请求链路。

require 和 require-dev 的本质区别不是“环境”,而是“是否参与运行时”
一句话结论:require 里的包,PHP 运行时会 new、use、调用——缺了就直接报 Class not found 或 Fatal error;require-dev 里的包,只在你敲命令时起作用,比如跑 ./vendor/bin/phpunit、执行 phpstan analyse,或者本地 php bin/console cache:clear 里调用了调试类——但这些类绝不会被线上请求链路加载。
常见错误现象:
- 把
symfony/var-dumper放进require-dev,结果线上开了 debug 模式,dump()调用失败,500 报错 - 测试代码里写了
new Mockery\MockInterface(),但没在autoload-dev里声明 mock 目录,CI 报Class not found -
composer require --dev phpunit/phpunit少打一个空格写成composer require-dev phpunit/phpunit,结果误装进require,生产镜像白胖 20MB
部署时必须显式加 --no-dev,否则等于没分
Composer 不看 APP_ENV=prod,也不读服务器 hostname,它只认命令参数。不加 --no-dev,require-dev 里的包照装不误——线上多出 PHPUnit、PHPStan、Faker,不仅体积大、启动慢,还可能暴露调试接口或引入 CVE。
实操建议:
- Dockerfile 里必须写死:
composer install --no-dev --optimize-autoloader - GitHub Actions / GitLab CI 中,缓存 key 要包含
--no-dev标志,例如:composer-${{ hashFiles('**/composer.lock') }}-${{ env.COMPOSER_FLAGS || '--no-dev' }},否则缓存复用导致 dev 包漏装或误装 - CI 流水线跑测试阶段,要用完整安装:
composer install && ./vendor/bin/phpunit;但构建生产镜像那步,必须切回--no-dev
autoload-dev 不是给 dev 包用的,是给“你的测试代码”用的
很多人以为 autoload-dev 是让 PHPUnit 自己能加载,其实不是。autoload-dev 的作用是:当项目装了 require-dev 包(即本地跑过完整 composer install)时,把 tests/、stubs/ 这些目录也加进自动加载规则,方便你写测试时直接 use App\Tests\FooTest。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
关键点:
-
autoload-dev配置再全,如果没装require-dev包(比如生产环境只跑--no-dev),这些路径也不会生效 - 如果你的测试类用了
mockery/mockery,除了把它放require-dev,还得确保autoload-dev包含了tests/目录,否则new Mockery\LegacyMockInterface()仍会找不到类 - 执行
composer dump-autoload --classmap-authoritative时,autoload-dev默认不生效,得加--dev参数才处理测试路径
别试图用 require-dev 实现 staging / prod 差异
Composer 没有 require-staging,也没有环境感知逻辑。想让预发环境装 Xdebug、生产不装?靠 require-dev 切不动——Xdebug 是扩展,不是 Composer 包;想让预发用 mock 网关、生产用真实 Guzzle?该拆的是代码,不是依赖字段。
更务实的做法:
- 把网关客户端抽象成接口,
require里只放稳定契约和真实实现(guzzlehttp/guzzle),require-dev里放 mock 工具(mockery/mockery)和测试桩 - 用环境变量或配置驱动行为,而不是依赖结构。例如:
if ($_ENV['APP_ENV'] === 'test') { $client = new MockClient(); },而非 “让 Composer 在 test 环境多装一个包” - 真要强隔离(比如 SDK 有完全不同的底层通信协议),用独立
composer.prod.json+COMPOSER=composer.prod.json composer install --no-dev,但维护成本高,仅限极少数场景
最容易被忽略的一点:依赖字段只是声明,真正决定类能否加载的,是 vendor/autoload.php 是否注册了对应路径,以及那个路径下有没有真实的文件——写对 require/require-dev 只是第一步,后面 autoload 配置、CI 安装参数、代码调用路径,三者缺一不可。










