require-dev 中的包在生产环境不会被加载,前提是部署时必须显式使用 composer install --no-dev;否则 phpunit 等调试依赖将被安装并注册,导致体积膨胀、性能损耗及安全风险。

开发环境和生产环境依赖混淆,根本原因不是 Composer 不懂环境,而是你没管住 composer install 的参数和 CI/CD 缓存逻辑。
require-dev 里的包到底会不会被生产环境加载?
不会——但有个前提:你必须在生产部署时加 --no-dev。不加的话,phpunit、symfony/var-dumper 全部照装不误,vendor 目录多出 20MB,autoload.php 多注册几十个路径,还可能暴露 dump() 或调试路由。
-
require-dev中的包在composer install --no-dev时完全不下载、不解压、不写入vendor/autoload.php -
autoload-dev配置再全,只要没装对应包(比如生产只跑--no-dev),这些路径就压根不会生效 - 哪怕你本地
composer dump-autoload --classmap-authoritative,默认也不处理autoload-dev路径;得显式加--dev参数才生效
Dockerfile 和 CI 脚本里漏掉 --no-dev 是高频翻车点
GitHub Actions、GitLab CI 默认缓存 vendor/,但缓存 key 很少包含 --no-dev 标志变化。结果就是:上一次带 --no-dev 的缓存被复用,phpunit 没装进去,测试直接报 Class 'PHPUnit\Framework\TestCase' not found;反过来,如果某次忘了加 --no-dev,缓存污染后,生产镜像里就混进了 phpstan 和 faker。
- Dockerfile 必须写死:
composer install --no-dev --optimize-autoloader - CI 缓存 key 要显式包含 dev 模式状态,例如:
composer-${{ hashFiles('**/composer.lock') }}-${{ env.COMPOSER_FLAGS || '--no-dev' }} - 别信“本地好好的”,CI 环境没有
vendor/初始状态,全靠缓存或重装,参数错一点就崩
把环境相关逻辑塞进 require-dev 是危险操作
有人想用 require-dev 区分 staging / prod,比如 staging 装 php-mock/php-mock,prod 换成 guzzlehttp/guzzle。这行不通——Composer 没有 require-staging 这种字段,也没有环境感知能力。
-
require-dev不是“环境开关”,它只是“是否参与运行时”的分界线:require 里的类会被线上请求 new/use;require-dev 里的只在你敲命令时起作用 - 真要按环境切换实现,应该下沉到代码层:定义接口,用工厂或条件判断加载不同实现,而不是靠 Composer 控制装不装包
- 如果某个包既被测试用、又被线上配置逻辑间接依赖(比如日志上下文处理器),它就不是纯 dev 依赖,必须挪回
require
最常被忽略的其实是 autoload-dev 和 require-dev 的耦合关系:你删了 require-dev 里的包,但忘了清理 autoload-dev 映射,上线后自动加载器仍会尝试找那些已不存在的路径——不报错,但浪费性能。检查方式很简单:composer dump-autoload --dry-run --dev 看它打算加载哪些文件,再对照 vendor 目录是否存在对应包。


















