$30 off During Our Annual Pro Sale. View Details »

PHP Middleware i njegova upotreba

DaFED
August 08, 2016

PHP Middleware i njegova upotreba

DaFED#46
Speaker: Nenad Lukić
Predavanje će obuhvatiti osnovne koncepte kreiranja middleware-a u aplikaciji brada middleware-a kao deo ciklusa izvršavanja. Primeri i upotreba middleware-a u aplikacijama.

DaFED

August 08, 2016
Tweet

More Decks by DaFED

Other Decks in Programming

Transcript

  1. Problemi u starom načinu obrade [ 'files' => [ 0

    => [ 'name' => 'file0.txt', 'type' => 'text/plain', ], 1 => [ 'name' => 'file1.html', 'type' => 'text/html', ], ], ]; [ 'files' => [ 'name' => [ 0 => 'file0.txt', 1 => 'file1.html', ], 'type' => [ 0 => 'text/plain, 1 => 'text/html', ], ], ]; Nelogično Logično 4
  2. Usvojeni PSR standardi 1. Basic Coding Standard - Paul M.

    Jones 2. Coding Style Guide - Paul M. Jones 3. Logger Interface - Jordi Boggiano 4. Autoloading Standard - Paul M. Jones 6. Caching Interface - Larry Garfield 7. HTTP Message Interface - Matthew Weier O'Phinney 6
  3. HTTP Request Immutability HTTP Response public function withProtocolVersion($version) { if

    ( $this->protocol === $version ) { return $this; } $new = clone $this; $new->protocol = $version; return $new; } 8
  4. HTTP Request Immutability HTTP Response $request = $request ->withMethod('POST') ->withUrl(new

    Url('http://example.org/') ->withHeader('Content-Type', 'text/plain'); 9
  5. Primjeri $headerValues = $request->getHeader('Cookie'); $headerLine = $request->getHeaderLine('Accept'); $headers = $request->getHeaders();

    $body = $request->getBody(); $message = $response->withHeader('Foo', 'Bar'); $message = $response->withBody($stream); 10
  6. Utisci Pozitivne strane: Interfejs postavlja pravila. Implementacije interfejsa mogu da

    se razlikuju. Jednostavan pristup objektima* Skidanje limita na velicinu poruka korišćenjem stream-ova Aplikacija uvijek ima pristup originalnim request i response objektima Osim $response->getContents() - Stream 11
  7. Utisci Negativne strane: Za svaku promjenu kreira se nova instanca.

    10 promjena 10 instanci. $response->getContents() - Stream $newRequest = $oldRequest ->withHeader('X-Powered-By', 'Dafed'); $newRequest->getBody()->getContents(); $oldRequest->getBody()->getContents(); 12
  8. Middleware Nezavisan dio aplikacije ko služi za obradu i pripremu

    requesta. Dio HTTP Messages standarda. Prijedlog novog PSR-15 standarda koji se specifično odnosi na Middleware. 13
  9. Middleware Tri podjele middleware-a: • Single pass (Request\Next) • Double

    pass (Request\Response\Next) • Closure • Callable • Before • After 14
  10. Prva podjela middleware-a Single pass Double pass public function __invoke(

    RequestInterface $request, callable $next ) { return $next($request); } public function __invoke( RequestInterface $request, ResponseInterface $response, callable $next ) { return $next($request, $response); } 16
  11. Druga podjela middleware-a Closure Callable function(Request $request, Closure $next) {

    return $next($request); } class CustomMiddleware extends MiddlewarePipe { public function __invoke(Request $request, callable $next) { return $next($request); } } 17
  12. Treća podjela middleware-a Before After function(Request $request, Closure $next) {

    return $next($request); } function(Request $request, Closure $next) { $response = $next($request); return $response; } 18
  13. Upotreba ➔ Autorizacija ➔ Autentifikacija ➔ CSRF validacija ➔ Validacija

    podataka ➔ Obrada i priprema podataka ➔ Obrada Response-a ➔ Obrada Request-a ➔ Logovanje ➔ Statistika ➔ itd…. 19
  14. Autentifikacija public function handle($request, Closure $next) { if ($this->auth->guest()) {

    if ($request->ajax()) { return response('Unauthorized.', 401); } else { return redirect()->guest('auth/login'); } } return $next($request); } 20
  15. Access log public function __invoke(ServerRequestInterface $request, callable $next) { if

    (!self::hasAttribute($request, ClientIp::KEY)) { throw new RuntimeException( 'AccessLog middleware needs ClientIp executed before' ); } $response = $next($request, $response); $message = $this->combined ? self::combinedFormat($request, $response) : self::commonFormat($request, $response); if ( $response->getStatusCode() >= 400 && $response->getStatusCode() < 600 ) { $this->logger->error($message); } else { $this->logger->info($message); } return $response; } 21
  16. Response obrada public function __invoke(ServerRequestInterface $request, callable $next) { $response

    = $next($request, $response); return $response->withHeader('X-Powered-By', 'This Blog'); } 22
  17. Obrada Requesta Onion 23 public function peel($request, Closure $core) {

    $coreFunction = $this->createCoreFunction($core); $layers = array_reverse($this->layers); $completeOnion = array_reduce($layers, function($nextLayer, $layer){ return $this->createLayer($nextLayer, $layer); }, $coreFunction); return $completeOnion($request); } private function createCoreFunction(Closure $core) { return function($request) use($core) { return call_user_func($core, $request); }; } private function createLayer($nextLayer, $layer) { return function($request) use($nextLayer, $layer){ return call_user_func_array([$layer, 'peel'], [$request, $nextLayer]); }; }
  18. Obrada Requesta Onion 24 class BeforeLayer implements LayerInterface { public

    function peel($request, Closure $next) { $request>runs[] = 'before'; return $next($request); } } class AfterLayer implements LayerInterface { public function peel($request, Closure $next) { $response = $next($request); $request>runs[] = 'after'; return $response; } }
  19. Obrada Requesta Onion 25 $request = new StdClass; $request->runs =

    []; $onion = new Onion; $end = $onion->layer([ new AfterLayer(), new BeforeLayer(), new AfterLayer(), new BeforeLayer() ])->peel($request, function($request){ $request->runs[] = 'core'; return $request; }); var_dump($end);
  20. Obrada Requesta Onion 26 object(stdClass)#1 (1) { ["runs"]=> array(5) {

    [0]=> string(6) "before" [1]=> string(6) "before" [2]=> string(4) "core" [3]=> string(5) "after" [4]=> string(5) "after" } }
  21. U drugim tehnologijama Ruby (via Rack) Python (via WSGI) Node

    (via Connect / ExpressJS) PHP Framerworks: • StackPHP • Laravel • Symfony • Slim • Zend etc... 27
  22. Opšte smjernice koje Middleware treba da poštuje • Primi request

    i response objekte od prethodnog middlewarea kao parametre, kao i sledeći middleware u stack-u. • Ukoliko je potrebno, napravi novi request objektat sa izmjenjenim parametrima. • Ukoliko je potrebno, pozovi sledeći middleware sa prosledjenim ili promjenjenim objektima • Ukoliko je potrebno modifikuj response objekat kreiranjem novog objekta sa izmjenjenim parametrima • Vrati response objekat prethodnim middleware-ovima. 28
  23. Laravel Default middleware stack // Global middleware 'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode', 'Illuminate\Cookie\Middleware\EncryptCookies', 'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',

    'Illuminate\Session\Middleware\StartSession', 'Illuminate\View\Middleware\ShareErrorsFromSession', 'App\Http\Middleware\VerifyCsrfToken', // Route middleware 'auth' => 'App\Http\Middleware\Authenticate', 'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth', 'guest' => 'App\Http\Middleware\RedirectIfAuthenticated', 33
  24. Laravel Check for maintenance mode <?php class CheckForMaintenanceMode implements Middleware

    { protected $app; public function __construct(Application $app) { $this->app = $app; } /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if ($this->app->isDownForMaintenance()) { throw new HttpException(503); } return $next($request); } } 34
  25. Laravel $ php artisan make:middleware DafedCheck $ php artisan make:middleware

    DafedCheck Middleware created successfully. $ ll /app/Http/Middleware total 10 -rw-r--r-- 1 bbs 197121 751 Apr 30 2015 Authenticate.php -rw-r--r-- 1 bbs 197121 295 Jul 31 18:18 DafedCheck.php -rw-r--r-- 1 bbs 197121 706 Apr 30 2015 RedirectIfAuthenticated.php -rw-r--r-- 1 bbs 197121 412 Apr 30 2015 VerifyCsrfToken.php 35
  26. <?php namespace App\Http\Middleware; use Closure; class DafedCheck { /** *

    Run the request filter. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if ($request->path() === ‘dafedWelcome’)) { return redirect('welcome'); } return $next($request); } } Laravel 36
  27. Route::get('/dafedWelcome, function () { // })->middleware('auth', ‘DafedCheck’); Laravel Laravel ////

    'DafedCheck'=> 'App\Http\Middleware\DafedCheck, //// Route::group(['middleware' => [‘DafedCheck’]], function () { // }); 37
  28. Laravel Laravel <?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class

    Kernel extends HttpKernel { /** * The application's global HTTP middleware stack. * * @var array */ protected $middleware = [ 'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode', 'Illuminate\Cookie\Middleware\EncryptCookies', 'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse', 'Illuminate\Session\Middleware\StartSession', 'Illuminate\View\Middleware\ShareErrorsFromSession', 'App\Http\Middleware\VerifyCsrfToken', 'App\Http\Middleware\DafedCheck', ]; ................ 38
  29. Laravel Laravel <?php namespace App\Http\Middleware; use Closure; class DafedCheck {

    /** * Run the request filter. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next, $param) { if ($request->path() === ‘dafedWelcome’ && $param === ‘perform’)) { return redirect('welcome'); } return $next($request); } } Route::get('/dafedWelcome, function () { // })->middleware('auth', ‘DafedCheck:perform); 39
  30. Laravel Laravel <?php namespace App\Http\Middleware; use Closure; use Monolog\Logger; use

    Monolog\Handler\StreamHandler; class DafedCheck { public function handle($request, Closure $next, $param) { /** * stuff */ } // After the response has been sent public function terminate($request, $response) { $log = new Logger('name'); $log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); $log->info('Visit'); } } 40
  31. Reference 1. http://www.php-fig.org/psr/psr-7 2. http://blog.ircmaxell.com/2016/05/all-about-middleware.html?m=1 3. https://github.com/php-fig/fig-standards/blob/master/proposed/http-middleware/mi ddleware-meta.md 4. http://relayphp.com/

    5. https://github.com/phly/conduit 6. http://stackphp.com 7. http://www.slimframework.com/docs/concepts/middleware.html 8. https://laravel.com/ 9. https://evertpot.com/psr-7-issues/ 42