beyond psr-7: the magical middleware tour
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/middlewarepipeline.global.php return [ 'middlewarepipeline' => [ 'basic_authentication' => [ 'middleware' => new BasicAuthentication($users), 'priority' => 4000 ], 'clientip' => [ 'middleware' => ClientIp::class, 'priority' => 3000 ], 'geolocate' => [ 'middleware' => Geolocate::class, 'priority' => 2000 ], 'accesslog' => [ 'middleware' => new AccessLog($logger), 'priority' => 1000 ] ]];
The magical Expressive tour
// config/autoload/middlewarepipeline.global.php return [ 'middlewarepipeline' => [ '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/middlewarepipeline.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
Resources by
by documentation
by documentation
by documentation
PSR-7 By Example Matthew Weier O'PhinneyOn HTTP, Middleware, and PSR-7 Matthew Weier O'PhinneySlim frameworkSlim resourcesRadarRadar and middleware resources Paul M. JonesZend ExpressiveProposed Middleware Interface PSR-N
Speakers love feedback
Leave your feedback at https://joind.in/talk/1ccba
Marco
Steve
@marcoshuttle [email protected]
@maraspin [email protected]
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