Middleware pattern in PHP

Ujjwal Ojha

Me on Github Me on Twitter ojhaujjwal

Agendas

  • PSR-7
  • Middleware pattern
  • Zend Expressive project

PSR-7

  • Proposed by PHP FIG(Framework Interoperability Group)
  • A standard which provides a set of common interfaces for reprensenting HTTP messages
  • Interfaces defined in composer package
    psr/http-message

HTTP Request

                
POST /path HTTP/1.1
Host: example.com
AnotherHeader: AnotherHeaderValue

foo=bar&baz=bat
                
			

HTTP Response

                
HTTP/1.1 200 OK
Content-Type: text/plain
AnotherHeader: AnotherHeaderValue

This is the response body
                
			

PSR-7 Interfaces

  • Psr\Http\Message\MessageInterface
  • Psr\Http\Message\RequestInterface
  • Psr\Http\Message\ResponseInterface
  • Psr\Http\Message\ServerRequestInterface
  • Psr\Http\Message\StreamInterface
  • Psr\Http\Message\UploadedFileInterface
  • Psr\Http\Message\UriInterface

Value objects and Immutability

  • HTTP Messages and URIs are value objects
  • Change to any aspect of the message is essentially a new message
  • HTTP Messages and URIs which are value objects are immutable
                
$baseUri = new Uri('http://api.example.com');
$baseRequest = new Request($baseUri, null, [
    'Authorization' => 'Bearer ' . $token,
    'Accept'        => 'application/json',
]);;

$uri = $baseUri->withPath('/user');
$request = $baseRequest->withUri($uri)
    ->withMethod('GET');
                
            

Streams

  • MessageInterface uses a body value that must implement StreamInterface
  • HTTP message may consume high memory for extremely large body
  • StreamInterface API is based on Python's io module

Without PSR-7

  • Project will access superglobals directly
  • Different libraries will have different implementations
  • Libraries may depend on a specific implementation
  • Different libraries may have multiple adapters for diffent http message implementation

PSR-7 Implementations

  • zendframework/zend-diactoros
  • guzzlehttp/psr7
  • slim/slim

Implementations list available on http://bit.ly/1OHQJHd

Middleware pattern

  • A middleware is a piece of code which sits between request and response
  • Middleware are functions that handle requests
  • A middleware based application is a queue of middlewares
  • A middleware can:
    • read incoming request
    • perform actions based on request
    • either complete the response or pass to the next middleware

Middleware signature(Double Pass)

                
function (
    Psr\Http\Message\ServerRequestInterface $request,
    Psr\Http\Message\ResponseInterface $response,
    callable $next = null
) {

}
                
            

Middleware signature(Single Pass)

                
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\ServerMiddleware\DelegateInterface;
use Psr\Http\ServerMiddleware\MiddlewareInterface;

class MyMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        DelegateInterface $delegate
    ) {
       return new Response();

       // or call the next middleware in the queue
       return $delegate->process($request);

    }
}
                
            

proposed PSR-15 standard as of now

Sample application as queue of middlewares

                
$app = new MiddlewareRunner();
$app->add(new Versioning());
$app->add(new Router());
$app->add(new Authentication());
$app->add(new Options());
$app->add(new Authorization());
$app->add(new ContentNegotiation());
$app->add(new ContentType());
$app->add(new Dispatcher());
$app->add(new ProblemHandler());
$app->run($request, $response);
                
            

You get to control the workflow of your application by deciding the order in which middleware is queued

Middleware based modules

                
$app = new MiddlewareRunner();
$app->add('/contact', new ContactFormMiddleware());
$app->add('/forum', new ForumMiddleware());
$app->add('/blog', new BlogMiddleware());
$app->add('/store', new EcommerceMiddleware());
$app->run($request, $response);
                
            

Stratigility

  • a port of Sencha Connect to PHP
  • allows you to build applications out of middleware
                
$app = new Zend\Stratigility\MiddlewarePipe();

// Landing page
$app->pipe('/', function ($req, $res, $next) {
    if (! in_array($req->getUri()->getPath(), ['/', ''], true)) {
        return $next($req, $res);
    }
    return $res->end('Hello world!');
});

// Another page
$app->pipe('/foo', function ($req, $res, $next) {
    return $res->end('FOO!');
});
                
            

Expressive

Installing Expressive skeleton

Expressive skelton installation

Select Router

Select Router

Select Container

Select Container

Select Template Engine

Select Template Engine

Conclusion

  • Forget using superglobals and different http message representation components. Use PSR-7
  • No more zf modules, symfony bundles. Use middlewares
  • You can use Expressive to easily build PSR-15 based middleware applications.

Thank You!