虚拟包是仅通过provide字段声明的占位符,不包含源码、不安装、不 autoload,专用于声明某包已提供特定接口能力以解决依赖冲突;必须在被依赖包的composer.json中定义,主项目中声明无效。

什么是虚拟包(virtual package)
虚拟包不是真实存在的代码包,而是一个仅靠 provide 字段声明的“占位符”,用来告诉 Composer:“当前项目(或某个已装包)已经提供了某接口/能力,无需再装同名包”。它不包含源码、不触发安装、不生成 autoload,纯粹用于解决依赖冲突或替代实现。
在 composer.json 中声明 provide 的正确写法
虚拟包必须通过 provide 字段显式声明,且只在被 require 的包中起作用——也就是说,你不能在主项目的 composer.json 里写 provide 让自己“提供”什么(Composer 会忽略),它只在被依赖的包(比如你发布的库)中生效。
-
provide是一个对象,键是包名(如"psr/log": "^1.0"),值是版本约束(通常用"*"或具体版本号) - 常见用途:你的库实现了 PSR 标准但不想硬依赖
psr/log,就加"provide": {"psr/log": "*"},下游项目 require 它时,Composer 就不会再去装psr/log - 不能写错包名:大小写、斜杠、vendor 名必须和 Packagist 上完全一致;
"PSR/log"或"psr-log"都无效 - 别和
require冲突:如果同时require和provide同一个包,Composer 会报错
为什么 require 虚拟包会失败
直接在主项目 composer.json 的 require 中写 "psr/log": "*",然后指望靠 provide 满足它——这行不通。Composer 不会扫描本地代码去“发现”你是否实现了 PSR 接口,它只认已安装包的 provide 声明。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 想跳过安装
psr/log?你得让某个已装包(比如你自己写的日志适配器)在它的composer.json里声明"provide": {"psr/log": "*"},然后主项目require那个适配器 - 常见错误现象:
Could not find package psr/log—— 因为你没装任何提供它的包,provide不是魔法,它不自动注册 - 验证是否生效:运行
composer show --tree,找到你 require 的包,看它下面是否列出psr/log * (provided)
provide 和 replace 的关键区别
replace 是更激进的替代机制,它会让 Composer 彻底忽略被替换的包(包括其依赖树),而 provide 只解决“满足依赖”的问题,不干涉其他行为。
-
replace常用于 fork 替换(如"monolog/monolog": "2.0.0"替换原版),但会导致原包所有依赖丢失,容易出错 -
provide更安全:你提供接口,但不接管原包的其他功能;比如你写了个轻量 logger,provide了psr/log,下游仍可自由 require 其他 PSR-3 兼容组件 - 两者都只能写在被依赖包的
composer.json里,主项目写无效
真正容易被忽略的是:虚拟包不解决自动加载问题。哪怕 provide 成功绕过了依赖安装,你仍需确保对应接口类(如 Psr\Log\LoggerInterface)能被加载——要么靠其他已装包提供,要么自己手动 files 或 classmap 注册。










