require-dev不是开发环境开关,而是非生产依赖声明;是否安装仅取决于是否执行composer install --no-dev,APP_ENV等变量无影响,关键看包的类是否会在生产环境被new、use或class_exists()调用。

require-dev 不是“开发环境开关”,而是“非生产依赖声明”
它不随 APP_ENV 或服务器类型自动生效,也不代表“只在本地装”。真正决定是否安装的,只有你执行的命令:没加 --no-dev,phpunit 就会进线上镜像;加了,monolog 却可能因误放 require-dev 而直接报 Class not found。
关键判断标准就一条:这个包的类或函数,会不会在 APP_ENV=production 下被 new、use、class_exists() 或 include?
- 会 → 必须进
require(如laravel/framework、guzzlehttp/guzzle) - 不会 → 才能进
require-dev(如phpunit/phpunit、phpstan/phpstan、laravel/sail) - 边界情况(如
symfony/var-dumper):只要控制器里写了dump($data),哪怕套了if (app()->environment('local')),也得进require—— 因为 PHP 解析时已加载该类,autoload 会触发
加错位置的典型翻车点
composer require --dev 看似简单,但参数顺序、版本写法、旧版兼容性全可能埋雷:
- 漏掉
--dev:运行composer require phpunit/phpunit→ 直接写进require字段,线上必报错 - 缩写成
-d:旧版 Composer 会静默忽略,新版已弃用;composer require phpunit/phpunit -d和不加一样,照样进require - 想升级已有 dev 包却不带版本号:
composer require --dev phpunit/phpunit不会更新版本,必须显式写^10或改用composer update phpunit/phpunit - 误用
composer require-dev(带连字符):这不是有效命令,Composer 会报错或忽略
验证是否成功:运行 composer show | grep phpunit,输出应只有一行,且不含 required by your project —— 表明它没被 require 引用。
部署时 --no-dev 不是可选项,是强制项
CI/CD 脚本或 Dockerfile 里漏掉 --no-dev,后果很实在:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 镜像体积多出 40–60MB(
phpunit+php-cs-fixer+ 依赖树) - 可能暴露
/vendor/phpunit或/_debugbar这类入口 - 因缺失扩展(如
phpdbg)导致composer install在生产机上直接失败 -
COMPOSER_NO_DEV=1环境变量存在时,--no-dev会重复生效,但不影响结果;真正危险的是它被意外设为0或未 unset
注意:composer.lock 文件锁定了依赖结构 —— 如果它是别人用 --no-dev 生成并提交的,你本地跑 composer install(无参数)也不会装 dev 包。但这不是常态,别依赖 lock 文件来兜底。
autoload-dev 配置和代码耦合才是隐形炸弹
autoload-dev 不是给 phpunit 自己用的,而是告诉 Composer:“当 dev 包被装上时,请把 tests/ 目录也加进 autoloader”。但它本身不控制运行时行为。
真正危险的是代码里藏着的调用:
- 在
config/app.php里写了class_exists('PhpCsFixer\Fixer\LineEnding\LineEndingFixer')→ 生产环境触发 autoload,找不到类 - 在
bootstrap/app.php中硬编码require 'tests/Helper.php'→--no-dev后文件不存在,直接 fatal error - 用了
class_exists()做判断,但没加false第二个参数(即不触发 autoload),结果还是加载失败
更安全的做法:用 interface_exists() 或 function_exists() 替代;或者把所有 dev 工具相关逻辑包在 if (app()->environment('local')) { ... } 里,且确保该判断发生在任何 autoload 触发之前。

















