workerman/psr7
PSR-7 HTTP message implementation for Workerman, offering interoperable request and response objects for middleware-friendly PHP services on the Workerman event loop. Lightweight and easy to integrate into existing Workerman HTTP applications.
This package is a PSR-7 (HTTP message interfaces) implementation maintained by the Workerman author. It is not an HTTP client or server — it is the data model for Request, Response, Stream, Uri, and UploadedFile objects, plus helpers to convert between Workerman's native HTTP request/response and PSR-7.
If you are in a plain Laravel app (nginx/php-fpm or Octane on Swoole/RoadRunner/FrankenPHP), you will almost never install this. Reach for it only when you are already inside the Workerman ecosystem — webman, workerman/http-client, or a custom Workerman\Worker HTTP server — and need PSR-7 messages to hand to PSR-15 middleware, a signed-URL/guzzle-based client that consumes PSR-7, or your own framework glue.
composer require workerman/psr7
First things to read in src/:
MessageTrait / StreamTrait — the immutable message base everything else composes.ServerRequest (or the equivalent factory in Workerman\Psr7\) — the class that wraps a Workerman Request.Response + Stream — how bodies are written and flushed.Uri — parsing/merging rules that will bite you on query strings.First use case: wrap an incoming Workerman request into a PSR-7 ServerRequest, run it through a PSR-15 middleware pipeline (e.g. relay/relay), then turn the resulting ResponseInterface back into something Workerman can send().
Bridging a Workerman request into PSR-7. Inside an onMessage callback you typically do something like reconstruct a ServerRequest from the Workerman $request->get(), $request->header(), $request->uri() and $request->post() values. Keep this conversion in a single factory class — do not scatter it across handlers.
Bridging PSR-7 back to Workerman. Iterate getHeaders(), call getBody()->rewind() (or copy it into a Workerman Response's body), then set $response->withStatus($psr->getStatusCode()). This is the piece people forget and end up with zero-length bodies.
Middleware pipelines. Because the objects are standard PSR-7 and (if the package implements it) PSR-15, you can drop in any ecosystem middleware — auth, CORS, request-id — without pulling Guzzle's PSR-7 as a second implementation. This is the main reason to prefer it over writing your own message classes in a Workerman service.
Inside webman. webman's HTTP client and several plugins already use this package's messages; if you write a plugin or a controller that talks to that client, you get ResponseInterface back. $resp->getBody()->getContents() and stream through large downloads rather than casting to string.
Interop with Laravel's kernel. If you genuinely need to call a Laravel controller from a Workerman handler, convert the PSR-7 request to a Symfony Request via symfony/psr-http-message-bridge + nyholm/psr7 (or this package, which is interface-compatible), dispatch through app()->handle(), then convert the Symfony\Component\HttpFoundation\Response back into PSR-7 with the same bridge. Do not try to reuse this package to talk to Laravel's kernel directly — the interfaces are different.
Uploads. Use getUploadedFiles() with moveTo() to persist files; PSR-7 gives you the target path, not a magic storage() helper. Wire this to Laravel's filesystem if you're sharing storage.
Testing. Because messages are immutable and streams are in-memory by default, you can unit-test handlers by constructing requests directly (e.g. new ServerRequest('GET', '/x') plus withHeader(...)) without spinning up a Workerman server. Write a tiny helper that builds a ServerRequest from an array — you will reuse it constantly.
guzzlehttp/psr7 or nyholm/psr7. Before committing to it, diff surface APIs — often you are better off pulling guzzlehttp/psr7 which has vastly more adoption, a test suite, and downstream tooling. Choose workerman/psr7 only when its Workerman-specific conversion helpers are what you actually need.with* returns a new object. Forgetting to reassign ($request->withHeader('X', 'y') with the return value dropped) is the #1 silent bug.getContents() consumes the stream; call rewind() before re-reading. Reading twice without a rewind will produce an empty string, which is easy to misdiagnose as an empty body.Uri::withQueryValue. The Uri implementation here follows the spec quirks (space vs +, duplicate keys, array values). Test with arrays (?a[]=1&a[]=2) from Laravel-generated URLs — round-tripping through PSR-7 sometimes re-encodes them differently.$_SERVER. This package is aimed at Workerman, not PHP-FPM. If you need a from-globals factory (e.g. for tests, or bridging into a legacy handler), write it yourself or pull in nyholm/psr7 for that path.php://temp will grow per request. Close/reset streams after sending responses, and stream large responses in chunks rather than buffering everything into a string.withProtocolVersion. Workerman HTTP/2 support is separate; don't assume this package negotiates it for you. If you forward getProtocolVersion(), handle '2' and '2.0' defensively.$response->getStatusCode(), count($response->getHeaders()), and (string) $response->getBody() in that order — this distinguishes "wrong response object" from "empty stream" quickly. For requests, log (string) $request->getUri() plus getMethod() before invoking middleware.Psr\Http\Message\*Interface or composing Workerman\Psr7\* classes; subclassing is rarely necessary and likely to break if the base traits change between minor versions. Keep your conversion layer behind your own interface (WorkermanRequestAdapter, PsrResponseEmitter) so you can swap to guzzlehttp/psr7 or nyholm/psr7 in one file if this package stalls.symfony/http-foundation for the server side and psr/http-message for the client side — this library is on the client side of that split. Using it as an ad-hoc Symfony replacement will cost you more than it saves.How can I help you explore Laravel packages today?