Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Http Laravel Package

psx/http

PSX HTTP is a lightweight PHP HTTP component for handling requests and responses, including PSR-7-style message abstractions and utilities for building HTTP-aware applications. It fits the PSX framework ecosystem and can be used standalone for routing-adjacent request/response work.

View on GitHub
Deep Wiki
Context7

Getting Started

psx/http ships only interfaces — there is no runtime you can composer require and call. Treat it as a contract package, not a library. The entire "documentation" is the src/ tree, so step one is to read it:

composer require psx/http
ls vendor/psx/http/src

You will find roughly three groups of contracts:

  • Messages — MessageInterface, RequestInterface, ResponseInterface, StreamInterface, HeadersInterface, UriInterface, EnvironmentInterface
  • Middleware — MiddlewareInterface, StackInterface (a middleware receives both a request and a response plus a $next callable)
  • Client — ClientInterface, which turns a RequestInterface into a ResponseInterface

Look at the interfaces first, not at the psx framework packages. The psx framework (psx/api, psx/framework) is what actually implements these contracts, and it is a full-stack framework you are not going to bolt onto Laravel. The realistic value for a Laravel developer is the opposite direction: implement these interfaces as thin adapters around Laravel's HTTP layer so psx-shaped code (third-party middleware, an API SDK, a legacy psx controller) can run inside a Laravel route.

First use case, a minimal inbound adapter:

namespace App\Http\Psx;

use Illuminate\Http\Request as LaravelRequest;
use PSX\Http\RequestInterface;
use Psr\Http\Message\StreamInterface;

final class RequestAdapter implements RequestInterface
{
    public function __construct(private LaravelRequest $request) {}

    // implement each interface method by delegating:
    // getMethod()      => $this->request->getMethod()
    // getUri()         => UriAdapter::fromSymfony($this->request->getUri())
    // getHeaders()     => $this->request->headers->all()
    // getBody()        => StreamAdapter::fromString($this->request->getContent())
}

Bind it once and the rest of your code can type-hint RequestInterface without ever importing Illuminate\Http\Request:

// app/Providers/PsxServiceProvider.php
$this->app->bind(RequestInterface::class, fn ($app) =>
    new RequestAdapter($app['request'])
);

Verify method signatures against vendor/psx/http/src before writing the adapter — the contracts are small, and a few (especially around header casing and stream handling) differ from what PSR-7 habits would suggest.

Implementation Patterns

Inbound adapter + route macro. The highest-value pattern is mounting psx-style middleware behind a single Laravel route. Build the psx request once, run the stack, and convert the resulting ResponseInterface back into something Laravel can return:

Route::any('/legacy/{any?}', function (RequestInterface $psxRequest) {
    $response = (new Stack($middleware))->handle($psxRequest);
    return response(
        (string) $response->getBody(),
        $response->getStatusCode(),
        $response->getHeaders()
    );
})->where('any', '.*');

The conversion layer (ResponseAdapter::toLaravel()) is where you centralize header normalization and body streaming — keep it boring and tested.

Bidirectional middleware bridge. psx middleware sees request and response, Laravel middleware sees only the request. Don't try to unify the two pipelines. Instead, wrap psx middleware as a Laravel middleware that owns the response object:

final class RunPsxStack
{
    public function __construct(private array $stack) {}

    public function handle($request, Closure $next)
    {
        $response = $next($request);          // let Laravel finish first
        $psxResponse = ResponseAdapter::fromLaravel($response);
        $psxResponse = (new Stack($this->stack))
            ->handle(RequestAdapter::fromLaravel($request), $psxResponse);

        return ResponseAdapter::toLaravel($psxResponse);
    }
}

This "post-process" shape matches how psx middleware actually behaves (it decorates a response) and avoids re-implementing Laravel's pipeline.

Outbound client abstraction. If you want to depend on ClientInterface rather than the Http facade (useful for swapping to a different transport or for fake-based tests), implement it over the facade:

