require-dev 与 require 的根本区别在于:前者仅用于开发阶段(如测试、静态分析),后者为运行时必需;composer 默认安装两者,生产环境必须用 --no-dev 显式跳过 require-dev,否则会导致体积膨胀、启动变慢及运行时异常。

require-dev 里的包不会被生产环境加载,但必须显式安装(composer install 默认不装),否则 phpunit、phpcs 这类工具直接报错找不到命令。
require-dev 和 require 的根本区别在哪
require-dev 是开发阶段才需要的依赖,比如测试、静态分析、代码生成工具;require 是运行时必需的,比如 monolog/monolog 或 laravel/framework。Composer 默认只装 require(除非加 --dev 或 --no-dev 显式控制)。CI/CD 流水线里漏掉 --no-dev,会导致生产镜像体积膨胀、启动变慢、甚至因 dev 包冲突引发运行时异常。
-
composer install:默认同时装 require 和 require-dev(本地开发常用) -
composer install --no-dev:跳过 require-dev(上线部署必须加) -
composer update:默认更新全部,包括 require-dev;加--no-dev才只更新 require - require-dev 中的包,其 autoloader 不会注册到 production autoloader map,但它的类仍可能被 require 加载 —— 这是隐患来源
怎么安全地添加一个 require-dev 包
用 composer require --dev,不是 composer require。少打个 --dev 就可能把 phpunit 装进 require,导致线上自动加载器试图加载它,抛出 Class 'PHPUnit\Framework\TestCase' not found。
- 正确命令:
composer require --dev phpunit/phpunit:^10 - 错误操作:
composer require phpunit/phpunit(没加--dev,进了 require) - 如果误操作了,手动删
composer.json里 require 下的那行,再运行composer update phpunit/phpunit(此时它已不在 require,update 会移除) - 检查是否生效:
composer show | grep phpunit,应只显示一行,且不含“required by”指向你项目
require-dev 包在生产环境意外执行的典型场景
最常见的是在 config/app.php(Laravel)或 bootstrap/app.php 里,条件判断没写严,导致 class_exists('PhpCsFixer\Fixer\LineEnding\LineEndingFixer') 这类调用在 prod 环境触发 autoload,而该类不存在于 production autoloader 中。
- 避免方式:用
interface_exists()或function_exists()替代class_exists()做存在性判断(前者不触发 autoload) - 更稳妥做法:把 dev 工具相关逻辑全放在
if (app()->environment('local')) { ... }或类似环境守卫内 - CI 部署前务必跑:
composer install --no-dev && php -d display_errors=0 -m | grep -q "opcache" || exit 1,验证是否真没加载 dev 代码
require-dev 不是“随便加”,它和 require 共享同一份 autoload 配置,一旦某个文件被自动加载机制扫描到(比如通过 PSR-4 映射),即使只在测试里用,也可能在 prod 报错。最省心的做法:dev 工具一律用 bin wrapper(如 vendor/bin/phpunit)调用,绝不让它们的类进入应用主流程。











