Collection of PSR-7 middlewares.
It is installable and autoloadable via Composer as oscarotero/psr7-middlewares.
- PHP >= 5.5
- A PSR-7 HTTP Message implementation, for example zend-diactoros
- A PSR-7 middleware dispatcher. For example Relay or any other compatible with the following signature:
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
function (RequestInterface $request, ResponseInterface $response, callable $next) {
// ...
}
use Psr7Middlewares\Middleware;
use Relay\RelayBuilder;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\Stream;
//Set a stream factory used by some middlewares
//(Required only if Zend\Diactoros\Stream is not detected)
Middleware::setStreamFactory(function ($file, $mode) {
return new Stream($file, $mode);
});
//Create a relay dispatcher and add some middlewares:
$relay = new RelayBuilder();
$dispatcher = $relay->newInstance([
//Calculate the response time
Middleware::responseTime(),
//Add an Uuid to request
Middleware::uuid(),
//Handle errors
Middleware::errorHandler('error_handler_function')->catchExceptions(true),
//Override the method using X-Http-Method-Override header
Middleware::methodOverride(),
//Block search engines robots indexing
Middleware::robots(),
//Parse the request payload
Middleware::payload(),
//Remove the path prefix
Middleware::basePath('/my-site/web'),
//Remove the trailing slash
Middleware::trailingSlash(),
//Digest authentication
Middleware::digestAuthentication(['username' => 'password']),
//Get the client ip
Middleware::clientIp(),
//Allow only some ips
Middleware::firewall(['127.0.0.*']),
//Detects the user preferred language
Middleware::languageNegotiator(['gl', 'es', 'en']),
//Detects the format
Middleware::formatNegotiator(),
//Adds the php debug bar
Middleware::debugBar($app->get('debugbar')),
//Execute fast route
Middleware::fastRoute($app->get('dispatcher')),
//Minify the result
Middleware::minify()
//Saves the response in a file
Middleware::saveResponse('app/public')
]);
$response = $dispatcher(ServerRequestFactory::fromGlobals(), new Response());
- AuraRouter
- AuraSession
- BasePath
- BasicAuthentication
- Cache
- ClientIp
- Cors
- DebugBar
- DigestAuthentication
- ErrorHandler
- FastRoute
- Firewall
- FormatNegotiation
- LanguageNegotiation
- MethodOverride
- Minify
- Payload
- ReadResponse
- ResponseTime
- Robots
- SaveResponse
- Shutdown
- TrailingSlash
- Uuid
- When
To use Aura.Router as a middleware. You need the 3.x version, compatible with psr-7:
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\AuraRouter;
use Aura\Router\RouterContainer;
//Create the router
$routerContainer = new RouterContainer();
$map = $routerContainer->getMap();
$map->get('hello', '/hello/{name}', function ($request, $response, $myApp) {
//The route parameters are stored as attributes
$name = $request->getAttribute('name');
//You can get also the route instance
$route = AuraRouter::getRoute($request);
//Write directly in the response's body
$response->getBody()->write('Hello '.$name);
//or echo the output (it will be captured and writted into body)
echo 'Hello world';
//or return a string
return 'Hello world';
//or return a new response
return $response->withStatus(200);
});
//Add to the dispatcher
$dispatcher = $relay->getInstance([
Middleware::AuraRouter()
->router($routerContainer) //Instance of Aura\Router\RouterContainer
->arguments($myApp) //(optional) append more arguments to the controller
]);
Creates a new Aura.Session instance with the request.
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\AuraSession;
$dispatcher = $relay->getInstance([
Middleware::AuraSession(),
->factory($sessionFactory) //(optional) Intance of Aura\Session\SessionFactory
->name('my-session-name'), //(optional) custom session name
function ($request, $reponse, $next) {
//Get the session instance
$session = AuraSession::getSession($request);
}
]);
Strip off the prefix from the uri path of the request. This is useful to combine with routers if the root of the website is in a subdirectory. For example, if the root of your website is /web/public
, a request with the uri /web/public/post/34
will be converted to /post/34
.
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::BasePath('/web/public'),
]);
Implements the basic http authentication. You have to provide an array with all users and password:
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::BasicAuthentication()
->users([
'username1' => 'password1',
'username2' => 'password2'
])
->realm('My realm') //(optional) change the realm value
]);
To save and reuse responses based in the Cache-Control: max-age directive and Expires header. You need a cache library compatible with psr-6
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::Cache()
->cache(new Psr6CachePool()) //the psr-6 cache implementation
function($request, $response, $next) {
//Cache the response 1 hour
return $response->withHeader('Cache-Control', 'max-age=3600');
}
]);
Detects the client ip(s).
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\ClientIp;
$dispatcher = $relay->getInstance([
Middleware::ClientIp()
->headers([
'Client-Ip',
'X-Forwarded-For',
'X-Forwarded'
]), //(optional) to change the trusted headers
function ($request, $response, $next) {
//Get the user ip
$ip = ClientIp::getIp($request);
//Get all ips found in the headers
$all_ips = ClientIp::getIps($request);
return $next($request, $response);
}
]);
To use the neomerx/cors-psr7 library:
use Neomerx\Cors\Strategies\Settings
$relay = new RelayBuilder();
$settings = (new Settings())
->setServerOrigin([
'scheme' => 'http',
'host' => 'example.com',
'port' => '123',
]);
$dispatcher = $relay->getInstance([
Middleware::Cors()
->settings($settings)
]);
Inserts the PHP debug bar in the html body. This middleware requires Middleware::formatNegotiator
executed before, to insert the debug bar only in Html responses.
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::formatNegotiator(),
Middleware::DebugBar()
->debugBar(new DebugBar\StandardDebugBar())
]);
Implements the digest http authentication. You have to provide an array with the users and password:
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::DigestAuthentication()
->users([
'username1' => 'password1',
'username2' => 'password2'
])
->realm('My realm') //(optional) custom realm value
->nonce(uniqid()) //(optional) custom nonce value
]);
Executes a handler if the response returned by the next middlewares has any error (status code 400-599). You can catch also the exceptions throwed or even use whoops as error handler.
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\ErrorHandler;
function errorHandler($request, $response, $myApp) {
switch ($response->getStatusCode()) {
case 404:
return 'Page not found';
case 500:
//you can get the exception catched
$exception = ErrorHandler::getException($request);
return 'Server error: '.$exception->getMessage();
default:
return 'There was an error'
}
}
$whoops = new Whoops\Run();
$dispatcher = $relay->getInstance([
Middleware::ErrorHandler()
->handler('errorHandler') //The error handler
->arguments($myApp) //(optional) extra arguments to the handler
->whoops($whoops) //(optional) provide a whoops instance to capture errors and exceptions
->catchExceptions() //(optional) to catch exceptions if you don't use an external library for that
]);
To use FastRoute as a middleware.
use Psr7Middlewares\Middleware;
$router = FastRoute\simpleDispatcher(function (FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/blog/{id:[0-9]+}', function ($request, $response, $app) {
return 'This is the post number'.$request->getAttribute('id');
});
});
$dispatcher = $relay->getInstance([
Middleware::FastRoute()
->router($router) //Instance of FastRoute\Dispatcher
->argument($myApp) //(optional) arguments appended to the controller
]);
Uses M6Web/Firewall to provide a IP filtering. This middleware deppends of ClientIp (to extract the ips from the headers).
See the ip formats allowed for trusted/untrusted options:
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
//needed to capture the user ips before
Middleware::ClientIp(),
//set the firewall
Middleware::Firewall()
->trusted(['123.0.0.*']) //(optional) ips allowed
->untrusted(['123.0.0.1']) //(optional) ips not allowed
]);
Uses willdurand/Negotiation (2.x) to detect and negotiate the format of the document using the url extension and/or the Accept
http header. It also adds the Content-Type
header to the response if it's missing.
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\FormatNegotiator;
$dispatcher = $relay->getInstance([
Middleware::FormatNegotiator()
->defaultFormat('html') //(optional) default format if it's unable to detect. (by default is "html")
->addFormat('pdf', ['application/pdf', 'application/x-download']) //(optional) add new formats and mimetypes
},
function ($request, $response, $next) {
//get the format (for example: html)
$format = FormatNegotiator::getFormat($request);
return $next($request, $response);
}
]);
Uses willdurand/Negotiation to detect and negotiate the client language. You must provide an array with all available languages:
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\LanguageNegotiator;
$dispatcher = $relay->getInstance([
Middleware::LanguageNegotiator()
->languages(['gl', 'en', 'es']), //Available languages
function ($request, $response, $next) {
//Get the preferred language
$language = LanguageNegotiator::getLanguage($request);
return $next($request, $response);
}
]);
Overrides the request method using the X-Http-Method-Override
header. This is useful for clients unable to send other methods than GET and POST:
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::MethodOverride()
->get(['HEAD', 'CONNECT', 'TRACE', 'OPTIONS']), //(optional) to customize the allowed GET overrided methods
->post(['PATCH', 'PUT', 'DELETE', 'COPY', 'LOCK', 'UNLOCK']), //(optional) to customize the allowed POST overrided methods
]);
Uses mrclay/minify to minify the html, css and js code from the responses.
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
//needed to get the format of the response
Middleware::formatNegotiator(),
Middleware::Minify()
->forCache(true) //(optional) only minify cacheable responses
->inlineCss(false) //(optional) enable/disable inline css minification
->inlineJs(false) //(optional) enable/disable inline js minification
]);
Parses the body of the request if it's not parsed and the method is POST, PUT or DELETE. It has support for json, csv and url encoded format.
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::Payload()
->associative(true) //(optional) To generate associative arrays with json objects
function ($request, $response, $next) {
//Get the parsed body
$content = $request->getParsedBody();
return $next($request, $response);
}
]);
Read the response content from a file. It's the opposite of SaveResponse
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::ReadResponse()
->storage('path/to/document/root') //Path where the files are stored
->basePath('public') //(optional) basepath ignored from the request uri
]);
Calculates the response time (in miliseconds) and saves it into X-Response-Time
header:
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::ResponseTime()
]);
Disables the robots of the search engines for non-production environment. Adds automatically the header X-Robots-Tag: noindex, nofollow, noarchive
in all responses and returns a default body for /robots.txt
request.
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::Robots()
]);
Saves the response content into a file if all of the following conditions are met:
- The method is
GET
- The status code is
200
- The
Cache-Control
header does not containno-cache
value - The request has not query parameters.
This is useful for cache purposes
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::SaveResponse()
->storage('path/to/document/root') //Path directory where save the responses
->basePath('public') //(optional) basepath ignored from the request uri
]);
Useful to display a 503 maintenance page. You need to specify a handler.
use Psr7Middlewares\Middleware;
function shutdownHandler ($request, $response, $app) {
$response->getBody()->write('Service unavailable');
}
$dispatcher = $relay->getInstance([
Middleware::Shutdown()
->handler('shutdownHandler') //Callable that generate the response
->arguments($app) //(optional) to add extra arguments to the handler
]);
Removes (or adds) the trailing slash of the path. For example, /post/23/
will be converted to /post/23
. If the path is /
it won't be converted. Useful if you have problems with the router.
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::TrailingSlash()
->addSlash(true) //(optional) to add the trailing slash instead remove
->redirect(301) //(optional) to return a 301 (seo friendly) or 302 response to the new path
->basePath('public') //(optional) basepath
]);
Uses ramsey/uuid to generate an Uuid (Universally Unique Identifiers) for each request (compatible with RFC 4122 versions 1, 3, 4 and 5). It's usefull for debugging purposes.
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\Uuid;
$dispatcher = $relay->getInstance([
Middleware::Uuid()
->version(4) //(optional) version of the identifier (1 by default). Versions 3 and 5 need more arguments (see https://github.com/ramsey/uuid#examples)
->header(false), //(optional) Name of the header to store the identifier (X-Uuid by default). Set false to don't save header
function ($request, $response, $next) {
//Get the X-Uuid header
$id = $request->getHeaderLine('X-Uuid');
//Get the Uuid instance
$uuid = Uuid::getUuid($request);
echo $uuid->toString();
return $next($request, $response);
}
]);
Execute a middleware only if a condition is evaluated as true. This is useful to add middleware only under some circunstances or environments.
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::When()
->condition(getenv('ENV') === 'production') //The condition to be evaluated
->middleware(Middleware::minify()), //The middleware to be executed when the condition is true
//You can use also callables:
Middleware::when()
->condition(function ($request, $response) {
return $request->hasHeader('X-Foo')
})
->middleware(function ($request, $response, $next) {
//your code
return $next($request, $response);
})
]);
If you're using any container compatible with the Container Interoperability Project you can use it to provide instances to some middlewares (routers, debugbar, psr6 cache, etc). To do that, there's the method ->from($container, $id)
available in the following middlewares:
$container = new MyContainerInterop();
$dispatcher = $relay->getInstance([
Middleware::Cache()->from($container, 'cache'),
Middleware::Cors()->from($container, 'cors-settings'),
Middleware::DebugBar()->from($container, 'debug-bar'),
Middleware::AuraSession()->from($container, 'session-factory'),
Middleware::FastRouter()->from($container, 'fast-router'),
]);
By using containers instead creating and passing the instances directly, these instances will be created (for example $container->get('fast-router')
) only if the middleware is executed.
New middlewares are appreciated. Just create a pull request.