Download the PHP package gilsegura/psr-messages-bundle without Composer
On this page you can find all versions of the php package gilsegura/psr-messages-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download gilsegura/psr-messages-bundle
More information about gilsegura/psr-messages-bundle
Files in gilsegura/psr-messages-bundle
Package psr-messages-bundle
Short Description Symfony integration for gilsegura/psr-messages: registers the parsers, schema resolver, response factories and PSR-15 middlewares as services, wires the Symfony<->PSR bridge, and adds a #[Pipeline] attribute plus an exception subscriber for JSON:API error responses.
License MIT
Informations about the package psr-messages-bundle
PSR MESSAGES BUNDLE
Symfony bundle that turns a controller into a PSR-15 pipeline. Each controller
declares, with a #[Pipeline] attribute, the middlewares that should run around
it — body parsing, header/query parsing and request/response validation — using
the real middlewares from the gilsegura/psr-messages, gilsegura/psr-validator
and gilsegura/psr-server packages. The controller is the terminal PSR-15
handler; the pipeline only adds middlewares in front of it.
How it works
A kernel.controller subscriber reads the #[Pipeline], builds the
MiddlewareRunner once per controller class (memoized for the lifetime of the
worker), converts the Symfony request to PSR-7, runs the pipeline and converts
the PSR-7 response back. A controller with no #[Pipeline] simply runs with no
middlewares — it is still the terminal handler.
Installation
Register the bundle (or let Symfony Flex do it):
Writing a controller
Extend AbstractController and declare the pipeline. Controllers are
autoconfigured as Symfony controllers, so you only route to them as usual.
The order of the configurations in #[Pipeline] is the order the middlewares
run in.
Building the response — presenting a model into a resource, applying sparse
fieldsets with withFieldset(), assembling a document and paginating with
Pagination — is the job of the psr-messages package; see its README for the
document model. This bundle only wires that toolkit into Symfony: the pipeline,
schema validation, and the PSR bridge.
The two kinds of schema
The bundle deliberately resolves schemas in two different ways, because they are two different things:
- Parsing (
ParsedBodyConfiguration,HeadersConfiguration,QueryParamsConfiguration) references aSchemaInterfaceclass by its class-string. That class deserializes the raw input into a typed object; it is resolved inline (no container needed). - Validation (
BodyValidationConfiguration, etc.) references a JSON Schema file by an alias. The bundle scans the schema directory, registers aFileFactoryservice per.json, and the validation factory resolves it by that alias.
So a parser turns the body into a CreateUserRequest, while validation checks
the raw body against create-user.json — independent pieces.
Schemas (validation)
JSON Schema files live in config/schemas/ by default. The file name (without
.json) is the alias you pass to the validation configurations:
Override the directory:
The directory is registered as a resource, so adding or changing a .json
invalidates the container in dev.
Request vs response validation
Validation runs in two halves. Request validation is the trust boundary and always runs — it checks the incoming, untrusted payload against its schema. Response validation checks the application's own output against its schema; it is a development safety net that is redundant in production once the presenters are trusted, and it is the costlier half of validation.
Response validation is therefore wired to kernel.debug: it runs in dev and test
and is off in production. No configuration is needed — the
ValidationMiddlewareFactory receives kernel.debug and builds an empty response
validator in prod, which passes the response through untouched. The phase
breakdown below shows this is the single largest warm-path saving in prod.
Each schema is read and compiled from disk only once per worker: a
CachedSchemaFactory memoizes the compiled schema, so validating any number of
requests within a worker reuses the same compiled schema rather than recompiling
it per request.
Mapping exceptions to HTTP status
The bundle is exception-agnostic: it ships no status mappers. Provide your own by
implementing ExceptionStatusMapper — it is autoconfigured by tag and collected
by the exception subscriber, which renders a JSON:API error response. The first
mapper that returns a Status wins; unmapped exceptions fall back to 500.
Adding your own middleware
Implement a MiddlewareConfiguration (plain data that names its factory) and a
MiddlewareFactory (an invokable that builds the PSR-15 middleware). The factory
is autoconfigured by tag, so it is reachable from any #[Pipeline] without
touching the bundle.
Then reference it in a pipeline: #[Pipeline(new RateLimitConfiguration(120), ...)].
Performance
The reflection of the #[Pipeline] and the MiddlewareRunner are built once per
controller class and reused for the lifetime of the worker. The first request to
a controller pays that one-off cost; every later request is a cached lookup plus
the actual middleware work (validation/parsing).
Benchmarks
Two benchmarks are included. Run both with Xdebug off — Xdebug in develop or coverage mode inflates timings several times over.
make benchmark reports the whole-request overhead: the cold cost (first call:
reflection + building the runner) and the warm, steady-state cost once the runner
is memoized per controller.
make benchmark-phases breaks the warm path into its stages (PSR bridge in,
header/body parsing, request validation, response validation, controller,
PSR bridge out) so you can see where the time goes before optimizing anything.
Each middleware is timed for its own work only, so the phases are additive. Pass
the environment to compare dev and prod:
What the breakdown shows
On a POST /things (body + request and response validation), the warm path is
dominated by validation; the document model (presenting and serializing in the
controller) is a small fraction:
Two conclusions drive where it is worth optimizing:
- Disabling response validation in prod is the single biggest saving (~30% of
the warm pipeline) and it is already on by default via
kernel.debug. - Request validation is the irreducible cost — the schema is already compiled once per worker, so what remains is the intrinsic work of validating the input against the compiled schema. It only shrinks by simplifying the schemas themselves; measure before assuming it helps.
The phase total is only the pipeline. End to end, a request is dominated by the
Symfony HttpKernel (routing, events, kernel lifecycle); the pipeline is a small
share of it. The controller — where presenters, sparse fieldsets and the document
model live — is consistently the cheapest measured phase, so optimizing it further
is not worthwhile.
License
MIT. See LICENSE.
All versions of psr-messages-bundle with dependencies
gilsegura/psr-messages Version ^1.0
gilsegura/psr-server Version ^1.0
gilsegura/psr-validator Version ^1.0
php-http/discovery Version ^1.19
psr/container Version ^2.0
psr/http-factory-implementation Version ^1.0
symfony/dependency-injection Version ^7.4 || ^8.1
symfony/event-dispatcher Version ^7.4 || ^8.1
symfony/http-kernel Version ^7.4 || ^8.1
symfony/psr-http-message-bridge Version ^7.4 || ^8.1