必须用composer require --dev,漏掉--dev会导致PHPUnit等工具误入require,上线后类找不到或白占资源;验证需用composer show | grep phpunit确认仅存于require-dev。

composer require --dev 是唯一正确命令
想加 PHPUnit、PHPStan 或 Laravel Pint 这类只在本地写代码或 CI 里用的工具?必须用 composer require --dev,不能漏掉 --dev。漏了就直接进 require 字段,上线跑 composer install --no-dev 后会发现类找不到,或者更糟——它被装进生产环境,白占内存还可能引发冲突。
常见错误操作包括:
- 手敲
composer require phpunit/phpunit(没加--dev) - 编辑
composer.json手动往require里塞"phpunit/phpunit": "^10" - 用
composer update顺手更新了 dev 包,却没意识到它已混入生产依赖树
验证是否成功:运行 composer show | grep phpunit,输出应只有一行,且不带 required by your-project 这类提示——说明它只存在于 require-dev,没被主项目“正经依赖”。
require-dev 的包不会自动加载到生产环境
require-dev 不只是“不安装”,它影响自动加载机制本身。Composer 在生成 vendor/autoload.php 时,只会把 autoload 和 autoload-dev 下声明的路径注册进去;但生产环境下执行 composer install --no-dev,连 autoload-dev 的映射都不会写入 autoloader map。
这意味着:
-
class_exists('PHPUnit\Framework\TestCase')在生产环境会返回false(不触发 autoload) - 但如果代码里写了
new \PhpCsFixer\Fixer\LineEnding\LineEndingFixer(),就会直接报错——因为类根本没注册 - 安全写法是用
interface_exists()或function_exists()做存在性判断,它们不触发 autoload
更稳妥的做法是把所有 dev 工具调用包在环境守卫里:if (app()->environment('local')) { ... } 或 if (PHP_SAPI === 'cli') { ... }。
CI/CD 流水线必须显式控制 --no-dev
本地开发可以默认装全量依赖,但 CI 构建镜像或部署到生产服务器时,composer install 必须带 --no-dev。不加的话,镜像体积多出 50MB+,启动慢一倍,还可能因 xdebug、psysh 等扩展导致 PHP 初始化失败。
推荐在 CI 脚本中固定写死:
- 开发构建(测试阶段):
composer install - 生产构建(Dockerfile 或部署脚本):
composer install --no-dev --optimize-autoloader
别信“我们只在本地装 dev 包,线上不会动”,只要 composer.lock 里有 dev 包记录,composer install 就默认装——除非你明确告诉它跳过。
误塞进 require 的 dev 包怎么清理
已经手误把 phpunit/phpunit 写进了 require?别删 vendor 重装。正确做法分三步:
- 手动删掉
composer.json中require下那行"phpunit/phpunit": "..." - 运行
composer update phpunit/phpunit(此时它已不在require,Composer 会把它从 vendor 移除) - 再补上
composer require --dev phpunit/phpunit:^10
最后检查 composer.lock:搜索 phpunit,确认它只出现在 packages-dev 节点下,而不是 packages —— 这才是干净状态。
真正容易被忽略的点是:require-dev 不是“可选安装项”,而是“运行时隔离区”。它的边界由 Composer 安装命令和 autoloader 生成逻辑共同划定,任何绕过这两者的操作(比如手动 require 文件、硬编码 new class)都会撕开这个边界。











