Middleware
Middlewares are ways of adding to the Request
or the Response
, or in fact
completely replace them, and doing so without changing code of the intended
Routed action. It is a good system for adding logging or security checks or
tidying up the response. Anything that should be applied to many actions but
is not part of handling the specifics of the Routed action.
How do they work?
Middlewares are callables with the following interface:
function(Request $request, Chain $chain): Response;
They recieve the Request
(either the original, or a different one created by
a middleware before them in the chain) and the Chain
with contains the stack
of all the middlewares and the Route
at the end. It knows what to call next,
you must do to create the Response
, and you do that like this:
$response = $chain->next($request, $chain);
The exception to this is ofc if your middleware has decided to “escape hatch”
the dispatch and create it’s own Response
to return instead. This possibility
is one of the reasons why the order is important to keep in mind.
In the end of all middlewares they must return the created Response
.
Keep in mind that both Request
and Response
are immutable and also both
extend Message
.
So an “empty” middleware closure that you start from looks like this:
use alkemann\h2l\{ Environment, Request, Response, util\Chain };
$log_middle = function(Request $request, Chain $chain): Response {
// Insert BEFORE activity here
/** @var Response $response */
$response = $chain->next($request, $chain);
// Insert AFTER activity here
return $response;
};
Activating Middlewares
The Dispatch
is responsible for running the process that converts a
Request
into a Response
. First you set the Route (normally using the
Router
in dispatch->setRouteFromRouter()
and then you ask it for the
Response
. It will create a stack off all middlewares and adding the Routed
action at the end. This allows all middleware to act before the “action”,
and also to reacto the response as it is passed back up the chain.
To add Middlewares to the Dispatch
$my_middleware = function(Request $r, Chain $c): Response { /* ... */ };
$dispatch->registerMiddle($my_middleware);
You can also register an array of all your middlewares at once as the argument
for registerMiddle
is callable ...$callables
:
$middlewares = [
function(Request $r, Chain $c): Response { /* ... */ };
function(Request $r, Chain $c): Response { /* ... */ };
];
$dispatch->registerMiddle($middlewares);
Using Environment
You can (and probably should) have a config file with all your middlewares, this allows them to be hosted together and ensures that the order makes the most sense (order matters). Since they are called in the order they are added.
This way you can also use a different set of middlewares depending on the active enviroment (only log on dev/local for example).
Example config/middelwares.php
use alkemann\h2l\{ Environment, Request, Response, util\Chain };
$log_middle = function(Request $r, Chain $c): Response { /* .. */ };
Environment::addMiddle($log_middle, Environment::DEV);
Environment::addMiddle($log_middle, Environment::LOCAL);
Environment::addMiddle(function(Request $request, Chain $chain): Response {
/* .. */
}, Environment::ALL);
NOTE: This only sets up the potential middlewares, it DOES NOT ACTIVATE them. You have to do the next step for that.
This is then activated in index.php
:
// After `Environment::setCurrent` has ensured the right one is active
// Before `$response = $dispatch->response();` is called
$dispatch->registerMiddle(...Environment::middlewares());
Example: Log Request/Response
Here you can see a middleware that demonstrates the ability for “before” and “after” actions in to log the request as it came in and the response going out.
use alkemann\h2l\{ Environment, Request, Response, util\Chain, Log };
// Log request and response
$log_middle = function(Request $request, Chain $chain): Response {
Log::info("== REQUEST == URL[" . $request->url() . "] ==");
/** @var Response $response */
$response = $chain->next($request, $chain);
$class = get_class($response);
$code = $response->code();
Log::info("== RESPONSE == TYPE[{$class}] == CODE[{$code}] ==");
return $response;
};
You can also check out the Tidy Example for another example on replacing the Response.