Beyond PSR-7: The magical middleware tour

Post on 10-Jan-2017

313 views 0 download

transcript

PHPDay 2016 - Verona, Italy, May 13th 2016 - @marcoshuttle @maraspin

Beyond PSR-7The magical middleware tour

MARCO

EXPLORER

AVID LEARNER

CHOCOLATE LOVER

STEVE

ENJOYS TRAVELLING

SOFTWARE ARCHITECTURES

IN A FEW YEARS...

Software Engineers

START OF THE JOURNEY...

EVERYBODY ON ITS OWN

A Total Mess$filename = 'log.txt'; $handle = fopen($filename, 'a')); fwrite($handle, $errorMessage); fclose($handle);

$filename = 'log.txt'; $handle = fopen($filename, 'a')); fwrite($handle, $errorMessage); fclose($handle);

LIBRARIES

Include Hellinclude 'config.php'; include_once 'dbcon.php'; include_once 'logger.php'; include 'utils.php'; include 'forms.php'; include 'calculations.php'; include 'graphs.php'; include 'auth.php';

MVC Frameworks

IOC FIRST WAVE

Same things, different ways...Zend Framework

Symfony

$logger = new Zend_Log(); $writer = new Zend_Log_Writer_Stream('php://output'); $logger­>addWriter($writer); $logger­>log('Hello PHPDay People!', Zend_Log::INFO);

// YAML Configuration // [...] sfContext::getInstance()­>getLogger()­>info('Hello PHPDay People!');

REINVENTING THE WHEEL

WITH A LITTLE HELP OF...

Microframeworks

IOC NEW WAVE

ONE ISSUE TO SOLVE...

namespace Symfony\Component\HttpFoundation;

class Request public static function createFromGlobals(): Request return self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);

namespace Zend\Http\PhpEnvironment;

class Request extends \Zend\Http\Request public function __construct() $this­>setEnv(new Parameters($_ENV)); $this­>setQuery(new Parameters($_GET)); $this­>setPost(new Parameters($_POST)); $this­>setCookies(new Parameters($_COOKIE)); $this­>setFiles(new Parameters($this­>mapPhpFiles())); $this­>setServer(new Parameters($_SERVER));

NEED FOR

A GOOD HTTP ABSTRACTION

SERVER API

$_SERVER

$_POST

$_GET

header()

setCookie()

echo

CLIENT ADAPTERS

PSR-7

PSR-7 GOALS

INTERFACES

PRACTICAL APPLICATIONS AND USABILITY

NO LIMITS

SERVER AND CLIENT

PSR-7 NON-GOALS

CONFORMATION

IMPOSE DETAILS

A VALUE OBJECT IS FOREVER

Pipes and filters

Pizza Example

DECORATOR

interface Pizza

class Margherita implements Pizza

class CheeseDecoratedPizza implements Pizza function __construct(Pizza $pizza)

class VegetablesDecoratedPizza implements Pizza function __construct(Pizza $pizza)

$myFavouritePizza =

new VegatablesDecoratedPizza(

new CheeseDecoratedPizza(

new Margherita()

)

);

MIDDLEWARE

MIDDLEWAREfunction (Request request): Response ...

response = next(request);

...

return response;

MIDDLEWAREfunction (Request): Response

MIDDLEWAREfunction (Request, Response): Response

MIDDLEWAREfunction (

Request request,

Response response,

callable next

): Response

HOT WEEK

MIDDLEWARE IN ACTION

class Middleware function __invoke($request, $response, $next) if (!$this­>preconditionsExist($request, $response)) throw new RuntimeException();

$request = $this­>doSomethingOnRequest($request);

$response = $next($request, $response);

return $this­>doSomethingOnResponse($response);

class BasicAuthentication function __invoke($request, $response, $next) $authorization = $request­>getHeaderLine('Authorization');

if ($this­>checkUserPassword($authorization)) $request = self::setAttribute( $request, 'USERNAME', $authorization['username'] );

return $next($request, $response);

return $this­>unauthorizedUserResponse($response);

class AccessLog function __invoke($request, $response, $next) if (!self::hasAttribute($request, 'CLIENT_IPS')) throw new RuntimeException();

$response = $next($request, $response);

$message = $this­>createMessage($request, $response);

$this­>logger­>log($message);

