Using Expressive’s middleware in Penny Framework
If we are using Penny Framework, we can use Expressive‘s middleware. We can do that with passing callable $next
that handle request and response with Penny Event. The $next
is simply like the following:
$next = function($request, $response) use ($event) { $event->setRequest($request); $event->setResponse($response); };
and we are ready to go! Let’s try to do real example. We need to use los/basepath
that can apply “base path” in our application that point to classic-app/public
and we can to call: /classic-app/public
after our host.
$ composer require los/basepath
We need to invoke the middleware to make it called. So, if we do in ‘bootstrap’ event, we can do:
$eventManager->attach('bootstrap', function($event) { $next = function($request, $response) use ($event) { $event->setRequest($request); $event->setResponse($response); }; $basepathMiddleware = new \LosMiddleware\BasePath\BasePath( '/classic-app/public' ); $basepathMiddleware( $event->getRequest(), $event->getResponse(), $next ); });
This is the case if we are using penny skeleton app and we install under ‘classic-app’ directory, and the “front controller” is in public/index.php
.
For routed middleware, we can just do the register the middleware as controller:
// config/di.php // ... 'router' => function () { return \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) { $r->addRoute( 'GET', '/', ['App\Controller\IndexController', 'index'] ); $r->addRoute( 'GET', '/api/ping', ['App\Controller\PingAction', '__invoke'] ); }); }, // ...
That’s it 😉
Middleware implementation in Penny Framework
Penny is a microframework that has different idea for accomplishing middleware implementation. If we found other frameworks that implements this signatures:
function ($request, $response, $next = null);
In Penny Framework, Middleware implemented via listener that has priority that we call before or/and after. If we need to initialize application flow, you may use ‘bootstrap’ event:
// config/di.php use App\Middleware\BootstrapMiddleware; use function DI\decorate; return [ 'event_manager' => decorate(function($eventManager, $container) { $eventManager->attach( 'bootstrap', [$container->get(BootstrapMiddleware::class), 'bootstrap'] ); // other attach here... return $eventManager; }), // other service definitions here... ];
We want the middleware executed in all application flow in all controller in the end? use ‘*’ and negative priority:
// config/di.php use App\Middleware\AllEnderMiddleware; use function DI\decorate; return [ 'event_manager' => decorate(function($eventManager, $container) { $eventManager->attach( '*', [$container->get(AllEnderMiddleware::class), 'end'], -1 ); // other attach here... return $eventManager; }), // other service definitions here... ];
Wanna handle for specific controller’s action ? Use following usage:
// config/di.php use App\Controller\IndexController; use App\Middleware\IndexMiddleware; use function DI\decorate; return [ 'event_manager' => decorate(function($eventManager, $container) { // before ... $eventManager->attach( IndexController::class.'.index', [$container->get(IndexMiddleware::class), 'pre'], 1 ); // after ... $eventManager->attach( IndexController::class.'.index', [$container->get(IndexMiddleware::class), 'post'], -1 ); // other attach here... return $eventManager; }), // other service definitions here... ];
If we use default Penny EventManager implementation, which is usage Zend Framework 2 EventManager, higher priority will be served first, lower priority will be served later.
If you are familiar with CakePHP, there is already a CakePHP Event implementation, that will serve lower priority first, higher later.
For callable listener, we may create listener service like the following:
namespace App\Middleware; class IndexMiddleware { public function pre($event) { $request = $event->getRequest(); $response = $event->getResponse(); // manipulate request or/and response here $request = $request->withAttribute('foo', 'fooValue'); // apply manipulated request or/and response $event->setRequest($request); $event->setResponse($response); } public function post($event) { $request = $event->getRequest(); $response = $event->getResponse(); // manipulate request or/and response here $response = $response->withHeader('X-Powered-By', 'JAVA'); // apply manipulated request or/and response $event->setRequest($request); $event->setResponse($response); } }
We can then register the middleware in our Service Container:
//config/di.php use App\Middleware\AllEnderMiddleware; use App\Middleware\BootstrapMiddleware; use App\Middleware\IndexMiddleware; use function DI\object; return [ // ... AllEnderMiddleware::class => object(AllEnderMiddleware::class), BootstrapMiddleware::class => object(BootstrapMiddleware::class), IndexMiddleware::class => object(IndexMiddleware::class), ];
leave a comment