psr/http-client-implementation 是 Composer 用于标识 PSR-18 实现的虚拟包,非真实可安装包;它通过 "provide": {"psr/http-client-implementation": "*"} 声明实现能力,配合 require "psr/http-client" 和真实接口实现类,才能被 DI 容器识别并自动注入。

psr/http-client-implementation 是什么,为什么不能直接 require
它不是真实包,而是 Composer 用来标记“谁实现了 PSR-18 接口”的虚拟占位符。执行 composer require psr/http-client 只会装一堆空接口(Psr\Http\Client\ClientInterface 等),没有任何请求能力——类存在,但你 new ClientInterface() 必然报错 Cannot instantiate interface。
真正能发请求的必须是具体实现,比如 guzzlehttp/guzzle、symfony/http-client 或 php-http/curl-client。这些包在自己的 composer.json 里通过 "provide" 字段主动声明:"psr/http-client-implementation": "*",Composer 才会在 composer show 中标出它们是“可用实现”。
如何让自己的包被识别为 PSR HTTP 客户端实现
如果你开发的是一个自定义 HTTP 客户端(比如封装了 cURL + 自动重试 + 日志),想让它被其他项目当作标准 PSR-18 实现自动注入,就必须在自己包的 composer.json 里写明 provide:
{
"name": "myorg/my-http-client",
"provide": {
"psr/http-client-implementation": "*"
},
"require": {
"psr/http-client": "^1.0"
}
}
注意三点:
-
provide是声明“我提供了这个虚拟包的能力”,不是依赖;它不会触发安装,只影响 Composer 的依赖解析逻辑 - 必须同时
requirepsr/http-client(接口定义),否则你的实现类无法类型兼容 - 你的主类必须真正实现
Psr\Http\Client\ClientInterface,否则运行时会因方法缺失崩溃,provide不做任何运行时校验
验证是否被正确识别的两种方式
装好你的包后,别只看 vendor/ 有没有文件——得确认 Composer 真把它当成了实现:
- 运行
composer show,搜索输出中是否出现带psr/http-client-implementation标签的包名(如myorg/my-http-client dev-main PSR-18 HTTP client implementation) - 在代码里检查:
class_exists('Psr\Http\Client\ClientInterface')应为true,且get_declared_classes()中应能找到你实现类的全名(比如MyOrg\Http\Client),并且is_subclass_of($class, 'Psr\Http\Client\ClientInterface')返回true
漏掉 provide 声明,哪怕你的类写得再标准,Laravel 或 Symfony 的 DI 容器也找不到可绑定的具体实现,Target [Psr\Http\Client\ClientInterface] is not instantiable 就会照常报。
常见陷阱:provide 写错或没生效
provide 看似简单,但极易静默失效:
- 键名写成
"psr/http-client"(少-implementation)——无效,Composer 完全忽略 - 值写成
"1.0"或"^1.0"——必须是"*",这是虚拟包约定死的写法 - 包未发布到 Packagist 或未用
path仓库方式引入主项目——composer show根本看不到它,provide再对也没用 - 本地测试时只改了包里的
composer.json,但主项目没执行composer update myorg/my-http-client或composer dump-autoload——autoload 映射没更新,类还是加载不到
最关键的其实是最后一点:声明只是第一步,类能被自动加载、能被容器实例化,才是闭环。很多问题表面是 provide 没配,实际是 autoload 路径没对齐或命名空间拼错了。











