Application

Learn all the different ways you can add a middleware in BitFrame

Add Middleware

Using A MiddlewareInterface Instance

By default, if the middleware implements \Psr\Http\Server\MiddlewareInterface, the process() method is invoked (as per the PSR-15 spec), for example:

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$app = new \BitFrame\Application;

/* 1: define controller class */
 
 // create middleware instance to run
 $middleware = new SomeMiddleware;

// 2: run middleware using the PSR-15 'process' method
$app->run([
    \BitFrame\Message\DiactorosResponseEmitter::class,
    $middleware
]);
Lazy Instantiation:

You can also do lazy instantiation for your middleware if it's supported by the middleware dispatcher you use (such as the default \BitFrame\Dispatcher\MiddlewareDispatcher), by just providing the class name with its namespace, or simply by appending ::class to an object instance or class name. Lazy instantiation will automatically instantiate your class once it's about to be processed. For example:

// method 1: appending ::class to class name
$middleware = \Namespaced\Middleware::class;

// method 2: appending ::class to object instance
$middleware = $middlewareObj::class;

// method 3: specify full, namespaced, middleware name
$middleware = '\Namespaced\Middleware';

The third method is not recommended as it can lead to various problems, and the first method only works if the class constructor has optional or no arguments.

Anonymous Class:

In PHP 7+, you could use anonymous classes implementing \Psr\Http\Server\MiddlewareInterface like so:

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
use \Psr\Http\Server\MiddlewareInterface;
use \Psr\Http\Server\RequestHandlerInterface;

require '../vendor/autoload.php';

$app = new \BitFrame\Application;

// run middleware
$app->run([
    \BitFrame\Message\DiactorosResponseEmitter::class,
    new class implements MiddlewareInterface {
        public function process(
            ServerRequestInterface $request, 
            RequestHandlerInterface $handler
        ): ResponseInterface 
        {
            $response = $handler->handle($request);
            $response->getBody()->write("Hello World!");

            return $response;
        }
    }
]);

Using A Named Class Method

You could specify a custom class method to call when the middleware executes using the format: [Object, 'methodName'].

require '../vendor/autoload.php';

$app = new \BitFrame\Application;

/* 1: define controller class */

// 2: run middleware
$app->run([
    \BitFrame\Message\DiactorosResponseEmitter::class,
    [new SomeMiddleware, 'someMethod']
]);
Anonymous Class With Named Class Method:

PHP 7+ allows us to create anonymous classes. If you wanted to can call a specific class method in an anonymous class (when the middleware executes), you could do so by using the format: [@AnonymousClass, 'methodName'] like so:

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
use \Psr\Http\Server\MiddlewareInterface;
use \Psr\Http\Server\RequestHandlerInterface;

require '../vendor/autoload.php';

$app = new \BitFrame\Application;

// run middleware
$app->run([
    \BitFrame\Message\DiactorosResponseEmitter::class,
    [new class {
        public function someMethod(
            ServerRequestInterface $request, 
            RequestHandlerInterface $handler
        ): ResponseInterface 
        {
            $response = $handler->handle($request);
            $response->getBody()->write("Hello World!");

            return $response;
        }
    }, 'someMethod']
]);

You should avoid using named methods and should instead use the PSR-15 recommended process() method wherever possible for greater interoperability between different frameworks.

Using The Magic __invoke

This pattern has been the most popularly used signature for HTTP middleware since PSR-7 was introduced:

require '../vendor/autoload.php';

$app = new \BitFrame\Application;

/* 1: define controller class */

// 2: run middleware
$app->run([
    \BitFrame\Message\DiactorosResponseEmitter::class,
    new SomeMiddleware
]);

You should use the PSR-15 based classes instead as the PSR-7 based approaches have their shortcomings.

Using Functions

Anonymous Functions:

You can use an anonymous function inside the middleware dispatcher queue. The anonymous function will receive three arguments; the http request, the http response and the reference to the next item in queue. If you choose to use this method, you must make a call to the next item in the queue yourself for the middleware to process it, for example:

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$app = new \BitFrame\Application;

$app->run([
    \BitFrame\Message\DiactorosResponseEmitter::class,
    function (Request $request, Response $response, callable $next) {
        $response->getBody()->write("Hello World!");
        return $next($request, $response);
    }
]);

PHP 7 introduces anonymous classes, which are similar to anonymous functions (or closures). Using anonymous classes has some benefits over using anonymous functions, such as:

  • Allowing for extension, interface implementation and trait composition;
  • Giving us important type-safety allowing the middleware to enforce typehinting;
  • Allowing us to prototype classes before writing them formally (and since the syntax for anonymous classes is identical to declared classes, we can extract them to a named class anytime by simply cutting the definition and pasting it into a file of its own).

In addition to that, the PSR-7 based approaches have their shortcomings. Therefore, we recommend you use PSR-15 anonymous classes instead of anonymous functions wherever possible.

Named Functions:
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$app = new \BitFrame\Application;

// 1: define controller function
function controller(Request $request, Response $response, callable $next) {
    $response->getBody()->write("Hello World!");
    return $response;
}

// 2: run middleware
$app->run([
    \BitFrame\Message\DiactorosResponseEmitter::class,
    'controller'
]);

As mentioned earlier, you should use the PSR-15 based classes wherever possible as the PSR-7 based approaches have their shortcomings.

Recommendation

Since the PSR-15 standard has been finalized we recommend you use the PSR-15 based classes instead, as the PSR-7 approaches have their shortcomings, most notably:

  • With the introduction of PSR-7 the double-pass style became a popularly used signature for HTTP middleware (adopted from Express middleware — of the Node.js fame), where middlewares pass the response from one to the other down the queue, where at any point an outer middleware can make a change to the response object passing it down to others in the queue, expecting it to propagate back out, but what if an inner middleware returns a different response entirely? This could lead to unexpected problems, so if a middleware needs to operate on a response, it should operate on the response returned by another middleware.
  • Typehinting $next as callable is not type safe as there's no way of ensuring that the callable is actually able to accept the arguments passed to it.

If you wish to add a fallback support for PSR-7 style of double-pass HTTP middleware signature, you could include the \BitFrame\Delegate\CallableMiddlewareTrait in your class and it will do it for you.

Conditionally Adding Middleware

You can conditionally add a middleware to the middleware queue by ensuring that one aspect of your condition returns either null or [] and the other a middleware, for example:

require '../vendor/autoload.php';

$app = new \BitFrame\Application;

$app->run([
    // if $someCondition is false, add SomeMiddleware
    (($someCondition) ? null : new SomeMiddleware)
]);

This will only work in a middleware queue (i.e. when added via run([..]) or addMiddleware([..])) as opposed to adding a single middleware (i.e. via run(..) or addMiddleware(..) — without using an array as argument to these methods).

Comments

Let us know if you have something to say or add