composer 不支持多版本并存,依赖隔离需手动控制路径与加载范围;共享 vendor 或误配 composer_vendor_dir 会导致版本冲突;私有 sdk 应用语义化版本约束而非固定版本。

多个项目共用 vendor 目录或 composer.json,必然冲突——Composer 没有“多版本并存”机制,它只按当前目录解析出一棵依赖树,装进一个 vendor 里。 所谓“隔离”,是你主动控制路径、加载时机和约束范围的结果,不是配置开关能一键开启的。
为什么 composer install 会覆盖其他项目的依赖
Composer 默认把所有包解压到当前目录下的 vendor/,且 autoload.php 是静态生成的映射文件。一旦你在项目 A 目录下执行 composer install,它就会按 A 的 composer.json + composer.lock 重建整个 vendor/ 和自动加载逻辑。如果项目 B 的源码也指向同一个 vendor/(比如通过软链接、共享路径或错误的 COMPOSER_VENDOR_DIR),那 B 运行时加载的类就可能来自 A 的依赖快照——版本错、类缺失、方法不存在全都有。
- 不要在父目录(如
/var/www/)运行任何composer命令,每个项目必须有自己的根目录(含composer.json) - 禁用
COMPOSER_VENDOR_DIR环境变量或全局配置,它会让多个项目往同一位置写入 - 检查 Web 服务器配置:Nginx/Apache 的
root必须严格指向各项目自己的public/,不能跨项目共享index.php入口 - Docker 构建中避免
COPY . /app后漏掉vendor/或未在容器内执行composer install --no-dev,否则autoload.php映射与实际代码不匹配
私有 SDK 的 require 字段为什么必须放宽约束
私有库(如 acme/payment-sdk)的 composer.json 不是“自留地”,而是下游项目的约束源头。一旦你写死 "monolog/monolog": "2.8.0",等于强制所有引用它的项目都只能用这个精确版本——哪怕另一个包要求 ^3.0,Composer 就直接放弃解析,报 Conclusion: don't install。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 正确写法是语义化范围:
"monolog/monolog": "^2.8"(前提是 SDK 真兼容 2.8–2.x 全系列) - 若需横跨主版本(如同时支持 monolog 1.x 和 2.x),必须在代码里做运行时判断,而不是靠 Composer 拉两个版本(PHP 不允许同名包共存)
-
conflict字段可声明不兼容版本,但只在install/update阶段起效,对已锁版本无效;语法必须是对象,如"conflict": { "monolog/monolog": ",不能写成数组 - 别在私有库里设
minimum-stability,让它继承主项目的稳定性策略;发布必须打 Git tag(如v2.3.0),别依赖dev-main
composer why-not 为什么必须在私有库目录下运行
很多人在主项目里跑 composer why-not acme/payment-sdk:^2.3,结果输出一堆无关路径。问题在于:私有 SDK 的约束是否生效,取决于它当前安装方式。如果你用 repositories 指向本地路径(path 类型),那 why-not 必须进那个私有库的根目录里执行,否则查不到它 composer.json 里的真实 require 和 conflict。
- 进私有库目录后执行:
composer why-not monolog/monolog:^3.0,若输出your-private-sdk dev-main requires monolog/monolog 2.9.3,说明问题就在你自己库里 -
composer show --tree显示的是composer.lock记录的已安装状态,不是约束源头;真正卡住升级的,永远是composer.json里的声明 - 若输出含
dev-main或dev-develop,检查是否误配了minimum-stability,导致 Composer 默认接受不稳定分支
多租户或子模块场景下 autoload 怎么不串
子项目(如 Git subtree)、多租户 SaaS 或模块化组件,不能靠“共享 vendor”实现复用。它们需要独立的 vendor/、独立的 autoload.php、独立的加载时机。
- 子项目
composer.json中必须设"vendor-dir": "lib/my-subtree/vendor",路径与主项目完全隔离 - 子项目
autoload必须用psr-4显式声明命名空间,如"MySubtree\": "src/",禁用自动发现 - 主项目
autoload和autoload-dev里,绝对不能包含子项目源码路径(如"src/subtree/") - 运行时不能
require vendor/autoload.php,而要按需include_once 'tenants/123/vendor/autoload.php',注意 ClassLoader 注册顺序(先租户后主项目) -
--no-autoloader --no-scripts是子项目初始化的关键参数,跳过 autoload 注册和脚本执行,避免污染主项目上下文
最常被忽略的一点:composer.lock 不是“记录结果”的日志,而是“确定行为”的契约。删了它再装,不会 magically 解决冲突——只会用当前 PHP 版本、当前平台配置、当前 composer.json 约束,重跑一遍相同的失败逻辑。










