SlideShare a Scribd company logo
Tobias Nyholm, @tobiasnyholm
Deep dive into Symfony4 internals
@tobiasnyholm
Why?
@tobiasnyholm
Tobias Nyholm
• Full stack unicorn on Happyr.com
• Certified Symfony developer
• Symfony core member
• PHP-Stockholm
• Open source
@tobiasnyholm
Open source
PHP-cache
HTTPlug
Mailgun
LinkedIn API clientSwap
Stampie
BazingaGeocoderBundle
PHP-Geocoder
FriendsOfApi/boilerplate
Guzzle Buzz
CacheBundlePSR7
SymfonyBundleTest
NSA
SimpleBus integrations
PSR HTTP clients
Neo4j
KNP Github API
PHP-Translation
Puli
Assert
Backup-manager/symfony
php-http/httplug-bundle php-http/multipart-stream
php-http/discovery
happyr/normal-distribution-bundle
nyholm/effective-interest-rate
MailgunBundle
league/geotools
@tobiasnyholm
Building your own
framework
@tobiasnyholm
Just a bunch of files
<?php // index.php
if (isset($_GET['page']) && $_GET['page'] === 'foo') {
echo "Foo page <br>";
} else {
echo "Welcome to index! <br>";
}
if ($_SERVER['REMOTE_ADDR'] === '127.0.0.1') {
echo "(admin stuff)";
}
@tobiasnyholm
Just a bunch of files
@tobiasnyholm
Just a bunch of files
<?php // index.php
if (isset($_GET['page']) && $_GET['page'] === 'foo') {
echo "Foo page <br>";
} else {
echo "Welcome to index! <br>";
}
if ($_SERVER['REMOTE_ADDR'] === '127.0.0.1') {
echo "(admin stuff)";
}
@tobiasnyholm
HTTP objects
@tobiasnyholm
HTTP objects
<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$query = $request->getQueryParams();
if (isset($query['page']) && $query['page'] === 'foo') {
$response = new Response(200, [], 'Foo page');
} else {
$response = new Response(200, [], 'Welcome to index!');
}
// Send response
echo $response->getBody();
@tobiasnyholm
HTTP objects
@tobiasnyholm
HTTP objects
@tobiasnyholm
HTTP objects
<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$query = $request->getQueryParams();
if (isset($query['page']) && $query['page'] === 'foo') {
$response = new Response(200, [], 'Foo page');
} else {
$response = new Response(200, [], 'Welcome to index!');
}
// Send response
echo $response->getBody();
@tobiasnyholm
Controllers
@tobiasnyholm
Controllers
@tobiasnyholm
Controllers
@tobiasnyholm
Controllers<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/../vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$uri = $request->getUri()->getPath();
if ($uri === '/') {
$response = (new AppControllerStartpageController())->run($request);
} elseif ($uri === '/foo') {
$response = (new AppControllerFooController())->run($request);
} else {
$response = new Response(404, [], 'Not found');
}
// Send response
echo $response->getBody();
@tobiasnyholm
Controllers
<?php
declare(strict_types=1);
namespace AppController;
use NyholmPsr7Response;
use PsrHttpMessageRequestInterface;
class StartpageController
{
public function run(RequestInterface $request)
{
return new Response(200, [], 'Welcome to index!');
}
}
@tobiasnyholm
Controllers
@tobiasnyholm
Controllers<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/../vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$uri = $request->getUri()->getPath();
if ($uri === '/') {
$response = (new AppControllerStartpageController())->run($request);
} elseif ($uri === '/foo') {
$response = (new AppControllerFooController())->run($request);
} else {
$response = new Response(404, [], 'Not found');
}
// Send response
echo $response->getBody();
@tobiasnyholm
Event loop
@tobiasnyholm
Event loop<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/../vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$response = new Response();
$middlewares[] = new AppMiddlewareRouter();
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
$response = $runner($request, $response);
// Send response
echo $response->getBody();
@tobiasnyholmnamespace AppMiddleware;
use PsrHttpMessageResponseInterface as Response;
use PsrHttpMessageServerRequestInterface as Request;
class Router implements MiddlewareInterface
{
public function __invoke(Request $request, Response $response, callable $next)
{
$uri = $request->getUri()->getPath();
switch ($uri) {
case '/':
$response = (new AppControllerStartpageController())->run($request);
break;
case '/foo':
$response = (new AppControllerFooController())->run($request);
break;
default:
$response = $response->withStatus(404);
$response->getBody()->write('Not Found');
break;
}
return $next($request, $response);
}
}
@tobiasnyholm
Event loop
@tobiasnyholm
Event loop<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/../vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$response = new Response();
$middlewares[] = new AppMiddlewareRouter();
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
$response = $runner($request, $response);
// Send response
echo $response->getBody();
@tobiasnyholm
Cache
@tobiasnyholm<?php // Cache.php
namespace AppMiddleware;
use CacheAdapterApcuApcuCachePool;
use PsrCacheCacheItemPoolInterface;
use PsrHttpMessageResponseInterface as Response;
use PsrHttpMessageServerRequestInterface as Request;
class Cache implements MiddlewareInterface
{
/*** @var CacheItemPoolInterface */
private $cache;
/** @var int */
private $ttl;
public function __construct()
{
$this->cache = new ApcuCachePool();
$this->ttl = 300;
}
public function __invoke(Request $request, Response $response, callable $next)
{
$uri = $request->getUri();
$cacheKey = 'url'.sha1($uri->getPath().'?'.$uri->getQuery());
$cacheItem = $this->cache->getItem($cacheKey);
@tobiasnyholm
Cache<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/../vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$response = new Response();
$middlewares[] = new AppMiddlewareCache();
$middlewares[] = new AppMiddlewareRouter();
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
$response = $runner($request, $response);
// Send response
echo $response->getBody();
@tobiasnyholm
Cache
@tobiasnyholm
Cache<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/../vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$response = new Response();
$middlewares[] = new AppMiddlewareCache();
$middlewares[] = new AppMiddlewareRouter();
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
$response = $runner($request, $response);
// Send response
echo $response->getBody();
@tobiasnyholm
Container
@tobiasnyholm
Container
<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/../vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$response = new Response();
$middlewares[] = new AppMiddlewareCache();
$middlewares[] = new AppMiddlewareRouter();
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
$response = $runner($request, $response);
// Send response
echo $response->getBody();
@tobiasnyholm
Container
<?php // index.php
use NyholmPsr7FactoryServerRequestFactory;
use NyholmPsr7Response;
require __DIR__.'/../vendor/autoload.php';
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$kernel = new AppKernel('dev', true);
$response = $kernel->handle($request);
// Send response
echo $response->getBody();
<?php // Kernel.php
namespace App;
use NyholmPsr7Response;
use PsrHttpMessageRequestInterface;
use PsrHttpMessageResponseInterface;
use SymfonyComponentConfigExceptionFileLocatorFileNotFoundException;
use SymfonyComponentConfigFileLocator;
use SymfonyComponentDependencyInjectionContainer;
use SymfonyComponentDependencyInjectionContainerBuilder;
use SymfonyComponentDependencyInjectionDumperPhpDumper;
use SymfonyComponentDependencyInjectionLoaderYamlFileLoader;
class Kernel
{
private $booted = false;
private $debug;
private $environment;
/** @var Container */
private $container;
public function __construct(string $env, bool $debug = false)
{
$this->debug = $debug;
$this->environment = $env;
}
@tobiasnyholm
Container
@tobiasnyholm
Container
@tobiasnyholm
Container
@tobiasnyholm
Container
@tobiasnyholm
Router
@tobiasnyholm
<?php
namespace AppMiddleware;
use PsrHttpMessageResponseInterface as Response;
use PsrHttpMessageServerRequestInterface as Request;
class Router implements MiddlewareInterface
{
public function __invoke(Request $request, Response $response, callable $next)
{
$uri = $request->getUri()->getPath();
switch ($uri) {
case '/':
$response = (new AppControllerStartpageController())->run($request);
break;
case '/foo':
$response = (new AppControllerFooController())->run($request);
break;
default:
$response = $response->withStatus(404);
$response->getBody()->write('Not Found');
break;
}
return $next($request, $response);
}
}
@tobiasnyholm
public function __invoke(Request $request, Response $response, callable $next)
{
$uri = $request->getUri()->getPath();
switch ($uri) {
case '/':
$response = (new AppControllerStartpageController())->run($request);
break;
case '/foo':
$response = (new AppControllerFooController())->run($request);
break;
default:
$response = $response->withStatus(404);
$response->getBody()->write('Not Found');
break;
}
return $next($request, $response);
}
@tobiasnyholm
Router
/admin/account
/admin/password
/images/upload
/images/{id}/show
/images/{id}/delete
/foo
/
public function __invoke(Request $request, Response $response, callable $next)
{
$uri = $request->getUri()->getPath();
if (0 === strpos($uri, '/admin')) {
if (0 === strpos($uri, '/admin/account')) {
$response = (new AppControllerManagerController())->accountAction($request);
}
if (0 === strpos($uri, '/admin/password')) {
$response = (new AppControllerManagerController())->passwordAction($request);
}
} elseif (0 === strpos($uri, '/images')) {
if (0 === strpos($uri, '/images/upload')) {
$response = (new AppControllerImageController())->uploadAction($request);
}
if (preg_match('#^/images/(?P<id>[^/]++)/show#sD', $uri, $matches)) {
$response = (new AppControllerImageController())->showAction($request, $matches[
}
if (preg_match('#^/images/(?P<id>[^/]++)/delete#sD', $uri, $matches)) {
$response = (new AppControllerImageController())->deleteAction($request, $matche
}
} elseif ($uri === '/') {
$response = (new AppControllerStartpageController())->run($request);
} elseif ($uri === '/foo') {
$response = (new AppControllerFooController())->run($request);
} else {
$response = $response->withStatus(404);
$response->getBody()->write('Not Found');
}
public function __invoke(Request $request, Response $response, callable $next)
{
$uri = $request->getUri()->getPath();
$routes = array(
'/foo' => 'AppControllerFooController::FooAction',
'/images/upload' => 'AppControllerImageController::uploadAction',
'/admin/account' => 'AppControllerManagerController::accountAction',
'/admin/password' => 'AppControllerManagerController::passwordAction',
// More static routes
'/' => 'AppControllerStartpageController::startpageAction',
);
if (isset($routes[$uri])) {
$response = call_user_func($routes[$uri], $request);
return $next($request, $response);
}
$regex =
'{^(?'
.'|/images/([^/]++)/(?'
.'|show(*:31)'
.'|delete(*:44)'
// my dynamic and complex regex
.')'
.')$}sD';
@tobiasnyholm
Router
@tobiasnyholm
Security
@tobiasnyholm
Security
<?php
namespace AppMiddleware;
use NyholmPsr7Response;
use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
class Security implements MiddlewareInterface
{
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callabl
{
$uri = $request->getUri()->getPath();
$ip = $request->getServerParams()['REMOTE_ADDR'];
if ($ip !== '127.0.0.1' && $uri === '/admin') {
return new Response(403, [], 'Forbidden');
}
if ($uri === '/images/4711/delete') {
if (true /* user is not "bob" */) {
return new Response(403, [], 'Forbidden');
}
}
return $next($request, $response);
}
}
@tobiasnyholm
<?php
namespace AppMiddleware;
use AppSecurityTokenStorage;
use NyholmPsr7Response;
use PsrHttpMessageResponseInterface ;
use PsrHttpMessageServerRequestInterface;
class Authentication implements MiddlewareInterface
{
private $tokenStorage;
/**
*
* @param $tokenStorage
*/
public function __construct(TokenStorage $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable
{
$uri = $request->getUri()->getPath();
$auth = $request->getServerParams()['PHP_AUTH_USER']??'';
$pass = $request->getServerParams()['PHP_AUTH_PW']??'';
@tobiasnyholm
Securityclass Kernel
{
// …
/**
* Handle a Request and turn it in to a response.
*/
public function handle(RequestInterface $request): ResponseInterface
{
$this->boot();
$middlewares[] = $this->container->get(‘middleware.auth');
$middlewares[] = $this->container->get('middleware.security');
$middlewares[] = $this->container->get('middleware.cache');
$middlewares[] = new AppMiddlewareRouter();
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
return $runner($request, new Response());
}
// …
}
@tobiasnyholm
Security
@tobiasnyholm
Security
@tobiasnyholm
Security
@tobiasnyholm
Security
<?php
declare(strict_types=1);
namespace AppController;
use AppSecurityTokenStorage;
use NyholmPsr7Response;
use PsrHttpMessageRequestInterface;
class AdminController
{
private $tokenStorage;
public function __construct(TokenStorage $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function run(RequestInterface $request)
{
return new Response(200, [],
sprintf('Hello %s (admin)', $this->tokenStorage->getLastToken()['username']));
}
}
@tobiasnyholm
Security
Authentication vs Authorisation
@tobiasnyholm
class SecurityVoters implements MiddlewareInterface
{
/** @var VoterInterface[] */
private $voters;
public function __construct(array $voters)
{
$this->voters = $voters;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable
{
$deny = 0;
foreach ($this->voters as $voter) {
$result = $voter->vote($request);
switch ($result) {
case VoterInterface::ACCESS_GRANTED:
return $next($request, $response);
case VoterInterface::ACCESS_DENIED:
++$deny;
break;
default:
break;
}
}
if ($deny > 0) {
return new Response(403, [], 'Forbidden');
}
return $next($request, $response);
@tobiasnyholm
Security
declare(strict_types=1);
namespace AppSecurityVoter;
use AppSecurityTokenStorage;
use PsrHttpMessageServerRequestInterface;
class AdminVoter implements VoterInterface
{
private $tokenStorage;
public function __construct(TokenStorage$tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function vote(ServerRequestInterface $request)
{
$uri = $request->getUri()->getPath();
$token = $this->tokenStorage->getLastToken();
if ($token['username'] !== 'Tobias' && $uri === '/admin') {
return VoterInterface::ACCESS_DENIED;
}
return VoterInterface::ACCESS_ABSTAIN;
}
}
@tobiasnyholm
Security
@tobiasnyholm
Securityclass Kernel
{
// …
/**
* Handle a Request and turn it in to a response.
*/
public function handle(RequestInterface $request): ResponseInterface
{
$this->boot();
$middlewares[] = $this->container->get(‘middleware.auth');
$middlewares[] = $this->container->get('middleware.security');
$middlewares[] = $this->container->get('middleware.cache');
$middlewares[] = new AppMiddlewareRouter();
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
return $runner($request, new Response());
}
// …
}
@tobiasnyholm
Security
@tobiasnyholm
Autowiring
@tobiasnyholm
Autowiring
@tobiasnyholm
Autowiring
P
P
P
@tobiasnyholm
Autowiringclass Kernel
{
// …
/**
* Handle a Request and turn it in to a response.
*/
public function handle(RequestInterface $request): ResponseInterface
{
$this->boot();
$middlewares[] = $this->container->get(Authentication::class);
$middlewares[] = $this->container->get(SecurityVoters::class);
$middlewares[] = $this->container->get(Cache::class);
$middlewares[] = new AppMiddlewareRouter();
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
return $runner($request, new Response());
}
// …
}
@tobiasnyholm
Autowiring
@tobiasnyholm
Toolbar
@tobiasnyholmclass Toolbar implements MiddlewareInterface
{
private $cacheDataCollector;
public function __construct(CacheDataCollector $cacheDataCollector)
{
$this->cacheDataCollector = $cacheDataCollector;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable
{
$calls = $this->cacheDataCollector->getCalls();
$getItemCalls = count($calls['getItem']);
$content = $response->getBody()->__toString();
$toolbar = <<<HTML
<br><br><br><hr>
URL: {$request->getUri()->getPath()}<br>
IP: {$request->getServerParams()['REMOTE_ADDR']}<br>
Cache calls: {$getItemCalls}<br>
HTML;
$stream = (new StreamFactory())->createStream($content.$toolbar);
$response = $response->withBody($stream);
return $next($request, $response);
}
}
@tobiasnyholm<?php
namespace AppDataCollector;
use PsrCacheCacheItemInterface;
use PsrCacheCacheItemPoolInterface;
class CacheDataCollector implements CacheItemPoolInterface
{
/** @var CacheItemPoolInterface */
private $real;
private $calls;
public function __construct(CacheItemPoolInterface $cache)
{
$this->real = $cache;
}
public function getCalls()
{
return $this->calls;
}
public function getItem($key)
{
$this->calls['getItem'][] = ['key'=>$key];
return $this->real->getItem($key);
}
@tobiasnyholm
Toolbar
@tobiasnyholmclass Toolbar implements MiddlewareInterface
{
private $cacheDataCollector;
public function __construct(CacheDataCollector $cacheDataCollector)
{
$this->cacheDataCollector = $cacheDataCollector;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable
{
$calls = $this->cacheDataCollector->getCalls();
$getItemCalls = count($calls['getItem']);
$content = $response->getBody()->__toString();
$toolbar = <<<HTML
<br><br><br><hr>
URL: {$request->getUri()->getPath()}<br>
IP: {$request->getServerParams()['REMOTE_ADDR']}<br>
Cache calls: {$getItemCalls}<br>
HTML;
$stream = (new StreamFactory())->createStream($content.$toolbar);
$response = $response->withBody($stream);
return $next($request, $response);
}
}
@tobiasnyholm
Exception
@tobiasnyholm
Exception
<?php
declare(strict_types=1);
namespace AppController;
use NyholmPsr7Response;
use PsrHttpMessageRequestInterface;
class ExceptionController
{
public function run(RequestInterface $request)
{
throw new RuntimeException('This is an exception');
}
}
@tobiasnyholm
Exception
<?php
namespace AppMiddleware;
use CacheAdapterApcuApcuCachePool;
use NyholmPsr7Response;
use PsrCacheCacheItemPoolInterface;
use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
class ExceptionHandler implements MiddlewareInterface
{
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, calla
{
try {
$response = $next($request, $response);
} catch (Throwable $exception) {
$response = new Response(500, [], $exception->getMessage());
}
return $response;
}
}
@tobiasnyholm
Exceptionclass Kernel
{
/**
* Handle a Request and turn it in to a response.
*/
public function handle(RequestInterface $request): ResponseInterface
{
$this->boot();
$middlewares[] = $this->container->get(ExceptionHandler::class);
$middlewares[] = $this->container->get(Authentication::class);
$middlewares[] = $this->container->get(SecurityVoters::class);
$middlewares[] = $this->container->get(Cache::class);
$middlewares[] = new AppMiddlewareRouter();
$middlewares[] = $this->container->get(Toolbar::class);
$runner = (new RelayRelayBuilder())->newInstance($middlewares);
return $runner($request, new Response());
}
}
@tobiasnyholm
Exception
@tobiasnyholm
Exception
<?php
namespace AppMiddleware;
use CacheAdapterApcuApcuCachePool;
use NyholmPsr7Response;
use PsrCacheCacheItemPoolInterface;
use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
class ExceptionHandler implements MiddlewareInterface
{
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, calla
{
try {
$response = $next($request, $response);
} catch (Throwable $exception) {
$response = new Response(500, [], $exception->getMessage());
}
return $response;
}
}
Tobias Nyholm "Deep dive into Symfony 4 internals"
@tobiasnyholm
@tobiasnyholm
@tobiasnyholm
Where is Symfony?
@tobiasnyholm
HTTP objects
@tobiasnyholm
Controllers
@tobiasnyholm
Event loop
kernel.request
kernel.controller
kernel.view
kernel.response
kernel.finish_request
kernel.terminate
@tobiasnyholm
Cache
PSR-6
PSR-16
@tobiasnyholm
Container
@tobiasnyholm
Router
@tobiasnyholm
Security
@tobiasnyholm
Autowiring
@tobiasnyholm
Toolbar
@tobiasnyholm
Exception
@tobiasnyholm
This was Symfony
sim
ple
@tobiasnyholm
What is Symfony?
nyholm/psr7 symfony/http-foundation
relay/relay symfony/event-dispatcher
php-cache/* symfony/cache
Custom kernel symfony/http-kernel
Custom security symfony/security
@tobiasnyholm
Questions?
https://joind.in/talk/8b9dd
Tobias Nyholm "Deep dive into Symfony 4 internals"

More Related Content

PPTX
Zephir - A Wind of Change for writing PHP extensions
KEY
Kansai.pm 10周年記念 Plack/PSGI 入門
PDF
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
ODP
Modern Web Development with Perl
PDF
Python Google Cloud Function with CORS
PDF
関西PHP勉強会 php5.4つまみぐい
PDF
Cli the other SAPI confoo11
ODP
Advanced Perl Techniques
Zephir - A Wind of Change for writing PHP extensions
Kansai.pm 10周年記念 Plack/PSGI 入門
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
Modern Web Development with Perl
Python Google Cloud Function with CORS
関西PHP勉強会 php5.4つまみぐい
Cli the other SAPI confoo11
Advanced Perl Techniques

What's hot (20)

PDF
Legacy applications - 4Developes konferencja, Piotr Pasich
PPTX
New in php 7
ODP
nginx mod PSGI
PDF
Get your teeth into Plack
PPTX
Introduction to windows power shell in sharepoint 2010
PDF
Codeigniter4の比較と検証
PDF
Utility Modules That You Should Know About
PPTX
Power Shell and Sharepoint 2013
PPTX
10 tips for making Bash a sane programming language
PDF
Into the ZF2 Service Manager
ODP
Building Web Services with Zend Framework (PHP Benelux meeting 20100713 Vliss...
ODP
Modern Perl
PDF
Getting testy with Perl
PDF
Symfony2 Components - The Event Dispatcher
PDF
Findbin libs
PDF
BASH Variables Part 1: Basic Interpolation
PDF
What's new in PHP 5.5
ODP
Introduction to Modern Perl
PDF
The $path to knowledge: What little it take to unit-test Perl.
PDF
BSDM with BASH: Command Interpolation
Legacy applications - 4Developes konferencja, Piotr Pasich
New in php 7
nginx mod PSGI
Get your teeth into Plack
Introduction to windows power shell in sharepoint 2010
Codeigniter4の比較と検証
Utility Modules That You Should Know About
Power Shell and Sharepoint 2013
10 tips for making Bash a sane programming language
Into the ZF2 Service Manager
Building Web Services with Zend Framework (PHP Benelux meeting 20100713 Vliss...
Modern Perl
Getting testy with Perl
Symfony2 Components - The Event Dispatcher
Findbin libs
BASH Variables Part 1: Basic Interpolation
What's new in PHP 5.5
Introduction to Modern Perl
The $path to knowledge: What little it take to unit-test Perl.
BSDM with BASH: Command Interpolation
Ad

Similar to Tobias Nyholm "Deep dive into Symfony 4 internals" (20)

PDF
What's new with PHP7
PDF
Symfony2 - OSIDays 2010
PDF
Symfony2 - WebExpo 2010
PDF
Symfony2 - WebExpo 2010
KEY
GettingStartedWithPHP
PDF
Supercharging WordPress Development in 2018
PDF
Symfony internals [english]
ODP
PHP pod mikroskopom
PDF
symfony on action - WebTech 207
PDF
The new features of PHP 7 - Enrico Zimuel - Codemotion Milan 2016
PDF
The new features of PHP 7
KEY
Phpne august-2012-symfony-components-friends
PDF
The symfony platform: Create your very own framework (PHP Quebec 2008)
PPTX
ODP
CodeIgniter PHP MVC Framework
ODP
The why and how of moving to PHP 5.4/5.5
PDF
Living With Legacy Code
PDF
PHP 5.3 Overview
PDF
How to build a High Performance PSGI/Plack Server
PPTX
Php7 HHVM and co
What's new with PHP7
Symfony2 - OSIDays 2010
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
GettingStartedWithPHP
Supercharging WordPress Development in 2018
Symfony internals [english]
PHP pod mikroskopom
symfony on action - WebTech 207
The new features of PHP 7 - Enrico Zimuel - Codemotion Milan 2016
The new features of PHP 7
Phpne august-2012-symfony-components-friends
The symfony platform: Create your very own framework (PHP Quebec 2008)
CodeIgniter PHP MVC Framework
The why and how of moving to PHP 5.4/5.5
Living With Legacy Code
PHP 5.3 Overview
How to build a High Performance PSGI/Plack Server
Php7 HHVM and co
Ad

More from Fwdays (20)

PDF
"Mastering UI Complexity: State Machines and Reactive Patterns at Grammarly",...
PDF
"Effect, Fiber & Schema: tactical and technical characteristics of Effect.ts"...
PPTX
"Computer Use Agents: From SFT to Classic RL", Maksym Shamrai
PPTX
"Як ми переписали Сільпо на Angular", Євген Русаков
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
PDF
"Validation and Observability of AI Agents", Oleksandr Denisyuk
PPTX
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
PDF
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
PPTX
"Co-Authoring with a Machine: What I Learned from Writing a Book on Generativ...
PPTX
"Human-AI Collaboration Models for Better Decisions, Faster Workflows, and Cr...
PDF
"AI is already here. What will happen to your team (and your role) tomorrow?"...
PPTX
"Is it worth investing in AI in 2025?", Alexander Sharko
PDF
''Taming Explosive Growth: Building Resilience in a Hyper-Scaled Financial Pl...
PDF
"Scaling in space and time with Temporal", Andriy Lupa.pdf
PDF
"Database isolation: how we deal with hundreds of direct connections to the d...
PDF
"Scaling in space and time with Temporal", Andriy Lupa .pdf
PPTX
"Provisioning via DOT-Chain: from catering to drone marketplaces", Volodymyr ...
PPTX
" Observability with Elasticsearch: Best Practices for High-Load Platform", A...
PPTX
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
PPTX
"Istio Ambient Mesh in production: our way from Sidecar to Sidecar-less",Hlib...
"Mastering UI Complexity: State Machines and Reactive Patterns at Grammarly",...
"Effect, Fiber & Schema: tactical and technical characteristics of Effect.ts"...
"Computer Use Agents: From SFT to Classic RL", Maksym Shamrai
"Як ми переписали Сільпо на Angular", Євген Русаков
"AI Transformation: Directions and Challenges", Pavlo Shaternik
"Validation and Observability of AI Agents", Oleksandr Denisyuk
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
"Co-Authoring with a Machine: What I Learned from Writing a Book on Generativ...
"Human-AI Collaboration Models for Better Decisions, Faster Workflows, and Cr...
"AI is already here. What will happen to your team (and your role) tomorrow?"...
"Is it worth investing in AI in 2025?", Alexander Sharko
''Taming Explosive Growth: Building Resilience in a Hyper-Scaled Financial Pl...
"Scaling in space and time with Temporal", Andriy Lupa.pdf
"Database isolation: how we deal with hundreds of direct connections to the d...
"Scaling in space and time with Temporal", Andriy Lupa .pdf
"Provisioning via DOT-Chain: from catering to drone marketplaces", Volodymyr ...
" Observability with Elasticsearch: Best Practices for High-Load Platform", A...
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
"Istio Ambient Mesh in production: our way from Sidecar to Sidecar-less",Hlib...

Recently uploaded (20)

PPTX
Understanding_Digital_Forensics_Presentation.pptx
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Approach and Philosophy of On baking technology
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Electronic commerce courselecture one. Pdf
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Spectral efficient network and resource selection model in 5G networks
Understanding_Digital_Forensics_Presentation.pptx
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Approach and Philosophy of On baking technology
Chapter 3 Spatial Domain Image Processing.pdf
Encapsulation_ Review paper, used for researhc scholars
Electronic commerce courselecture one. Pdf
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
MYSQL Presentation for SQL database connectivity
Dropbox Q2 2025 Financial Results & Investor Presentation
Per capita expenditure prediction using model stacking based on satellite ima...
Advanced methodologies resolving dimensionality complications for autism neur...
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Agricultural_Statistics_at_a_Glance_2022_0.pdf
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
NewMind AI Monthly Chronicles - July 2025
Spectral efficient network and resource selection model in 5G networks

Tobias Nyholm "Deep dive into Symfony 4 internals"

  • 1. Tobias Nyholm, @tobiasnyholm Deep dive into Symfony4 internals
  • 3. @tobiasnyholm Tobias Nyholm • Full stack unicorn on Happyr.com • Certified Symfony developer • Symfony core member • PHP-Stockholm • Open source
  • 4. @tobiasnyholm Open source PHP-cache HTTPlug Mailgun LinkedIn API clientSwap Stampie BazingaGeocoderBundle PHP-Geocoder FriendsOfApi/boilerplate Guzzle Buzz CacheBundlePSR7 SymfonyBundleTest NSA SimpleBus integrations PSR HTTP clients Neo4j KNP Github API PHP-Translation Puli Assert Backup-manager/symfony php-http/httplug-bundle php-http/multipart-stream php-http/discovery happyr/normal-distribution-bundle nyholm/effective-interest-rate MailgunBundle league/geotools
  • 6. @tobiasnyholm Just a bunch of files <?php // index.php if (isset($_GET['page']) && $_GET['page'] === 'foo') { echo "Foo page <br>"; } else { echo "Welcome to index! <br>"; } if ($_SERVER['REMOTE_ADDR'] === '127.0.0.1') { echo "(admin stuff)"; }
  • 8. @tobiasnyholm Just a bunch of files <?php // index.php if (isset($_GET['page']) && $_GET['page'] === 'foo') { echo "Foo page <br>"; } else { echo "Welcome to index! <br>"; } if ($_SERVER['REMOTE_ADDR'] === '127.0.0.1') { echo "(admin stuff)"; }
  • 10. @tobiasnyholm HTTP objects <?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $query = $request->getQueryParams(); if (isset($query['page']) && $query['page'] === 'foo') { $response = new Response(200, [], 'Foo page'); } else { $response = new Response(200, [], 'Welcome to index!'); } // Send response echo $response->getBody();
  • 13. @tobiasnyholm HTTP objects <?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $query = $request->getQueryParams(); if (isset($query['page']) && $query['page'] === 'foo') { $response = new Response(200, [], 'Foo page'); } else { $response = new Response(200, [], 'Welcome to index!'); } // Send response echo $response->getBody();
  • 17. @tobiasnyholm Controllers<?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/../vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $uri = $request->getUri()->getPath(); if ($uri === '/') { $response = (new AppControllerStartpageController())->run($request); } elseif ($uri === '/foo') { $response = (new AppControllerFooController())->run($request); } else { $response = new Response(404, [], 'Not found'); } // Send response echo $response->getBody();
  • 18. @tobiasnyholm Controllers <?php declare(strict_types=1); namespace AppController; use NyholmPsr7Response; use PsrHttpMessageRequestInterface; class StartpageController { public function run(RequestInterface $request) { return new Response(200, [], 'Welcome to index!'); } }
  • 20. @tobiasnyholm Controllers<?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/../vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $uri = $request->getUri()->getPath(); if ($uri === '/') { $response = (new AppControllerStartpageController())->run($request); } elseif ($uri === '/foo') { $response = (new AppControllerFooController())->run($request); } else { $response = new Response(404, [], 'Not found'); } // Send response echo $response->getBody();
  • 22. @tobiasnyholm Event loop<?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/../vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $response = new Response(); $middlewares[] = new AppMiddlewareRouter(); $runner = (new RelayRelayBuilder())->newInstance($middlewares); $response = $runner($request, $response); // Send response echo $response->getBody();
  • 23. @tobiasnyholmnamespace AppMiddleware; use PsrHttpMessageResponseInterface as Response; use PsrHttpMessageServerRequestInterface as Request; class Router implements MiddlewareInterface { public function __invoke(Request $request, Response $response, callable $next) { $uri = $request->getUri()->getPath(); switch ($uri) { case '/': $response = (new AppControllerStartpageController())->run($request); break; case '/foo': $response = (new AppControllerFooController())->run($request); break; default: $response = $response->withStatus(404); $response->getBody()->write('Not Found'); break; } return $next($request, $response); } }
  • 25. @tobiasnyholm Event loop<?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/../vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $response = new Response(); $middlewares[] = new AppMiddlewareRouter(); $runner = (new RelayRelayBuilder())->newInstance($middlewares); $response = $runner($request, $response); // Send response echo $response->getBody();
  • 27. @tobiasnyholm<?php // Cache.php namespace AppMiddleware; use CacheAdapterApcuApcuCachePool; use PsrCacheCacheItemPoolInterface; use PsrHttpMessageResponseInterface as Response; use PsrHttpMessageServerRequestInterface as Request; class Cache implements MiddlewareInterface { /*** @var CacheItemPoolInterface */ private $cache; /** @var int */ private $ttl; public function __construct() { $this->cache = new ApcuCachePool(); $this->ttl = 300; } public function __invoke(Request $request, Response $response, callable $next) { $uri = $request->getUri(); $cacheKey = 'url'.sha1($uri->getPath().'?'.$uri->getQuery()); $cacheItem = $this->cache->getItem($cacheKey);
  • 28. @tobiasnyholm Cache<?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/../vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $response = new Response(); $middlewares[] = new AppMiddlewareCache(); $middlewares[] = new AppMiddlewareRouter(); $runner = (new RelayRelayBuilder())->newInstance($middlewares); $response = $runner($request, $response); // Send response echo $response->getBody();
  • 30. @tobiasnyholm Cache<?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/../vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $response = new Response(); $middlewares[] = new AppMiddlewareCache(); $middlewares[] = new AppMiddlewareRouter(); $runner = (new RelayRelayBuilder())->newInstance($middlewares); $response = $runner($request, $response); // Send response echo $response->getBody();
  • 32. @tobiasnyholm Container <?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/../vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $response = new Response(); $middlewares[] = new AppMiddlewareCache(); $middlewares[] = new AppMiddlewareRouter(); $runner = (new RelayRelayBuilder())->newInstance($middlewares); $response = $runner($request, $response); // Send response echo $response->getBody();
  • 33. @tobiasnyholm Container <?php // index.php use NyholmPsr7FactoryServerRequestFactory; use NyholmPsr7Response; require __DIR__.'/../vendor/autoload.php'; $request = (new ServerRequestFactory())->createServerRequestFromGlobals(); $kernel = new AppKernel('dev', true); $response = $kernel->handle($request); // Send response echo $response->getBody();
  • 34. <?php // Kernel.php namespace App; use NyholmPsr7Response; use PsrHttpMessageRequestInterface; use PsrHttpMessageResponseInterface; use SymfonyComponentConfigExceptionFileLocatorFileNotFoundException; use SymfonyComponentConfigFileLocator; use SymfonyComponentDependencyInjectionContainer; use SymfonyComponentDependencyInjectionContainerBuilder; use SymfonyComponentDependencyInjectionDumperPhpDumper; use SymfonyComponentDependencyInjectionLoaderYamlFileLoader; class Kernel { private $booted = false; private $debug; private $environment; /** @var Container */ private $container; public function __construct(string $env, bool $debug = false) { $this->debug = $debug; $this->environment = $env; }
  • 40. @tobiasnyholm <?php namespace AppMiddleware; use PsrHttpMessageResponseInterface as Response; use PsrHttpMessageServerRequestInterface as Request; class Router implements MiddlewareInterface { public function __invoke(Request $request, Response $response, callable $next) { $uri = $request->getUri()->getPath(); switch ($uri) { case '/': $response = (new AppControllerStartpageController())->run($request); break; case '/foo': $response = (new AppControllerFooController())->run($request); break; default: $response = $response->withStatus(404); $response->getBody()->write('Not Found'); break; } return $next($request, $response); } }
  • 41. @tobiasnyholm public function __invoke(Request $request, Response $response, callable $next) { $uri = $request->getUri()->getPath(); switch ($uri) { case '/': $response = (new AppControllerStartpageController())->run($request); break; case '/foo': $response = (new AppControllerFooController())->run($request); break; default: $response = $response->withStatus(404); $response->getBody()->write('Not Found'); break; } return $next($request, $response); }
  • 43. public function __invoke(Request $request, Response $response, callable $next) { $uri = $request->getUri()->getPath(); if (0 === strpos($uri, '/admin')) { if (0 === strpos($uri, '/admin/account')) { $response = (new AppControllerManagerController())->accountAction($request); } if (0 === strpos($uri, '/admin/password')) { $response = (new AppControllerManagerController())->passwordAction($request); } } elseif (0 === strpos($uri, '/images')) { if (0 === strpos($uri, '/images/upload')) { $response = (new AppControllerImageController())->uploadAction($request); } if (preg_match('#^/images/(?P<id>[^/]++)/show#sD', $uri, $matches)) { $response = (new AppControllerImageController())->showAction($request, $matches[ } if (preg_match('#^/images/(?P<id>[^/]++)/delete#sD', $uri, $matches)) { $response = (new AppControllerImageController())->deleteAction($request, $matche } } elseif ($uri === '/') { $response = (new AppControllerStartpageController())->run($request); } elseif ($uri === '/foo') { $response = (new AppControllerFooController())->run($request); } else { $response = $response->withStatus(404); $response->getBody()->write('Not Found'); }
  • 44. public function __invoke(Request $request, Response $response, callable $next) { $uri = $request->getUri()->getPath(); $routes = array( '/foo' => 'AppControllerFooController::FooAction', '/images/upload' => 'AppControllerImageController::uploadAction', '/admin/account' => 'AppControllerManagerController::accountAction', '/admin/password' => 'AppControllerManagerController::passwordAction', // More static routes '/' => 'AppControllerStartpageController::startpageAction', ); if (isset($routes[$uri])) { $response = call_user_func($routes[$uri], $request); return $next($request, $response); } $regex = '{^(?' .'|/images/([^/]++)/(?' .'|show(*:31)' .'|delete(*:44)' // my dynamic and complex regex .')' .')$}sD';
  • 47. @tobiasnyholm Security <?php namespace AppMiddleware; use NyholmPsr7Response; use PsrHttpMessageResponseInterface; use PsrHttpMessageServerRequestInterface; class Security implements MiddlewareInterface { public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callabl { $uri = $request->getUri()->getPath(); $ip = $request->getServerParams()['REMOTE_ADDR']; if ($ip !== '127.0.0.1' && $uri === '/admin') { return new Response(403, [], 'Forbidden'); } if ($uri === '/images/4711/delete') { if (true /* user is not "bob" */) { return new Response(403, [], 'Forbidden'); } } return $next($request, $response); } }
  • 48. @tobiasnyholm <?php namespace AppMiddleware; use AppSecurityTokenStorage; use NyholmPsr7Response; use PsrHttpMessageResponseInterface ; use PsrHttpMessageServerRequestInterface; class Authentication implements MiddlewareInterface { private $tokenStorage; /** * * @param $tokenStorage */ public function __construct(TokenStorage $tokenStorage) { $this->tokenStorage = $tokenStorage; } public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable { $uri = $request->getUri()->getPath(); $auth = $request->getServerParams()['PHP_AUTH_USER']??''; $pass = $request->getServerParams()['PHP_AUTH_PW']??'';
  • 49. @tobiasnyholm Securityclass Kernel { // … /** * Handle a Request and turn it in to a response. */ public function handle(RequestInterface $request): ResponseInterface { $this->boot(); $middlewares[] = $this->container->get(‘middleware.auth'); $middlewares[] = $this->container->get('middleware.security'); $middlewares[] = $this->container->get('middleware.cache'); $middlewares[] = new AppMiddlewareRouter(); $runner = (new RelayRelayBuilder())->newInstance($middlewares); return $runner($request, new Response()); } // … }
  • 53. @tobiasnyholm Security <?php declare(strict_types=1); namespace AppController; use AppSecurityTokenStorage; use NyholmPsr7Response; use PsrHttpMessageRequestInterface; class AdminController { private $tokenStorage; public function __construct(TokenStorage $tokenStorage) { $this->tokenStorage = $tokenStorage; } public function run(RequestInterface $request) { return new Response(200, [], sprintf('Hello %s (admin)', $this->tokenStorage->getLastToken()['username'])); } }
  • 55. @tobiasnyholm class SecurityVoters implements MiddlewareInterface { /** @var VoterInterface[] */ private $voters; public function __construct(array $voters) { $this->voters = $voters; } public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable { $deny = 0; foreach ($this->voters as $voter) { $result = $voter->vote($request); switch ($result) { case VoterInterface::ACCESS_GRANTED: return $next($request, $response); case VoterInterface::ACCESS_DENIED: ++$deny; break; default: break; } } if ($deny > 0) { return new Response(403, [], 'Forbidden'); } return $next($request, $response);
  • 56. @tobiasnyholm Security declare(strict_types=1); namespace AppSecurityVoter; use AppSecurityTokenStorage; use PsrHttpMessageServerRequestInterface; class AdminVoter implements VoterInterface { private $tokenStorage; public function __construct(TokenStorage$tokenStorage) { $this->tokenStorage = $tokenStorage; } public function vote(ServerRequestInterface $request) { $uri = $request->getUri()->getPath(); $token = $this->tokenStorage->getLastToken(); if ($token['username'] !== 'Tobias' && $uri === '/admin') { return VoterInterface::ACCESS_DENIED; } return VoterInterface::ACCESS_ABSTAIN; } }
  • 58. @tobiasnyholm Securityclass Kernel { // … /** * Handle a Request and turn it in to a response. */ public function handle(RequestInterface $request): ResponseInterface { $this->boot(); $middlewares[] = $this->container->get(‘middleware.auth'); $middlewares[] = $this->container->get('middleware.security'); $middlewares[] = $this->container->get('middleware.cache'); $middlewares[] = new AppMiddlewareRouter(); $runner = (new RelayRelayBuilder())->newInstance($middlewares); return $runner($request, new Response()); } // … }
  • 63. @tobiasnyholm Autowiringclass Kernel { // … /** * Handle a Request and turn it in to a response. */ public function handle(RequestInterface $request): ResponseInterface { $this->boot(); $middlewares[] = $this->container->get(Authentication::class); $middlewares[] = $this->container->get(SecurityVoters::class); $middlewares[] = $this->container->get(Cache::class); $middlewares[] = new AppMiddlewareRouter(); $runner = (new RelayRelayBuilder())->newInstance($middlewares); return $runner($request, new Response()); } // … }
  • 66. @tobiasnyholmclass Toolbar implements MiddlewareInterface { private $cacheDataCollector; public function __construct(CacheDataCollector $cacheDataCollector) { $this->cacheDataCollector = $cacheDataCollector; } public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable { $calls = $this->cacheDataCollector->getCalls(); $getItemCalls = count($calls['getItem']); $content = $response->getBody()->__toString(); $toolbar = <<<HTML <br><br><br><hr> URL: {$request->getUri()->getPath()}<br> IP: {$request->getServerParams()['REMOTE_ADDR']}<br> Cache calls: {$getItemCalls}<br> HTML; $stream = (new StreamFactory())->createStream($content.$toolbar); $response = $response->withBody($stream); return $next($request, $response); } }
  • 67. @tobiasnyholm<?php namespace AppDataCollector; use PsrCacheCacheItemInterface; use PsrCacheCacheItemPoolInterface; class CacheDataCollector implements CacheItemPoolInterface { /** @var CacheItemPoolInterface */ private $real; private $calls; public function __construct(CacheItemPoolInterface $cache) { $this->real = $cache; } public function getCalls() { return $this->calls; } public function getItem($key) { $this->calls['getItem'][] = ['key'=>$key]; return $this->real->getItem($key); }
  • 69. @tobiasnyholmclass Toolbar implements MiddlewareInterface { private $cacheDataCollector; public function __construct(CacheDataCollector $cacheDataCollector) { $this->cacheDataCollector = $cacheDataCollector; } public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable { $calls = $this->cacheDataCollector->getCalls(); $getItemCalls = count($calls['getItem']); $content = $response->getBody()->__toString(); $toolbar = <<<HTML <br><br><br><hr> URL: {$request->getUri()->getPath()}<br> IP: {$request->getServerParams()['REMOTE_ADDR']}<br> Cache calls: {$getItemCalls}<br> HTML; $stream = (new StreamFactory())->createStream($content.$toolbar); $response = $response->withBody($stream); return $next($request, $response); } }
  • 71. @tobiasnyholm Exception <?php declare(strict_types=1); namespace AppController; use NyholmPsr7Response; use PsrHttpMessageRequestInterface; class ExceptionController { public function run(RequestInterface $request) { throw new RuntimeException('This is an exception'); } }
  • 72. @tobiasnyholm Exception <?php namespace AppMiddleware; use CacheAdapterApcuApcuCachePool; use NyholmPsr7Response; use PsrCacheCacheItemPoolInterface; use PsrHttpMessageResponseInterface; use PsrHttpMessageServerRequestInterface; class ExceptionHandler implements MiddlewareInterface { public function __invoke(ServerRequestInterface $request, ResponseInterface $response, calla { try { $response = $next($request, $response); } catch (Throwable $exception) { $response = new Response(500, [], $exception->getMessage()); } return $response; } }
  • 73. @tobiasnyholm Exceptionclass Kernel { /** * Handle a Request and turn it in to a response. */ public function handle(RequestInterface $request): ResponseInterface { $this->boot(); $middlewares[] = $this->container->get(ExceptionHandler::class); $middlewares[] = $this->container->get(Authentication::class); $middlewares[] = $this->container->get(SecurityVoters::class); $middlewares[] = $this->container->get(Cache::class); $middlewares[] = new AppMiddlewareRouter(); $middlewares[] = $this->container->get(Toolbar::class); $runner = (new RelayRelayBuilder())->newInstance($middlewares); return $runner($request, new Response()); } }
  • 75. @tobiasnyholm Exception <?php namespace AppMiddleware; use CacheAdapterApcuApcuCachePool; use NyholmPsr7Response; use PsrCacheCacheItemPoolInterface; use PsrHttpMessageResponseInterface; use PsrHttpMessageServerRequestInterface; class ExceptionHandler implements MiddlewareInterface { public function __invoke(ServerRequestInterface $request, ResponseInterface $response, calla { try { $response = $next($request, $response); } catch (Throwable $exception) { $response = new Response(500, [], $exception->getMessage()); } return $response; } }
  • 91. @tobiasnyholm What is Symfony? nyholm/psr7 symfony/http-foundation relay/relay symfony/event-dispatcher php-cache/* symfony/cache Custom kernel symfony/http-kernel Custom security symfony/security