用途
该技能用于在编写与框架无关的 PHP HTTP 代码时提供指导,覆盖 PSR-7、PSR-15、PSR-17、PSR-18 四项标准。这些标准让库能够处理 HTTP 而不与任何框架耦合:代码依赖接口(Psr\Http*),具体实现通过注入提供。
不适用于 Laravel 自身的 HTTP 层(应改用 laravel-expert),也不适用于 Symfony 的 HttpFoundation 组件——后者是另一种非 PSR-7 的 HTTP 模型;需要时可通过 symfony/psr-http-message-bridge 桥接两者(参见 references/implementations.md)。
主要能力
四项标准及其对应包与提供内容:
- PSR-7(psr/http-message):不可变 HTTP 消息,包括 MessageInterface、RequestInterface、ResponseInterface、ServerRequestInterface、StreamInterface、UriInterface、UploadedFileInterface。
- PSR-15(psr/http-server-handler + psr/http-server-middleware):RequestHandlerInterface、MiddlewareInterface,即中间件管道。
- PSR-17(psr/http-factory):在不指定具体类的情况下创建 PSR-7 对象的工厂。
- PSR-18(psr/http-client):ClientInterface::sendRequest(),发送 PSR-7 请求并获得 PSR-7 响应。
技能还包含参考实现(nyholm/psr7、guzzlehttp/psr7、laminas-diactoros)及其选择指导,以及一个完整的中间件管道模板。
关键规则
- 消息是不可变的:每个 with*() 返回新实例,原实例不变。单独调用 $r->withHeader(...) 是空操作,必须重新赋值。
- 依赖接口而非类:类型提示使用 ResponseInterface,注入 ResponseFactoryInterface。可复用代码中绝不 new Response()。
- 流不是不可变的:StreamInterface 包装真实资源;请求/响应应使用只读流。
- PSR-18 的 4xx/5xx 不是错误:客户端必须将其作为正常响应返回;仅在传输失败(NetworkExceptionInterface)或请求格式错误(RequestExceptionInterface)时抛出异常。
- 头部大小写不敏感:getHeaderLine('foo') == getHeaderLine('FOO');getHeaders() 保留原始大小写。
使用方式
技能规定了强制性的代理工作流:在任何实现之前,并行启动 3 个代理,每个 Agent 调用带一个名称:
- fuse-ai-pilot:explore-codebase:检测 composer.json 中已在使用的 PSR-7 实现。
- fuse-ai-pilot:research-expert:在 php-fig.org 与 Packagist 上核实当前接口版本。
- mcp__context7__query-docs:检查所选实现的工厂 API。
实现之后,运行 fuse-ai-pilot:sniper 进行验证。
架构示例:
src/ ├── Http/ │ ├── Middleware/ # implements Psr\Http\Server\MiddlewareInterface │ │ ├── ErrorHandlerMiddleware.php │ │ └── AuthMiddleware.php │ ├── Handler/ # implements Psr\Http\Server\RequestHandlerInterface │ │ └── Dispatcher.php # the pipeline runner │ └── Client/ # wraps a Psr\Http\Client\ClientInterface └── interfaces/ # your own contracts (SOLID)
完整可运行的管道参见 middleware-pipeline.md。
参考指南按主题组织:消息(psr7-messages.md)、中间件(psr15-middleware.md)、工厂(psr17-factories.md)、HTTP 客户端(psr18-client.md)、实现选择(implementations.md)。模板包括 middleware-pipeline.md(构建带中间件队列的 PSR-15 调度器)。
快速参考示例:
不可变更新(需重新赋值):
$response = $response ->withStatus(201) ->withHeader('Content-Type', 'application/json');
最小中间件:
final class AuthMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { if (!$request->hasHeader('Authorization')) { return $this->responseFactory->createResponse(401); } return $handler->handle($request); } }
PSR-17 工厂(不指定具体类):
$request = $requestFactory->createRequest('GET', 'https://api.example.com'); $request = $request->withBody($streamFactory->createStream('{"ping":true}'));
全部六个工厂接口参见 psr17-factories.md。
最佳实践
应当:注入 ResponseFactoryInterface,使中间件不指定具体类;将捕获异常的中间件放在队列最前(PSR-15 建议);需要轻量、严格的 PSR-7 + PSR-17 单包时使用 nyholm/psr7;对每个 with*() 调用的结果重新赋值。
不应:就地修改消息——with*() 返回新对象;将 PSR-18 客户端返回的 404/500 响应视为异常;混淆 Symfony HttpFoundation 与 PSR-7——两者不同,需通过 symfony/psr-http-message-bridge 桥接;在库代码中硬编码 new GuzzleHttp\Psr7\Response()——应依赖工厂。
适用场景
适用于构建与框架无关的 PHP HTTP 代码,包括 PSR-7/15/17/18 消息、中间件、工厂和客户端。
限制
不适用于 Laravel 自身的 HTTP 层,也不适用于 Symfony 的 HttpFoundation 组件。