return $response;

Slim

Slim

// src/middleware.php

$app­>add(new AccessLog($logger));

$app­>add(new Geolocate());

$app­>add(new ClientIp());

$app­>add(new BasicAuthentication($users));

Radar & Relay

Radar & Relay

// web/index.php

$adr­>middle(new BasicAuthentication($users));

$adr­>middle(new ClientIp());

$adr­>middle(new Geolocate());

$adr­>middle(new AccessLog($logger));

Expressive

Expressive

// config/autoload/middleware­pipeline.global.php return [ 'middleware­pipeline' => [ 'basic_authentication' => [ 'middleware' => new BasicAuthentication($users), 'priority' => 4000 ], 'clientip' => [ 'middleware' => ClientIp::class, 'priority' => 3000 ], 'geolocate' => [ 'middleware' => Geolocate::class, 'priority' => 2000 ], 'access­log' => [ 'middleware' => new AccessLog($logger), 'priority' => 1000 ] ]];

The magical Expressive tour

// config/autoload/middleware­pipeline.global.php return [ 'middleware­pipeline' => [ 'always' => [ 'middleware' => [ Helper\ServerUrlMiddleware::class ], 'priority' => 10000 ], 'routing' => [ 'middleware' => [ ApplicationFactory::ROUTING_MIDDLEWARE, Helper\UrlHelperMiddleware::class, ApplicationFactory::DISPATCH_MIDDLEWARE ], 'priority' => 1 ], 'error' => [ 'middleware' => [], 'error' => true, 'priority' => ­10000 ] ]];

ReactPhp

Expressive/ReactPhp

// config/autoload/middleware­pipeline.global.php return [ 'dependencies' => [ 'factories' => [ React2Psr7\StaticFiles::class => React2Psr7\StaticFilesFactory::class, ] ], 'middleware_pipeline' => [ 'static' => [ 'middleware' => React2Psr7\StaticFiles::class, 'priority' => 100000, // Execute earliest! ], ... ]];

USE CASES

Debug bar

class DebugBar public function __invoke($request, $response, $next) if (!self::hasAttribute($request, FormatNegotiator::KEY)) throw new RuntimeException('Need FormatNegotiator executed before');

if ($this­>isAsset($request)) return $this­>responsewithAssetBody($request, $response); $response = $next($request, $response);

if (Utils\Helpers::isRedirect($response)) $this­>debugBar­>stackData(); else if (FormatNegotiator::getFormat($request) === 'html') $response = $this­>createHtmlResponse($response); else if (Utils\Helpers::isAjax($request)) $response = $this­>createAjaxResponse($response); return $response;

More Available Middleware

Storage-Less Sessions

Device Detection

Analytics Support

Robot-Blocking

Request Rate Limiting

And More...

RoundupPSR-7: A good HTTP abstraction

Abstractions VS Implementations

Re-Inventing the Wheel is over

Middleware is a Hot Topic

Beware of Runtime Dangers

THANK YOU VERY MUCH

Speakers love feedback

Leave your feedback at https://joind.in/talk/1ccba

Marco

Steve

@marcoshuttle m.perone@mvlabs.it

@maraspin s.maraspin@mvassociati.it

ALL YOU NEED IS MIDDLEWARE

CreditsPlane view by Chocolate by Orioles Fan by Mosque by Fans by Hippies by Hippie Van by Students by Rave by Weird Bicicle by Suitcase found at A320 model found at Beatles picture by Beatles picture by Board by Abstract painting by

Figs by Kungsleden by Danger zone by PSR-7 diagram by Diamond by Pizza by Cheese and vegetables by Pizza by Onion by Onion by Cutting onion by Onion by Onion by Onion by Locked door by Log by

Victor CostanJohn LooKeith Allison

FasihjeeMirage Kale

Roland GodefroyJoe Mabel

Shimer CollegeEDM Playlist

Thomas Guestpublicdomainpictures.net

wesharepics.infoNationaal ArchiefUnited Press Intl.

ericfleming8Earle M. Pilgrim

MburnatShyguy24x7cvander

ninjagrlEWAR

ElfQrinStockSnap

Scott bauerColinAmada44

Lali Masrieradarwinbellcostanzimarcosarangib

LEEROY.caGreenpeace Finland