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 Message Laravel Package

berlioz/http-message

berlioz/http-message offers PSR-7 compliant HTTP message classes for PHP: immutable requests, responses, URIs, streams, and uploaded files. It works seamlessly with Laravel and other frameworks, giving developers a clean, standards-based foundation for handling HTTP requests and responses.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package directly, or let it come in transitively with berlioz/http-core:

composer require berlioz/http-message

First place to look: src/Http/Message/ in the monorepo (README.md and the HttpFactory class). Since this is a pure PSR-7/PSR-17 component with zero framework dependencies, you can read the whole surface area in one sitting — it is factory + message classes + streams + body parsers.

The one object you need: Berlioz\Http\Message\HttpFactory implements all six PSR-17 factory interfaces (RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface). Type-hint against the PSR interfaces in your own code, and instantiate HttpFactory only at the composition root:

use Berlioz\Http\Message\HttpFactory;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;

$factory  = new HttpFactory();
$response = $factory->createResponse(200)
    ->withHeader('Content-Type', 'application/json');
$stream   = $factory->createStream(json_encode(['ok' => true]));

return $response->withBody($stream);

First use case to try: build a ServerRequest from globals, read the parsed body, and return a JSON Response. That single loop exercises factories, streams, body parsing, and header immutability — i.e. 90% of what you'll do day-to-day. Once that works, drop the same factory into a PSR-15 middleware stack or a PSR-18 client call and you've validated interoperability.

Implementation Patterns

1. Factory-as-service, interfaces everywhere. Register HttpFactory in the container (Berlioz's ServiceContainer auto-wires by reflection, so binding the six PSR-17 interfaces to the one concrete class is a two-line provider). Every controller, middleware, and listener then depends on ResponseFactoryInterface / StreamFactoryInterface rather than the concrete class. This keeps your code swappable with nyholm/psr7, guzzlehttp/psr7, etc.

2. Controller return conventions in http-core. You rarely build responses by hand inside a controller. HttpApp's ControllerHandler maps return values: ResponseInterface passes through, null/empty → 204, scalar → 200, anything else → JSON. Know this map, because it decides when you must return a ResponseInterface (custom headers, non-200 statuses, streaming) and when you can just return $array;.

3. Immutable message pipelines. PSR-7 messages are immutable, so the idiomatic Berlioz middleware pattern is to return the new instance, never mutate in place:

final class JsonErrorMiddleware implements MiddlewareInterface
{
    public function __construct(private ResponseFactoryInterface $factory, private StreamFactoryInterface $streams) {}

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        try {
            return $handler->handle($request);
        } catch (\Throwable $e) {
            return $this->factory->createResponse(500)
                ->withHeader('Content-Type', 'application/json')
                ->withBody($this->streams->createStream(json_encode(['error' => $e->getMessage()])));
        }
    }
}

4. Body parsing is pluggable. http-message ships body parsers so $request->getParsedBody() is useful without hand-rolling json_decode. For content types the default parsers don't cover (e.g. application/x-ndjson, vendor +json suffixes), register a custom parser rather than decoding inside the controller — that keeps parsing testable and applied uniformly to every route.

5. Streaming and file responses. Use StreamFactoryInterface::createStreamFromFile() / createStreamFromResource() instead of file_get_contents() when serving downloads or proxying large payloads. Pair with UploadedFileFactoryInterface for tests, so you can exercise upload handling without a real HTTP request.

6. Testing. Because there's no framework coupling, your unit tests can construct requests directly:

$request = (new HttpFactory())->createServerRequest('POST', '/api/items')
    ->withHeader('Content-Type', 'application/json')
    ->withBody((new HttpFactory())->createStream('{"name":"widget"}'));

This is the biggest ergonomic win — you can assert on middleware and controllers with zero boot of Core, HttpApp, or the router.

7. Interop with the client. berlioz/http-client (PSR-18) and this package share the same PSR-7 message classes, so responses from Session/client calls can be returned directly from a controller by http-core, or fed into HtmlSelector for scraping. That symmetry is a real workflow win when writing proxy/aggregator endpoints.

Gotchas and Tips

  • PSR-7 immutability bites in loops. $response->withHeader(...) returns a new object; the old one is untouched. In middleware chains, forgetting $request = $request->withAttribute(...) silently drops your data. Always reassign.
  • Body streams are consumed once. A StreamInterface has a cursor; reading it twice returns an empty string unless you rewind(). If two middlewares inspect the raw body, the second sees nothing. Either cache the parsed body via withParsedBody() or rewind explicitly.
  • withBody() does not rewind. After $response->withBody($stream), the stream position is wherever you left it. If you wrote to it, rewind() before returning or the client gets an empty body.
  • Header casing. PSR-7 requires case-insensitive lookup but preserves the original casing for output. Don't rely on getHeaderLine('Content-Type') === $arrayKey. Use the message API, never getHeaders()[$key].
  • Factory methods return interfaces, not the concrete class. IDE autocompletion for Berlioz-specific extensions (extra parsers, specialized streams) won't appear if you type-hint ResponseFactoryInterface. Bind the concrete class in DI when you need the extra methods; otherwise stick to interfaces.
  • Config quirks are elsewhere. This package holds no config keys — nothing under berlioz.* touches it. If you're debugging body-parser or stream behaviour, look at how the container wires HttpFactory in http-core, not at config.default.json.
  • Debug output. Berlioz's DebugHandler captures request/response data for the debug toolbar, but it hooks in at http-core. When dumping messages during local dev, json_encode($request) won't work — use the __toString() for headers plus (string) $body for content.
  • Extension points to remember: pluggable body parsers (register new content-type handlers), specialized streams (custom write/read behaviour), and the six PSR-17 factory interfaces (swap the whole implementation without touching call sites). These are the three seams worth learning if you're customising.
  • Ecosystem reality check. This package has ~0 dependents and a low stars count. That's not a quality problem — it's a batteries-included-framework-internals component. If you're using Berlioz, use it; if you're not, nyholm/psr7 or guzzlehttp/psr7 have larger communities, more StackOverflow answers, and identical PSR-17 behaviour. The only Berlioz-specific reason to adopt it is that http-core, http-client, and html-selector are typed against it.
  • PHP >= 8.2, PSR-12, declare(strict_types=1) required. If you contribute or extend, match the license header block and one-class-per-file convention — the monorepo enforces it in review.
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