swlib/http
swlib/http is a lightweight HTTP message library for PHP and Swoole, providing request, response, URI, and stream objects for coroutine-friendly clients and servers. It offers a simple foundation for building HTTP interactions without heavy framework dependencies.
composer require swlib/http
The package ships no Laravel service provider, config file, or facade — there is nothing to register. Everything lives under the Swlib\Http\ namespace and is a set of concrete PSR-7 (Psr\Http\Message\*) implementations built on top of Guzzle's guzzlehttp/psr7 code, so the mental model is: these are Request/Response/Uri/Stream value objects, not a client.
Where to look first:
composer.json — confirm which psr/http-message version it targets (^1.0 vs ^2.0) and which Guzzle it borrows from. This is the single most important file, because it determines whether it will coexist with your existing guzzlehttp/guzzle install.src/Request.php, src/Response.php, src/Uri.php, src/Stream.php — the PSR-7 surface you'll actually touch.MessageTrait / StreamTrait — that's where the Guzzle-derived immutability and header handling lives.First use case: you have a Psr\Http\Message\ServerRequestInterface consumer (a middleware, a PSR-15 handler, an SDK, a signed-webhook verifier) and you need to hand it a request object constructed from Laravel's Illuminate\Http\Request:
use Swlib\Http\Uri;
use Swlib\Http\Request;
use Swlib\Http\Stream;
$laravel = request();
$psr = (new Request($laravel->method(), new Uri($laravel->fullUrl())))
->withBody(Stream::create($laravel->getContent()))
->withHeaders($laravel->headers->all());
That conversion is the realistic day-to-day entry point in a Laravel app; you rarely instantiate these classes to send traffic.
Adapter between Illuminate\Http\Request and PSR-7. Wrap the conversion in a small dedicated class (App\Http\Psr7\FromLaravelRequest) and register it as a singleton. Keeping the mapping in one place means header casing, query parsing, and body handling stay consistent across controllers, listeners, and queued jobs.
final class Psr7Factory
{
public function fromLaravel(\Illuminate\Http\Request $r): \Psr\Http\Message\ServerRequestInterface
{
$uri = new Uri($r->fullUrl());
return (new ServerRequest($r->method(), $uri, $r->headers->all()))
->withBody(Stream::create($r->getContent()))
->withQueryParams($r->query->all())
->withParsedBody($r->isJson() ? $r->json()->all() : $r->post());
}
}
Pairing with a PSR-18 client. If you'd rather depend on Psr\Http\Client\ClientInterface than on GuzzleHttp\Client directly, these objects slot in as the request/response types:
$response = $client->sendRequest(
(new Request('POST', new Uri('https://api.example.com/hooks')))
->withHeader('Content-Type', 'application/json')
->withBody(Stream::create(json_encode($payload)))
);
if ($response->getStatusCode() >= 400) {
throw new \RuntimeException((string) $response->getBody());
}
Bind the client interface in a service provider and this package becomes the only PSR-7 dependency you need to know about in your domain code.
Middleware / pipeline usage. A PSR-15 middleware stack (or a hand-rolled pipeline for webhook handling) can consume the ServerRequest produced by the factory, mutate it immutably, and hand it to your handler. Because every with*() returns a clone, you can safely pass the same base request into several branches of a pipeline without defensive copying.
Converting back out. When you need to return a PSR-7 ResponseInterface from a Laravel route (e.g. a package that emits PSR-7 responses), map status, headers, and body onto an Illuminate\Http\Response in the controller rather than trying to teach Laravel about PSR-7.
Testing. The value objects are trivially constructible in isolation; use them directly in unit tests for services that accept Psr\Http\Message\* so you don't have to boot the framework or spin up a fake HTTP kernel.
composer.json and be prepared to swap to guzzlehttp/psr7 or nyholm/psr7 — the PSR-7 interface means the swap is a namespace change, not a rewrite.guzzlehttp/psr7 transitively through guzzlehttp/guzzle. Installing swlib/http gives you a second set of classes satisfying the same interfaces. That's fine at the interface boundary, but type-hint Psr\Http\Message\RequestInterface, never Swlib\Http\Request, in service signatures — otherwise you've hard-coupled your domain to a dead package.psr/http-message: ^1.0. If another dependency requires ^2.0, Composer will either fail to resolve or install a second psr/http-message major — neither is pleasant. Check composer why psr/http-message before adding it to an existing project.$request->withHeader(...) returns a new object; the original is untouched. In Laravel's mutable-request world this trips people up constantly. Assign the result: $request = $request->withHeader(...).(string) $response->getBody() once, calling it again may yield an empty string. Call $stream->rewind() first, or cast once and cache.Uri will percent-encode and normalize as Guzzle does — double-encoding of already-encoded query strings is a classic surprise. Build query params via withQueryParams() rather than string concatenation.php artisan vendor:publish. If you want a singleton factory, write it yourself in AppServiceProvider; the package intentionally stays out of the framework's way.var_dump/dd on these objects dumps the whole stream and headers; use $request->getMethod(), (string) $request->getUri(), $request->getHeaders() explicitly. For stream contents, (string) $request->getBody() after a rewind().composer dump-autoload; small packages occasionally ship PSR-0-style or mismatched namespaces that need a refresh.How can I help you explore Laravel packages today?