symfony/psr7-pack
Symfony PSR-7 pack is a Composer metapackage that installs PSR-7 HTTP message interfaces, a lightweight PSR-7 implementation, and Symfony’s PSR HTTP message bridge for smooth interoperability.
symfony/psr7-pack is a Composer pack (metapackage), not a library. There is no PHP code, no service provider, no config file — the entire package is a composer.json that pulls in a coherent PSR-7 stack. You interact with it exclusively through Composer.
First steps:
composer require symfony/psr7-pack
composer show symfony/psr7-pack # what it actually depends on
cat vendor/symfony/psr7-pack/composer.json
composer why nyholm/psr7 # and php-http/discovery
In a Laravel app you will usually get this package transitively (a PSR-15 middleware, an OAuth2 server, a webhook SDK, a PSR-18 HTTP client) rather than requiring it by hand. First use case: a third-party package type-hints Psr\Http\Message\ServerRequestInterface, ResponseInterface, or Psr\Http\Message\RequestFactoryInterface, and you need working PSR-7 objects plus PSR-17 factories inside a Laravel request lifecycle.
What changed in v1.3.0: the pack swapped nyholm/psr7 for php-http/discovery. php-http/discovery does not ship a PSR-7 implementation — it provides the discovery API (Psr17FactoryDiscovery, Psr18ClientDiscovery, …) and a Composer plugin that auto-adds a concrete implementation when one is missing. That is a behavioural change disguised as a minor release.
Pin the implementation in your root composer.json. Packs are convenience for applications; your app should still be explicit. Keep nyholm/psr7 (or guzzlehttp/psr7) in require so the pack's dependency churn can never silently change your runtime.
Bind discovered factories once, in a service provider. Don't scatter Psr17FactoryDiscovery::find*() calls across controllers — resolve at boot and bind interfaces:
$this->app->singleton(RequestFactoryInterface::class,
fn () => Psr17FactoryDiscovery::findRequestFactory());
$this->app->singleton(ResponseFactoryInterface::class,
fn () => Psr17FactoryDiscovery::findResponseFactory());
$this->app->singleton(StreamFactoryInterface::class,
fn () => Psr17FactoryDiscovery::findStreamFactory());
This gives you one swap point, fails fast at boot rather than mid-request, and makes tests trivial (bind a mock).
Bridging Laravel ⇄ PSR-7. Add symfony/psr-http-message-bridge separately (it is not in this pack) and build a PsrHttpFactory from the three factories above to turn Illuminate\Http\Request into a ServerRequestInterface.
Approve the Composer plugin before CI. Composer 2.2+ blocks plugins by default:
composer config allow-plugins.php-http/discovery true
Treat pack bumps as dependency-graph changes. Pin the pack to 1.2.* if you depend on nyholm/psr7 existing, or upgrade deliberately with composer update symfony/psr7-pack -W and read the composer.lock diff.
Libraries you publish should require psr/http-message, psr/http-factory, or psr/http-client directly — never a pack. Packs are an application/Flex concern.
Stream large files, don't string them. StreamFactoryInterface::createStreamFromFile($upload->getRealPath()) instead of loading getContents() into memory.
composer update silently. Apps on ^1.2 pick it up automatically; if nothing else supplies a PSR-7 implementation and the discovery plugin doesn't run, you get Http\Discovery\Exception\NotFoundException: No PSR-17 factory detected at runtime. Fix: composer require nyholm/psr7 explicitly.composer install -n, --no-plugins, or a locked-down Docker build means discovery never installs an implementation. The failure surfaces only in the container, not locally.guzzlehttp/psr7 and nyholm/psr7 present, the chosen implementation can differ between environments — different stream/header/multipart edge-case behaviour. Pin exactly one.find*() returns, or rely on the boot-time bindings above.withHeader() returns a clone — a very common Laravel-side bug when mutating inside a loop.replace/provide in your root composer.json, a Flex recipe, or wrap discovery in your own factory class.composer require it just for curiosity in a lean app — it drags in a PSR stack you may never call. Let consumers pull it transitively, and audit with composer why.How can I help you explore Laravel packages today?