provide字段必须写在被require的库的composer.json根级,声明标准虚拟包名(如psr/log-implementation),值推荐"*",仅当其他包require该虚拟包时才在install/update中生效,不负责自动加载。

provide字段写在哪才生效
必须写在被 require 的包(即你发布的库)的 composer.json 顶级位置,根项目里的 provide 字段完全被 Composer 忽略。常见错误是把 provide 放在自己项目的 composer.json 里,以为能“对外提供能力”,结果什么都不会发生。
-
provide是给其他包看的声明,不是给自己用的配置 - 键必须是标准虚拟包名,比如
psr/log-implementation,不能写成psr/log(那是接口包本身) - 值推荐用
"*",除非你明确实现了某个严格版本的契约(如 PSR-16 v2.0),否则写"^2.0"却没真兼容,运行时可能报错
哪些虚拟包名是生态认的
别自己造名字,Composer 不校验实现,只靠字符串匹配。主流框架和规范定义了一套事实标准,填错就等于白写。
-
psr/log-implementation:任何实现Psr\Log\LoggerInterface的包都该提供它 -
psr/cache-implementation或psr/simple-cache-implementation:对应缓存标准 -
psr/http-client-implementation:Guzzle、Symfony HttpClient 都提供这个 -
ext-gd、ext-mbstring这类ext-*名字,用于声明 PHP 扩展可用性,供其他包做条件依赖 - 框架私有约定如
laravel/framework,需配合replace使用才有效,单独provide不触发 Laravel 特定逻辑
provide 和 replace 混用时的陷阱
两者语义不同,但可以共存。误用会导致依赖解析失败或运行时报错。
-
provide只影响依赖解析阶段——告诉 Composer “我满足某个 require 条件”,不删任何东西 -
replace是硬替代:被替换的包及其 autoload、脚本、依赖全被丢弃,Composer 当它不存在 - 如果两个包都
provide同一个虚拟包(如都声明psr/cache-implementation),Composer 默认选第一个解析到的,不保证顺序,容易踩到隐式依赖问题 - 若同时
provide和replace,例如"provide": {"psr/cache": "^1.0"}+"replace": {"doctrine/cache": "^1.11"},注意前者是“能力声明”,后者是“物理移除”,别指望provide能绕过replace带来的 autoload 缺失
为什么装了包,provide 还是不生效
最常被忽略的一点:provide 只在 composer install 或 composer update 时参与依赖解析,且仅当有其他包通过 require 明确依赖该虚拟包时,才会起作用。
- 本地开发时只是
require了你的包,但没人require "psr/log-implementation",那provide就纯属声明,无任何行为 - 类自动加载仍需单独配好
autoload,provide不负责加载任何文件 - 如果你的包没发布到 Packagist 或自建仓库,别人无法通过
composer require安装它,那它的provide对整个生态就是不可见的 - 检查是否生效,可用
composer show --tree看依赖树里有没有因你的provide而跳过安装原虚拟包的痕迹
实际生效的关键不在写得多漂亮,而在是否被另一个包的 require 真正触发——这一步最容易被当成“配置完了就该有用”,结果卡在依赖链下游根本没走到解析环节。











