oscarotero/psr7-middlewares
A collection of PSR-7 middleware for PHP, including authentication, caching, CORS, HTTPS, and routing. Works with any PSR-7 compatible framework or application.
composer require oscarotero/psr7-middlewares. Because the package is archived and last released in 2017, pin the version, check PHP/PSR-7 compatibility, and audit transitive dependencies before using it in a maintained app.($request, $response, $next) returning a ResponseInterface. They are not PSR-15 process() middleware. A middleware may short-circuit by returning a response, or call $next($request, $response) and modify the downstream response.src/Middleware/ for the available middleware classes, src/Middleware.php for the static factory, and the tests for each middleware. The tests are the most reliable documentation for config array keys and request-attribute names.A minimal first stack:
use Oscarotero\Psr7Middlewares\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
$stack = [
Middleware::responseTime(),
Middleware::clientIp(['10.0.0.0/8']),
Middleware::errorHandler(),
Middleware::cors([
'origins' => ['https://app.example.com'],
'methods' => ['GET', 'POST', 'OPTIONS'],
'headers' => ['Content-Type', 'Authorization'],
]),
Middleware::payload(),
Middleware::formatNegotiator(),
function (ServerRequestInterface $request, ResponseInterface $response, callable $next) {
$response->getBody()->write(json_encode([
'ip' => $request->getAttribute('client-ip'),
'format' => $request->getAttribute('format'),
]));
return $response->withHeader('Content-Type', 'application/json');
},
];
Then dispatch with a queue runner:
$dispatch = function (
ServerRequestInterface $request,
ResponseInterface $response,
array $stack
) use (&$dispatch) {
$middleware = array_shift($stack);
if ($middleware === null) {
return $response;
}
return $middleware($request, $response, function ($request, $response) use (&$dispatch, $stack) {
return $dispatch($request, $response, $stack);
});
};
$response = $dispatch($request, $response, $stack);
Emit the final response with Diactoros/Laminas, Guzzle, or whatever PSR-7 implementation you already use.
ErrorHandler/Whoops outermost so exceptions from all inner middleware are caught. Put Https, ClientIp, and proxy handling early. Put Cors before authentication so preflight OPTIONS requests are not rejected. Put Payload and negotiation before the handler. Response-transformers wrap downstream and run again after $next.Middleware::cors(), Middleware::payload(), Middleware::formatNegotiator(), etc. return configured instances. Build named stacks — api, web, admin — from config rather than scattering new calls.$request->getAttribute(...); do not reach into superglobals. Document the attribute names you depend on because they are informal contracts.Firewall, ClientIp, and Https. Return 401 with WWW-Authenticate for unauthenticated requests and 403 for authenticated-but-forbidden requests. Keep trusted-proxy configuration consistent with your edge/load balancer.OPTIONS short-circuits before auth and routing. Emit Vary: Origin when reflecting an origin. Never combine credentialed CORS with Access-Control-Allow-Origin: *.Payload to parse JSON/form bodies into an attribute, then validate in your handler or a dedicated validation middleware. Use FormatNegotiator and LanguageNegotiator to select response format/language, and set matching Content-Type and Vary: Accept, Accept-Language headers.ResponseTime, AccessLog, and Uuid are useful for correlating a request across the stack. Decide whether you want to log cached and error responses, and place the logger accordingly.RemoveHeaders, Csp, Minify, and redirect/trailing-slash middleware are best kept near the edge of the stack. Keep business logic in a final handler; middleware should remain cross-cutting.$next, and assert request attributes, status codes, and headers. Test stack order with a middleware that records entry/exit.process(ServerRequestInterface, RequestHandlerInterface), you need an adapter or a compatibility dispatcher. Do not mix the two contracts in one stack without wrapping.TrustProxies, CORS, auth, throttle, cache headers) is simpler and faster.X-Forwarded-For/X-Forwarded-Proto from known proxies. Blindly trusting client headers allows IP spoofing and HTTPS-detection bypass. Keep this configuration aligned with your load balancer and Laravel TrustProxies.echo from middleware. Do not assume getBody() is a string; use stream factories or withBody(). Avoid writing to the same body twice when wrapping downstream responses.400.Vary dimension: Accept, Accept-Language, Origin, and Authorization where relevant. Never cache authenticated or personalized responses unless the key includes the user/tenant. Invalidate on writes.PhpSession assumes PHP session behavior: locking, headers, and global state. It is a poor fit for stateless APIs and can conflict with Laravel’s session handling. Prefer an explicit session service in Laravel.ErrorHandler to map exceptions to JSON or HTML responses, log them, and keep it outermost in the stack.($request, $response, $next). Compose or wrap existing middleware rather than editing vendor code. If you need long-term maintenance, fork and pin the package, or migrate the stack to PSR-15 equivalents.How can I help you explore Laravel packages today?