composer 不支持按环境动态切换 require/require-dev;require 全局生效,require-dev 仅由 --no-dev 控制,非环境感知;正确方案是用 config.platform 模拟扩展或拆分多份 composer.json 配合 path repo。

Composer 的 require 和 require-dev 不能按环境动态切换
Composer 本身不支持「运行时」或「部署环境」(如 prod / dev)触发不同依赖安装。你写的 require 是全局生效的,require-dev 只在 --no-dev 被跳过——但这不是环境感知,而是安装指令控制。
常见错误现象:在 prod 环境里手动删掉某些包、或靠 CI 脚本临时改 composer.json,结果本地和线上依赖不一致、composer.lock 频繁冲突、部署失败。
-
require-dev不是「开发环境专用」,它只是「非生产安装时可选」;composer install --no-dev在任何机器上都跳过它们 - 没有
require-prod或environment: prod这类原生字段 - 试图用
scripts+composer config动态改require会导致composer.lock哈希失效,CI 构建不可重现
真正可行的方案:用 platform + config.platform 模拟环境约束
这不是加载不同包,而是让 Composer 在不同环境下「假装」运行在不同 PHP/扩展环境中,从而影响依赖解析结果——尤其适用于「某包只在 dev 环境需要特定扩展,而 prod 环境不需要」这类场景。
使用场景:你的应用在开发机装了 xdebug,但线上没装;而某个 dev-only 包(如 phpunit/phpunit)依赖 ext-xdebug。你不希望线上 composer install 因为缺扩展报错。
- 在
composer.json里加:"config": { "platform": { "ext-xdebug": "3.1.5" } }—— 这会让 Composer 认为「当前环境已有 xdebug」,跳过检查 - 线上部署时,用
COMPOSER_PLATFORM_CHECK=0 composer install --no-dev关闭平台扩展校验(比改platform更干净) - 注意:
platform只影响扩展和 PHP 版本声明,不能用来「隐藏整个包」
需要彻底隔离依赖?用多份 composer.json + path repo
当确实要「prod 安装 A 包,dev 安装 B 包,且两者互斥」时,唯一可靠做法是拆成两个独立依赖集,通过 Composer 的 path 类型仓库机制组合。
JTBC CMS(5.0) 是一款基于PHP和MySQL的内容管理系统原生全栈开发框架,开源协议为AGPLv3,没有任何附加条款。系统可以通过命令行一键安装,源码方面不基于任何第三方框架,不使用任何脚手架,仅依赖一些常见的第三方类库如图表组件等,您只需要了解最基本的前端知识就能很敏捷的进行二次开发,同时我们对于常见的前端功能做了Web Component方式的封装,即便是您仅了解HTML/CSS也
例如:把核心业务逻辑抽成 myapp/core,再建两个包装项目:myapp/prod-deps 和 myapp/dev-deps,各自定义完整 require,然后在主项目中用 repositories 引入:
{
"repositories": [
{
"type": "path",
"url": "../myapp/prod-deps"
}
],
"require": {
"myapp/prod-deps": "*"
}
}
- CI 中根据环境变量切换
url指向../myapp/prod-deps或../myapp/dev-deps - 必须确保两个子项目都声明
"minimum-stability": "stable",否则主项目可能因稳定性冲突拒绝安装 - 维护成本高:每次加新包都要同步到对应子项目,
composer.lock不共享,无法复用依赖树优化
别碰 scripts 自动增删依赖这种操作
有人写 post-install-cmd 脚本,根据 APP_ENV 删除某些 vendor 目录或改 composer.json,这会破坏 Composer 的确定性模型。
后果很直接:同一份 composer.lock 在不同机器上产生不同 vendor/ 结构;composer update 行为不可预测;composer show 显示的已安装包与实际不符。
- Composer 的设计前提是「lock 文件决定最终依赖状态」,任何绕过它的修改都会导致后续命令失序
- 哪怕用
composer remove在脚本里删包,也会触发重新 dump-autoload,可能破坏 classmap 缓存 - 如果真要条件性功能,应该把逻辑下沉到代码层(比如用
class_exists('SomeDevClass')做运行时判断),而不是在依赖层硬切
环境差异越复杂,越要守住「一份 lock 文件、一份 vendor 结构」这条线。否则问题不会消失,只会从依赖管理转移到运行时异常和排查成本上。









