require-dev 无法按环境安装依赖,因其仅为全局开关,不区分具体环境;真正方案需通过 platform.config-extra 动态注入、脚本钩子或自定义插件在运行时条件化处理 require,确保 lock 文件准确反映环境依赖。

require-dev 为什么不能解决按环境安装的问题
因为 require-dev 是全局开关,它只控制「是否安装 dev 包」,不区分 staging、production 或 ci 等具体环境。即使你用 composer install --no-dev,所有非 dev 的 require 项仍会无条件装上——这和「按环境变量决定装哪些 require」完全不是一回事。
真正要实现的是:比如在 CI 环境中跳过 phpunit/phpunit(哪怕它在 require 里),或在本地开发时额外装 psy/psysh,但又不把它塞进 require-dev(避免污染 dev 流程)。
用 composer config platform.config-extra 实现运行时条件判断
Composer 本身不支持 if-else 式的依赖声明,但可通过 platform.config-extra 注入环境感知的配置,再配合脚本钩子做动态处理。核心思路是:让 composer.json 中的 require 字段变成“占位符”,实际解析由外部逻辑接管。
- 在
composer.json里把需按环境控制的包移到extra.require-by-env下,例如:"extra": { "require-by-env": { "ci": ["phpunit/phpunit:^10"], "local": ["psy/psysh:^0.12"] } } - 写一个
scripts/composer-require-env.php,读取$_SERVER['COMPOSER_ENV']或getenv('COMPOSER_ENV'),拼出对应包列表,写入临时composer.json或直接调用Composer\Package\Loader\JsonLoader解析 - 在
composer.json的scripts中注册钩子:"scripts": { "prepare-require": "php scripts/composer-require-env.php", "post-install-cmd": "@prepare-require", "post-update-cmd": "@prepare-require" }
注意:该方案绕过了 Composer 原生依赖解析器,所以不会自动处理版本冲突或 autoload 注册——你需要手动调用 composer dump-autoload 并确保包已下载到 vendor/。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
更稳妥的做法:用 Composer 插件拦截 install/update 流程
如果项目需要稳定、可复现、且兼容 lock 文件的行为,硬编码脚本不可靠。推荐使用自定义 Composer 插件,在 InstallerEvent 触发前修改 $event->getPackage()->getRequires()。
- 插件需实现
Composer\Plugin\PluginInterface和Composer\EventDispatcher\EventSubscriberInterface - 监听
PRE_INSTALL_CMD和PRE_UPDATE_CMD,读取环境变量,从$composer->getPackage()->getExtra()['require-by-env'] ?? []中提取当前环境所需包 - 关键限制:
Package对象是只读的,不能直接改requires;必须通过RepositoryManager::setRepositoryClass()注入自定义Repository,在findPackage()阶段过滤掉非目标环境的包 - 这样生成的
composer.lock会真实反映当前环境的依赖图,CI 和本地执行composer install得到的 vendor 结构一致
代价是插件需维护 Composer 版本兼容性(如 v2.5+ 的 PoolBuilder 接口变动),且调试难度高——composer -vvv 日志里看不到你的过滤逻辑是否生效,得加 file_put_contents('/tmp/log', ...) 手动埋点。
为什么不要用环境变量直接改 composer.json 再执行 install
看起来最直白的方法——用 shell 脚本根据 $ENV 替换 composer.json 中的 require 字段,再跑 composer install——实际踩坑最多:
- 每次替换都会导致
composer.lock的 hash 变化,即使内容没变,Git 也会标记为修改,干扰 PR 审查 -
composer update会覆盖你手工改的composer.json,导致下次 install 拉错包 - 无法保证多阶段构建(如 Docker build 中的 builder stage 和 final stage)使用同一份 lock,容易出现 “本地能跑,CI 报 Class not found”
- 某些包(如
ext-*扩展依赖)的 require 条件与 PHP SAPI 类型强相关,仅靠字符串替换无法判断clivsfpm
真正可靠的按需安装,本质是把“环境决策”从文件内容移到执行时逻辑,并让 lock 文件成为该逻辑的权威输出。否则省下的几行代码,后期花十倍时间 debug。










