composer require --dev仅修改composer.json的require-dev字段并更新composer.lock,不控制安装行为;是否安装开发依赖取决于后续composer install是否加--no-dev参数。

composer require --dev 只改配置,不控制安装行为
composer require --dev 的作用非常明确:它只修改 composer.json 中的 require-dev 字段,把包写进去,并更新 composer.lock。它不会让这个包“自动只在开发环境生效”,也不会阻止它被装到生产服务器上。
真正起决定作用的是后续的安装命令:
-
composer install(无参数)→ 安装require和require-dev里的全部包 -
composer install --no-dev→ 只装require,跳过require-dev全部内容
很多线上故障就源于本地测试时用了 composer require --dev,但部署脚本漏了 --no-dev,结果 phpunit/phpunit 或 symfony/var-dumper 直接进了生产容器。
哪些包必须放 require-dev?判断标准很实在
别靠“感觉”,看运行时是否真被 PHP 执行加载:
- 测试框架:
phpunit/phpunit、pestphp/pest—— 测试跑完就退出,代码里不会new它的类进业务逻辑 - 静态分析工具:
phpstan/phpstan、phpcs—— CLI 调用,不参与 HTTP 请求生命周期 - 本地辅助工具:
laravel/sail、symfony/cli—— 启动本地服务用,线上根本不会执行 - Mock 类库:
mockery/mockery—— 只在测试文件中use,业务代码不依赖
反例:doctrine/doctrine-bundle 看似只在 dev 用,但它在 Symfony 容器启动时就被注册,必须放 require;否则线上直接报服务未定义。
漏加 --no-dev 的真实后果不只是“多几个文件”
它会直接改变运行时表现:
-
vendor/autoload.php映射变大,PHP-FPM worker 启动慢 100–300ms(尤其 Laravel + debugbar 场景) -
symfony/var-dumper的dump()函数若残留且未禁用,可能泄露数据库连接信息或用户 session 结构 -
barryvdh/laravel-debugbar前端面板未关闭时,暴露完整 SQL 查询和环境变量 - Docker 镜像体积膨胀 20–60MB,CI 构建缓存失效更频繁
- 某些 dev 包(如
psy/psysh)依赖/tmp写入或readline扩展,上线后因权限或扩展缺失抛错,错误日志却藏在 FPM error_log 里难定位
最隐蔽的一点:--no-dev 不影响 autoload-dev 的路径声明 —— 它只是不装包。如果你在 require-dev 里加了一个带 autoload-dev 的包,又没在生产执行 composer dump-autoload --classmap-authoritative,类仍可能被意外加载(比如通过 require_once 手动引入)。
CI/CD 和 Dockerfile 里怎么写才安全
不能只靠人记,得固化成命令:
- CI 脚本中,所有生产构建步骤必须显式写:
composer install --no-dev --optimize-autoloader --no-interaction - Dockerfile 中,避免
RUN composer install这种裸命令,应为:RUN composer install --no-dev --optimize-autoloader --no-interaction - 检查
COMPOSER_NO_DEV环境变量是否被设为0或空字符串 —— 它会覆盖命令行参数,导致--no-dev失效 - 上线前可加一行校验:
composer show | grep -E '^(phpunit|phpstan|debugbar)' || echo "no dev packages installed"
真正容易被忽略的是:Laravel 的 APP_DEBUG=true 和 --no-dev 完全解耦。就算你用了 --no-dev,只要 .env 里 APP_DEBUG=true,框架仍可能输出堆栈,而此时 symfony/debug-bundle 虽没装,别的调试逻辑也可能触发敏感信息输出。


