final class LaravelHttpClient implements ClientInterface
{
    public function request(RequestInterface $request): ResponseInterface
    {
        $response = Http::withHeaders($request->getHeaders())
            ->send($request->getMethod(), (string) $request->getUri(), [
                'body' => (string) $request->getBody(),
            ]);

        return new ResponseAdapter($response->status(), $response->headers(), $response->body());
    }
}

Bind the interface in a provider, and mock the interface in tests instead of faking the facade.

Fakes over mocks for unit tests. Because these are narrow interfaces, a hand-written FakeResponse implements ResponseInterface is often faster and more readable than a PHPUnit mock, and it survives interface additions better than a mock with a dozen ->method() calls.

Decorator layering. The interfaces compose cleanly: a caching client decorator, a logging middleware, a header-normalizing message decorator. This is the one place psx/http genuinely shines — the contracts are small enough that a decorator is ~30 lines.

Keep the adapter layer thin and in one namespace. App\Http\Psx\{RequestAdapter, ResponseAdapter, UriAdapter, StreamAdapter}. Everything else in your app should speak Laravel. If PSX\ imports start leaking into controllers, the abstraction is costing you more than it returns.

Gotchas and Tips

  • Not PSR-7, not PSR-15, not PSR-18. There is no Psr\Http\Message inheritance and no interoperability with Guzzle, Symfony HttpClient, or Laravel's own PSR bridges. You cannot hand a psx/http request to a package expecting PSR-7. Plan to convert at the boundary, once, in the adapter.
  • Interfaces only. composer require gives you zero classes your app can instantiate. If class_exists() fails on something you expected, you are looking for an implementation in psx/framework / psx/api, which pulls in a much larger dependency graph. For a Laravel app, writing your own adapters is usually cheaper.
  • Mutability, not immutability. The psx message contracts use setter-style methods (setHeader, setBody) rather than PSR-7's withHeader. Code written from PSR-7 muscle memory will silently mutate shared instances. If you need immutability, implement it in your adapter (clone-on-write) and document it.
  • Middleware signature asymmetry. psx middleware takes (request, response, next) — the response is passed in, not returned by $next. Anyone porting Laravel middleware will get this backwards and produce a doubled body or a lost status code. Sketch the flow on paper before coding.
  • Streams are single-shot. Consuming getBody() twice (once for logging, once for the response) yields an empty body. Buffer to a string in the adapter, or rewind explicitly if the stream reports seekable. This is the single most common cause of "the JSON response is empty in one environment only."
  • Exceptions instead of status codes. psx-style code often throws status-code exceptions (the PSX\Http\Exception family) rather than returning a 4xx response. In Laravel those become 500s unless you catch and map them in Handler::render or in your adapter. Add a mapping before you ship.
  • Header normalization drift. Symfony/Laravel headers are arrays keyed by lowercase name; psx contracts often expect a single value or a different casing. Centralize this in HeadersAdapter and write one test per edge case (multi-value Set-Cookie, empty header, numeric header name).
  • Debugging is var_dump hostile. A dd() on a request implementation can dump megabytes of stream and environment data. Log a deliberate context array (method, uri, header names, body length) rather than the object.
  • Adoption is effectively zero (3 stars, 0 dependents) and the API surface is whatever the psx framework needs. Pin the version in composer.json, read the changelog on every bump, and do not assume semver discipline for the interfaces — an added method is a breaking change for your adapter.
  • Extension point worth knowing: the interfaces themselves. If a psx interface is missing a method you need, extend it in your own namespace (App\Http\Psx\ExtendedRequestInterface extends RequestInterface) rather than forking the package. Your adapters implement the extended contract; psx code still sees the base one.
  • When not to use it: if you only need "an HTTP client I can swap," PSR-18 plus php-http/guzzle7-adapter gives you far more ecosystem for the same effort. Reach for psx/http only when you are explicitly interoperating with psx-based code or artifacts.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource