SlideShare uma empresa Scribd logo
1 de 110
Baixar para ler offline
Developing cacheable
PHP applications
Thijs Feryn
Slow websites suck
Hi, I’m Thijs
I’m
@ThijsFeryn
on Twitter
I’m an
Evangelist
At
I’m an
Evangelist
At
Cache
Don’t
recompute
if the data
hasn’t
changed
Whatifwe
coulddesign
oursoftware
withHTTP
cachingin
mind?
✓Portability
✓Developer empowerment
✓Control
✓Consistent caching behavior
Caching state of mind
Reverse
caching
proxy
Normally
User Server
With ReCaPro *
User ReCaPro Server
* Reverse Caching Proxy
Content Delivery Network
HTTP caching mechanisms
Expires: Sat, 09 Sep 2017 14:30:00 GMT
Cache-control: public, max-age=3600,
s-maxage=86400
Cache-control: private, no-cache, no-store
Inanidealworld
✓Stateless
✓Well-defined TTL
✓Cache / no-cache per resource
✓Cache variations
✓Conditional requests
✓HTTP fragments for non-
cacheable content
In an ideal world
Reality
sucks
Common
problems
Time To Live
Cache variations
Authentication
Legacy
Twig templates
✓Multi-lingual (Accept-Language)
✓Nav
✓Header
✓Footer
✓Main
✓Login page & private content
composer require symfony/flex
composer require annotations twig translation
<?php
namespace AppController;
use SymfonyComponentHttpFoundationRequest;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SymfonyBundleFrameworkBundleControllerController;
class DefaultController extends Controller
{
/**
* @Route("/", name="home")
*/
public function index()
{
return $this->render('index.twig');
}
}
src/Controller/DefaultController.php
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html" xmlns:hx="http://purl.org/NET/hinclude">
<head>
<title>{% block title %}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
</head>
<body>
<div class="container-fluid">
{{ include('header.twig') }}
<div class="row">
<div class="col-sm-3 col-lg-2">
{{ include('nav.twig') }}
</div>
<div class="col-sm-9 col-lg-10">
{% block content %}{% endblock %}
</div>
</div>
{{ include('footer.twig') }}
</div>
</body>
</html>
templates/base.twig
{% extends "base.twig" %}
{% block title %}Home{% endblock %}
{% block content %}
<div class="page-header">
<h1>{{ 'example' | trans }}</h1>
</div>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris consequat orci
eget libero sollicitudin, non ultrices turpis mollis. Aliquam sit amet tempus elit.
Ut viverra risus enim, ut venenatis justo accumsan nec. Praesent a dolor tellus.
Maecenas non mauris leo. Pellentesque lobortis turpis at dapibus laoreet. Mauris
rhoncus nulla et urna mollis, et lobortis magna ornare. Etiam et sapien consequat,
egestas felis sit amet, dignissim enim.</p>
<p>Quisque quis mollis justo, imperdiet fermentum velit. Aliquam nulla justo,
consectetur et diam non, luctus commodo metus. Vestibulum fermentum efficitur nulla
non luctus. Nunc lorem nunc, mollis id efficitur et, aliquet sit amet ante. Sed
ipsum turpis, vehicula eu semper eu, malesuada eget leo. Vestibulum venenatis dui
id pulvinar suscipit. Etiam nec massa pharetra justo pharetra dignissim quis non
magna. Integer id convallis lectus. Nam non ullamcorper metus. Ut vestibulum ex ut
massa posuere tincidunt. Vestibulum hendrerit neque id lorem rhoncus aliquam. Duis
a facilisis metus, a faucibus nulla.</p>
{% endblock %}
templates/index.twig
<nav class="navbar navbar-default navbar-fixed-side">
<ul class="nav">
<li><a href="{{ url('home') }}">Home</a></li>
<li><a href="{{ url('login') }}">{{ 'log_in' | trans }}</a></li>
<li><a href="{{ url('private') }}">Private</a></li>
</ul>
</nav>
templates/nav.twig
<footer>
<hr />
<small>Footer</small>
</footer>
templates/footer.twig
home: Home
welcome : Welcome to the site
rendered : Rendered at %date%
example : An example page
log_in : Log in
login : Login
log_out : Log out
username : Username
password : Password
private : Private
privatetext : Looks like some very private data
translations/messages.en.yml
home: Start
welcome : Welkom op de site
rendered : Samengesteld op %date%
example : Een voorbeeldpagina
log_in : Inloggen
login : Login
log_out : Uitloggen
username : Gebruikersnaam
password : Wachtwoord
private : Privé
privatetext : Deze tekst ziet er vrij privé uit
translations/messages.nl.yml
<?php
namespace AppEventListener;
use SymfonyComponentHttpKernelEventGetResponseEvent;
class LocaleListener
{
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$preferredLanguage = $request->getPreferredLanguage();
if(null !== $preferredLanguage) {
$request->setLocale($preferredLanguage);
}
}
}
src/EventListener/LocaleListener.php
services:
_defaults:
autowire: true
autoconfigure: true
public: false
App:
resource: '../src/*'
exclude: '../src/{Entity,Migrations,Tests,Kernel.php}'
AppController:
resource: '../src/Controller'
tags: ['controller.service_arguments']
AppEventListenerLocaleListener:
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 100}
config/services.yml
Accept-Language: en
Accept-Language: nl
VS
The mission
Maximum
Cacheability
Cache-control
Cache-Control: public, s-maxage=500
<?php
namespace AppController;
use SymfonyComponentHttpFoundationRequest;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SymfonyBundleFrameworkBundleControllerController;
class DefaultController extends Controller
{
/**
* @Route("/", name="home")
*/
public function index()
{
return $this
->render('index.twig')
->setSharedMaxAge(500)
->setPublic();
}
}
Conditional
requests
Only fetch
payload that has
changed
HTTP/1.1 200 OK
Otherwise:
HTTP/1.1 304 Not Modified
Conditional requests
HTTP/1.1 200 OK
Host: localhost
Etag: 7c9d70604c6061da9bb9377d3f00eb27
Content-type: text/html; charset=UTF-8
Hello world output
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.48.0
Conditional requests
HTTP/1.0 304 Not Modified
Host: localhost
Etag: 7c9d70604c6061da9bb9377d3f00eb27
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.48.0
If-None-Match:
7c9d70604c6061da9bb9377d3f00eb27
Conditional requests
HTTP/1.1 200 OK
Host: localhost
Last-Modified: Fri, 22 Jul 2016 10:11:16 GMT
Content-type: text/html; charset=UTF-8
Hello world output
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.48.0
Conditional requests
HTTP/1.0 304 Not Modified
Host: localhost
Last-Modified: Fri, 22 Jul 2016 10:11:16 GMT
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.48.0
If-Last-Modified: Fri, 22 Jul 2016 10:11:16
GMT
composer require symfony-bundles/redis-bundle
<?php
namespace AppEventListener;
use SymfonyBridgeMonologLogger;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpKernelEventGetResponseEvent;
use SymfonyComponentHttpKernelEventFilterResponseEvent;
use SymfonyBundlesRedisBundleRedisClient as RedisClient;
class ConditionalRequestListener
{
protected $redis;
protected $logger;
public function __construct(RedisClient $redis, Logger $logger)
{
$this->redis = $redis;
$this->logger = $logger;
}
protected function isModified(Request $request, $etag)
{
if ($etags = $request->getETags()) {
return in_array($etag, $etags) || in_array('*', $etags);
}
return true;
}
...
src/EventListener/ConditionalRequestListener.php
{
$this->redis = $redis;
$this->logger = $logger;
}
protected function isModified(Request $request, $etag)
{
if ($etags = $request->getETags()) {
return in_array($etag, $etags) || in_array('*', $etags);
}
return true;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$etag = $this->redis->get('etag:'.md5($request->getUri()));
if(!$this->isModified($request,$etag)) {
$event->setResponse(Response::create('Not Modified',Response::HTTP_NOT_MODIFIED));
}
}
public function onKernelResponse(FilterResponseEvent $event)
{
$response = $event->getResponse();
$request = $event->getRequest();
$etag = md5($response->getContent());
$response->setEtag($etag);
if($this->isModified($request,$etag)) {
$this->redis->set('etag:'.md5($request->getUri()),$etag);
}
}
}
src/EventListener/ConditionalRequestListener.php
Do not cache
Cache-Control: private, no-store
/**
* @Route("/private", name="private")
*/
public function private()
{
$response = $this
->render('private.twig')
->setPrivate();
$response->headers->addCacheControlDirective('no-store');
return $response;
}
Dot not
cache private
page
session
cookie
Nocache
Code
renders
singleHTTP
response
Lowest
common
denominator:
nocache
Blockcaching
<esi:include src="/header" />
Edge Side Includes
✓Placeholder
✓Parsed by Varnish
✓Output is a composition of blocks
✓State per block
✓TTL per block
Surrogate-Capability: key="ESI/1.0"
Surrogate-Control: content="ESI/1.0"
Varnish
Backend
<esi:include src="/header" />
Parse ESI placeholdersVarnish
ESI
vs
AJAX
✓ Server-side
✓ Standardized
✓ Processed on the
“edge”, no in the
browser
✓ Generally faster
Edge-Side Includes
- Sequential
- One fails, all fail
- Limited
implementation in
Varnish
✓ Client-side
✓ Common knowledge
✓ Parallel processing
✓ Graceful
degradation
AJAX
- Processed by the
browser
- Extra roundtrips
- Somewhat slower
Subrequests
<div class="container-fluid">

{{ include('header.twig') }}

<div class="row">

<div class="col-sm-3 col-lg-2">

{{ include('nav.twig') }}

</div>

<div class="col-sm-9 col-lg-10">

{% block content %}{% endblock %}

</div>

</div>

{{ include('footer.twig') }}

</div>
<div class="container-fluid">

{{ render_esi(url('header')) }}

<div class="row">

<div class="col-sm-3 col-lg-2">

{{ render_esi(url('nav')) }}

</div>

<div class="col-sm-9 col-lg-10">

{% block content %}{% endblock %}

</div>

</div>

{{ render_esi(url('footer')) }}

</div>
<div class="container-fluid">
<esi:include src="/header" />
<div class="row">
<div class="col-sm-3 col-lg-2">
<esi:include src="/nav" />
</div>
<div class="col-sm-9 col-lg-10">
<div class="page-header">
<h1>An example page <small>Rendered at 2017-05-17 16:57:14</small></h1>
</div>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris consequat orci eget libero
sollicitudin,…</p>
</div>
</div>
<esi:include src="/footer" />
</div>
/**
* @Route("/", name="home")
*/
public function index()
{
return $this
->render('index.twig')
->setPublic()
->setSharedMaxAge(500);
}
/**
* @Route("/header", name="header")
*/
public function header()
{
$response = $this
->render('header.twig')
->setPrivate();
$response->headers->addCacheControlDirective('no-store');
return $response;
}
/**
* @Route("/footer", name="footer")
*/
public function footer()
{
$response = $this->render('footer.twig');
$response
->setSharedMaxAge(500)
->setPublic();
return $response;
}
/**
* @Route("/nav", name="nav")
*/
public function nav()
{
$response = $this->render('nav.twig');
$response
->setVary('X-Login',false)
->setSharedMaxAge(500)
->setPublic();
return $response;
}
Controller
action per
fragment
Problem:
no language
cache
variation
Vary: Accept-Language
<?php
namespace AppEventListener;
use SymfonyComponentHttpKernelEventFilterResponseEvent;
class VaryListener
{
public function onKernelResponse(FilterResponseEvent $event)
{
$response = $event->getResponse();
$response->setVary('Accept-Language',false);
}
}
src/EventListener/VaryListener.php
✓Navigation page
✓Private page
Weak spots
Not cached
because of
stateful content
Move state client-side
Replace PHP session with
JSON Web Tokens
JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pb
iIsImV4cCI6MTQ5NTUyODc1NiwibG9naW4iOnRydWV9.u4Idy-
SYnrFdnH1h9_sNc4OasORBJcrh2fPo1EOTre8
✓3 parts
✓Dot separated
✓Base64 encoded JSON
✓Header
✓Payload
✓Signature (HMAC with secret)
eyJzdWIiOiJhZG1pbiIsIm
V4cCI6MTQ5NTUyODc1Niwi
bG9naW4iOnRydWV9
{
"alg": "HS256",
 "typ": "JWT"
}
{
"sub": "admin",
"exp": 1495528756,
"login": true
}
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
eyJhbGciOiJIUzI1NiIsI
nR5cCI6IkpXVCJ9
u4Idy-
SYnrFdnH1h9_sNc4OasOR
BJcrh2fPo1EOTre8
JWT
Cookie:token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJhZG1pbiIsImV4cCI6MTQ5NTUyODc1NiwibG9naW4iOnRydW
V9.u4Idy-SYnrFdnH1h9_sNc4OasORBJcrh2fPo1EOTre8
✓Stored in a cookie
✓Can be validated by Varnish
✓Payload can be processed by any
language (e.g. Javascript)
sub jwt {
std.log("Ready to perform some JWT magic");
if(cookie.isset("jwt_cookie")) {
#Extract header data from JWT
var.set("token", cookie.get("jwt_cookie"));
var.set("header", regsub(var.get("token"),"([^.]+).[^.]+.[^.]+","1"));
var.set("type", regsub(digest.base64url_decode(var.get("header")),{"^.*?"typ"s*:s*"(w+)".*?$"},"1"));
var.set("algorithm", regsub(digest.base64url_decode(var.get("header")),{"^.*?"alg"s*:s*"(w+)".*?$"},"1"));
#Don't allow invalid JWT header
if(var.get("type") == "JWT" && var.get("algorithm") == "HS256") {
#Extract signature & payload data from JWT
var.set("rawPayload",regsub(var.get("token"),"[^.]+.([^.]+).[^.]+$","1"));
var.set("signature",regsub(var.get("token"),"^[^.]+.[^.]+.([^.]+)$","1"));
var.set("currentSignature",digest.base64url_nopad_hex(digest.hmac_sha256(var.get("key"),var.get("header") + "." + var.get("rawPayload"))));
var.set("payload", digest.base64url_decode(var.get("rawPayload")));
var.set("exp",regsub(var.get("payload"),{"^.*?"exp"s*:s*([0-9]+).*?$"},"1"));
var.set("jti",regsub(var.get("payload"),{"^.*?"jti"s*:s*"([a-z0-9A-Z_-]+)".*?$"},"1"));
var.set("userId",regsub(var.get("payload"),{"^.*?"uid"s*:s*"([0-9]+)".*?$"},"1"));
var.set("roles",regsub(var.get("payload"),{"^.*?"roles"s*:s*"([a-z0-9A-Z_-, ]+)".*?$"},"1"));
#Only allow valid userId
if(var.get("userId") ~ "^d+$") {
#Don't allow expired JWT
if(std.time(var.get("exp"),now) >= now) {
#SessionId should match JTI value from JWT
if(cookie.get(var.get("sessionCookie")) == var.get("jti")) {
#Don't allow invalid JWT signature
if(var.get("signature") == var.get("currentSignature")) {
#The sweet spot
set req.http.X-login="true";
} else {
std.log("JWT: signature doesn't match. Received: " + var.get("signature") + ", expected: " + var.get("currentSignature"));
}
} else {
std.log("JWT: session cookie doesn't match JTI." + var.get("sessionCookie") + ": " + cookie.get(var.get("sessionCookie")) + ", JTI:" + var.get("jti"));
}
} else {
std.log("JWT: token has expired");
}
} else {
std.log("UserId '"+ var.get("userId") +"', is not numeric");
}
} else {
std.log("JWT: type is not JWT or algorithm is not HS256");
}
std.log("JWT processing finished. UserId: " + var.get("userId") + ". X-Login: " + req.http.X-login);
}
#Look for full private content
if(req.url ~ "/node/2" && req.url !~ "^/user/login") {
if(req.http.X-login != "true") {
return(synth(302,"/user/login?destination=" + req.url));
}
}
}
Insert incomprehensible
Varnish VCL code here …
X-Login: true
End result:
X-Login: false
Custom request
header set by
Varnish
Extra cache
variation
required
Vary: Accept-Language, X-Login
Content for logged-in
& anonymous differs
<script language="JavaScript">

function getCookie(name) {

var value = "; " + document.cookie;

var parts = value.split("; " + name + "=");

if (parts.length == 2) return parts.pop().split(";").shift();

}

function parseJwt (token) {

var base64Url = token.split('.')[1];

var base64 = base64Url.replace('-', '+').replace('_', '/');

return JSON.parse(window.atob(base64));

};

$(document).ready(function(){

if ($.cookie('token') != null ){

var token = parseJwt($.cookie("token"));

$("#usernameLabel").html(', ' + token.sub);

}

});

</script>
Parse JWT
in Javascript
Does not require
backend access
composer require firebase/php-jwt
<?php
namespace AppService;
use FirebaseJWTJWT;
use SymfonyComponentHttpFoundationCookie;
class JwtAuthentication
{
protected $key;
protected $username;
protected $password;
public function __construct($key,$username,$password)
{
$this->key = $key;
$this->username = $username;
$this->password = $password;
}
public function jwt($username)
{
return JWT::encode([
'sub'=>$username,
'exp'=>time() + (4 * 24 * 60 * 60),
'login'=>true,
],$this->key);
}
public function createCookie($username)
{
return new Cookie("token",$this->jwt($username), time() + (3600 * 48), '/', null,
false, false);
}
public function validate($token)
{
src/Service/JwtAuthentication.php
$this->password = $password;
}
public function jwt($username)
{
return JWT::encode([
'sub'=>$username,
'exp'=>time() + (4 * 24 * 60 * 60),
//'exp'=>time() + 60,
'login'=>true,
],$this->key);
}
public function createCookie($username)
{
return new Cookie("token",$this->jwt($username), time() + (3600 * 48), '/', null,
false, false);
}
public function validate($token)
{
try {
$data = JWT::decode($token,$this->key,['HS256']);
$data = (array)$data;
if($data['sub'] !== $this->username) {
return false;
}
return true;
} catch(UnexpectedValueException $e) {
return false;
}
}
}
src/Service/JwtAuthentication.php
services:
AppServiceJwtAuthentication:
arguments:
$key: '%env(JWT_KEY)%'
$username: '%env(JWT_USERNAME)%'
$password: '%env(JWT_PASSWORD)%'
src/Service/JwtAuthentication.php
###> JWT authentication ###
JWT_KEY=SlowWebSitesSuck
JWT_USERNAME=admin
JWT_PASSWORD=$2y$10$431rvq1qS9ewNFP0Gti/o.kBbuMK4zs8IDTLlxm5uzV7cbv8wKt0K
###< JWT authentication ###
.env
/**
* @Route("/login", name="login", methods="GET")
*/
public function login(Request $request, JwtAuthentication $jwt)
{
if($jwt->validate($request->cookies->get('token'))) {
return new RedirectResponse($this->generateUrl('home'));
}
$response = $this->render('login.twig',['loginLogoutUrl'=>$this-
>generateUrl('login'),'loginLogoutLabel'=>'log_in']);
$response
->setSharedMaxAge(500)
->setVary('X-Login',false)
->setPublic();
return $response;
}
/**
* @Route("/login", name="loginpost", methods="POST")
*/
public function loginpost(Request $request, JwtAuthentication $jwt)
{
$username = $request->get('username');
$password = $request->get('password');
if(!$username || !$password || getenv('JWT_USERNAME') != $username || !
password_verify($password,getenv('JWT_PASSWORD'))) {
return new RedirectResponse($this->generateUrl('login'));
}
$response = new RedirectResponse($this->generateUrl('home'));
$response->headers->setCookie($jwt->createCookie($username));
return $response;
}
src/Controller/DefaultController.php
/**
* @Route("/logout", name="logout")
*/
public function logout()
{
$response = new RedirectResponse($this->generateUrl('login'));
$response->headers->clearCookie('token');
return $response;
}
src/Controller/DefaultController.php
/**
* @Route("/nav", name="nav")
*/
public function nav(Request $request, JwtAuthentication $jwt)
{
if($jwt->validate($request->cookies->get('token'))) {
$loginLogoutUrl = $loginLogoutUrl = $this->generateUrl('logout');
$loginLogoutLabel = 'log_out';
} else {
$loginLogoutUrl = $this->generateUrl('login');
$loginLogoutLabel = 'log_in';
}
$response = $this->render('nav.twig',
['loginLogoutUrl'=>$loginLogoutUrl,'loginLogoutLabel'=>$loginLogoutLabel]);
$response
->setVary('X-Login',false)
->setSharedMaxAge(500)
->setPublic();
return $response;
}
src/Controller/DefaultController.php
{% extends "base.twig" %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="page-header">
<h1>{{ 'log_in' | trans }}</h1>
</div>
<form class="form-horizontal" method="post" action="{{ url('loginpost') }}">
<div class="form-group">
<label for="usernameInput" class="col-sm-2 control-label">{{ 'username' | trans }}</label>
<div class="col-sm-6">
<input type="text" name="username" class="form-control" id="usernameInput"
placeholder="{{ 'username' | trans }}">
</div>
</div>
<div class="form-group">
<label for="passwordInput" class="col-sm-2 control-label">{{ 'password' | trans }}</label>
<div class="col-sm-6">
<input type="password" name="password" class="form-control" id="passwordInput"
placeholder="{{ 'password' | trans }}">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">{{ 'log_in' | trans }}</button>
</div>
</div>
</form>
{% endblock %}
templates/login.twig
Cookie:
token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1Ni
J9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTUyMDk0M
jExNSwibG9naW4iOnRydWV9._161Lf8BKkbzjqVv
5d62O5aMdCotKvCqd7F8qFqZC2Y
With the proper VCL, Varnish
can process the JWT and
make cache variations for
authenticated & anonymous
content
https://github.com/ThijsFeryn/
cacheable-sites-symfony4
https://feryn.eu
https://twitter.com/ThijsFeryn
https://instagram.com/ThijsFeryn
Developing cacheable PHP applications - Confoo 2018

Mais conteúdo relacionado

Mais procurados

LCA2014 - Introduction to Go
LCA2014 - Introduction to GoLCA2014 - Introduction to Go
LCA2014 - Introduction to Godreamwidth
 
Advanced HTTP Caching
Advanced HTTP CachingAdvanced HTTP Caching
Advanced HTTP CachingMartin Breest
 
Northeast PHP - High Performance PHP
Northeast PHP - High Performance PHPNortheast PHP - High Performance PHP
Northeast PHP - High Performance PHPJonathan Klein
 
ApacheConNA 2015: What's new in Apache httpd 2.4
ApacheConNA 2015: What's new in Apache httpd 2.4ApacheConNA 2015: What's new in Apache httpd 2.4
ApacheConNA 2015: What's new in Apache httpd 2.4Jim Jagielski
 
Devoxx Maroc 2015 HTTP 1, HTTP 2 and folks
Devoxx Maroc  2015 HTTP 1, HTTP 2 and folksDevoxx Maroc  2015 HTTP 1, HTTP 2 and folks
Devoxx Maroc 2015 HTTP 1, HTTP 2 and folksNicolas Martignole
 
HipHop VM: overclocking Symfony
HipHop VM: overclocking SymfonyHipHop VM: overclocking Symfony
HipHop VM: overclocking SymfonyVadim Borodavko
 
PyGotham 2014 Introduction to Profiling
PyGotham 2014 Introduction to ProfilingPyGotham 2014 Introduction to Profiling
PyGotham 2014 Introduction to ProfilingPerrin Harkins
 
Interactive web. O rly?
Interactive web. O rly?Interactive web. O rly?
Interactive web. O rly?timbc
 
Scaling PHP to 40 Million Uniques
Scaling PHP to 40 Million UniquesScaling PHP to 40 Million Uniques
Scaling PHP to 40 Million UniquesJonathan Klein
 
PSGI and Plack from first principles
PSGI and Plack from first principlesPSGI and Plack from first principles
PSGI and Plack from first principlesPerl Careers
 
Altitude San Francisco 2018: Programming the Edge
Altitude San Francisco 2018: Programming the EdgeAltitude San Francisco 2018: Programming the Edge
Altitude San Francisco 2018: Programming the EdgeFastly
 
AnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time webAnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time webclkao
 
"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr VronskiyFwdays
 
Site Performance Optimization for Joomla #jwc13
Site Performance Optimization for Joomla #jwc13Site Performance Optimization for Joomla #jwc13
Site Performance Optimization for Joomla #jwc13Hans Kuijpers
 
Adventures in infrastructure as code
Adventures in infrastructure as codeAdventures in infrastructure as code
Adventures in infrastructure as codeJulian Simpson
 
Genkidama:実装と課題
Genkidama:実装と課題Genkidama:実装と課題
Genkidama:実装と課題Takuya ASADA
 
HTTP/2, SPDY e Otimizações Web - Front In Maceió 2014 - Sérgio Lopes
HTTP/2, SPDY e Otimizações Web - Front In Maceió 2014 - Sérgio LopesHTTP/2, SPDY e Otimizações Web - Front In Maceió 2014 - Sérgio Lopes
HTTP/2, SPDY e Otimizações Web - Front In Maceió 2014 - Sérgio LopesCaelum
 
June8 presentation
June8 presentationJune8 presentation
June8 presentationnicobn
 

Mais procurados (20)

Running php on nginx
Running php on nginxRunning php on nginx
Running php on nginx
 
LCA2014 - Introduction to Go
LCA2014 - Introduction to GoLCA2014 - Introduction to Go
LCA2014 - Introduction to Go
 
Advanced HTTP Caching
Advanced HTTP CachingAdvanced HTTP Caching
Advanced HTTP Caching
 
Northeast PHP - High Performance PHP
Northeast PHP - High Performance PHPNortheast PHP - High Performance PHP
Northeast PHP - High Performance PHP
 
ApacheConNA 2015: What's new in Apache httpd 2.4
ApacheConNA 2015: What's new in Apache httpd 2.4ApacheConNA 2015: What's new in Apache httpd 2.4
ApacheConNA 2015: What's new in Apache httpd 2.4
 
Devoxx Maroc 2015 HTTP 1, HTTP 2 and folks
Devoxx Maroc  2015 HTTP 1, HTTP 2 and folksDevoxx Maroc  2015 HTTP 1, HTTP 2 and folks
Devoxx Maroc 2015 HTTP 1, HTTP 2 and folks
 
HipHop VM: overclocking Symfony
HipHop VM: overclocking SymfonyHipHop VM: overclocking Symfony
HipHop VM: overclocking Symfony
 
PyGotham 2014 Introduction to Profiling
PyGotham 2014 Introduction to ProfilingPyGotham 2014 Introduction to Profiling
PyGotham 2014 Introduction to Profiling
 
Elastic stack
Elastic stackElastic stack
Elastic stack
 
Interactive web. O rly?
Interactive web. O rly?Interactive web. O rly?
Interactive web. O rly?
 
Scaling PHP to 40 Million Uniques
Scaling PHP to 40 Million UniquesScaling PHP to 40 Million Uniques
Scaling PHP to 40 Million Uniques
 
PSGI and Plack from first principles
PSGI and Plack from first principlesPSGI and Plack from first principles
PSGI and Plack from first principles
 
Altitude San Francisco 2018: Programming the Edge
Altitude San Francisco 2018: Programming the EdgeAltitude San Francisco 2018: Programming the Edge
Altitude San Francisco 2018: Programming the Edge
 
AnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time webAnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time web
 
"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy
 
Site Performance Optimization for Joomla #jwc13
Site Performance Optimization for Joomla #jwc13Site Performance Optimization for Joomla #jwc13
Site Performance Optimization for Joomla #jwc13
 
Adventures in infrastructure as code
Adventures in infrastructure as codeAdventures in infrastructure as code
Adventures in infrastructure as code
 
Genkidama:実装と課題
Genkidama:実装と課題Genkidama:実装と課題
Genkidama:実装と課題
 
HTTP/2, SPDY e Otimizações Web - Front In Maceió 2014 - Sérgio Lopes
HTTP/2, SPDY e Otimizações Web - Front In Maceió 2014 - Sérgio LopesHTTP/2, SPDY e Otimizações Web - Front In Maceió 2014 - Sérgio Lopes
HTTP/2, SPDY e Otimizações Web - Front In Maceió 2014 - Sérgio Lopes
 
June8 presentation
June8 presentationJune8 presentation
June8 presentation
 

Semelhante a Developing cacheable PHP applications - Confoo 2018

ApacheCon 2014 - What's New in Apache httpd 2.4
ApacheCon 2014 - What's New in Apache httpd 2.4ApacheCon 2014 - What's New in Apache httpd 2.4
ApacheCon 2014 - What's New in Apache httpd 2.4Jim Jagielski
 
Caching the Uncacheable
Caching the UncacheableCaching the Uncacheable
Caching the Uncacheabledanrot
 
Less and faster – Cache tips for WordPress developers
Less and faster – Cache tips for WordPress developersLess and faster – Cache tips for WordPress developers
Less and faster – Cache tips for WordPress developersSeravo
 
Drupal Performance - SerBenfiquista.com Case Study
Drupal Performance - SerBenfiquista.com Case StudyDrupal Performance - SerBenfiquista.com Case Study
Drupal Performance - SerBenfiquista.com Case Studyhernanibf
 
REST in ( a mobile ) peace @ WHYMCA 05-21-2011
REST in ( a mobile ) peace @ WHYMCA 05-21-2011REST in ( a mobile ) peace @ WHYMCA 05-21-2011
REST in ( a mobile ) peace @ WHYMCA 05-21-2011Alessandro Nadalin
 
What's New and Newer in Apache httpd-24
What's New and Newer in Apache httpd-24What's New and Newer in Apache httpd-24
What's New and Newer in Apache httpd-24Jim Jagielski
 
Apache and PHP: Why httpd.conf is your new BFF!
Apache and PHP: Why httpd.conf is your new BFF!Apache and PHP: Why httpd.conf is your new BFF!
Apache and PHP: Why httpd.conf is your new BFF!Jeff Jones
 
WordPress At Scale. WordCamp Dhaka 2019
WordPress At Scale. WordCamp Dhaka 2019WordPress At Scale. WordCamp Dhaka 2019
WordPress At Scale. WordCamp Dhaka 2019Anam Ahmed
 
VUG5: Varnish at Opera Software
VUG5: Varnish at Opera SoftwareVUG5: Varnish at Opera Software
VUG5: Varnish at Opera SoftwareCosimo Streppone
 
Ajax to the Moon
Ajax to the MoonAjax to the Moon
Ajax to the Moondavejohnson
 
Caching and tuning fun for high scalability
Caching and tuning fun for high scalabilityCaching and tuning fun for high scalability
Caching and tuning fun for high scalabilityWim Godden
 
Altitude SF 2017: Optimizing your hit rate
Altitude SF 2017: Optimizing your hit rateAltitude SF 2017: Optimizing your hit rate
Altitude SF 2017: Optimizing your hit rateFastly
 
Modern javascript localization with c-3po and the good old gettext
Modern javascript localization with c-3po and the good old gettextModern javascript localization with c-3po and the good old gettext
Modern javascript localization with c-3po and the good old gettextAlexander Mostovenko
 
MNPHP Scalable Architecture 101 - Feb 3 2011
MNPHP Scalable Architecture 101 - Feb 3 2011MNPHP Scalable Architecture 101 - Feb 3 2011
MNPHP Scalable Architecture 101 - Feb 3 2011Mike Willbanks
 
20190516 web security-basic
20190516 web security-basic20190516 web security-basic
20190516 web security-basicMksYi
 
Caching with Varnish
Caching with VarnishCaching with Varnish
Caching with Varnishschoefmax
 
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Codemotion
 
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Codemotion
 
OWASP Top 10 - Checkmarx Presentation at Polytechnic Institute of Cávado and Ave
OWASP Top 10 - Checkmarx Presentation at Polytechnic Institute of Cávado and AveOWASP Top 10 - Checkmarx Presentation at Polytechnic Institute of Cávado and Ave
OWASP Top 10 - Checkmarx Presentation at Polytechnic Institute of Cávado and AveCheckmarx
 
Choosing A Proxy Server - Apachecon 2014
Choosing A Proxy Server - Apachecon 2014Choosing A Proxy Server - Apachecon 2014
Choosing A Proxy Server - Apachecon 2014bryan_call
 

Semelhante a Developing cacheable PHP applications - Confoo 2018 (20)

ApacheCon 2014 - What's New in Apache httpd 2.4
ApacheCon 2014 - What's New in Apache httpd 2.4ApacheCon 2014 - What's New in Apache httpd 2.4
ApacheCon 2014 - What's New in Apache httpd 2.4
 
Caching the Uncacheable
Caching the UncacheableCaching the Uncacheable
Caching the Uncacheable
 
Less and faster – Cache tips for WordPress developers
Less and faster – Cache tips for WordPress developersLess and faster – Cache tips for WordPress developers
Less and faster – Cache tips for WordPress developers
 
Drupal Performance - SerBenfiquista.com Case Study
Drupal Performance - SerBenfiquista.com Case StudyDrupal Performance - SerBenfiquista.com Case Study
Drupal Performance - SerBenfiquista.com Case Study
 
REST in ( a mobile ) peace @ WHYMCA 05-21-2011
REST in ( a mobile ) peace @ WHYMCA 05-21-2011REST in ( a mobile ) peace @ WHYMCA 05-21-2011
REST in ( a mobile ) peace @ WHYMCA 05-21-2011
 
What's New and Newer in Apache httpd-24
What's New and Newer in Apache httpd-24What's New and Newer in Apache httpd-24
What's New and Newer in Apache httpd-24
 
Apache and PHP: Why httpd.conf is your new BFF!
Apache and PHP: Why httpd.conf is your new BFF!Apache and PHP: Why httpd.conf is your new BFF!
Apache and PHP: Why httpd.conf is your new BFF!
 
WordPress At Scale. WordCamp Dhaka 2019
WordPress At Scale. WordCamp Dhaka 2019WordPress At Scale. WordCamp Dhaka 2019
WordPress At Scale. WordCamp Dhaka 2019
 
VUG5: Varnish at Opera Software
VUG5: Varnish at Opera SoftwareVUG5: Varnish at Opera Software
VUG5: Varnish at Opera Software
 
Ajax to the Moon
Ajax to the MoonAjax to the Moon
Ajax to the Moon
 
Caching and tuning fun for high scalability
Caching and tuning fun for high scalabilityCaching and tuning fun for high scalability
Caching and tuning fun for high scalability
 
Altitude SF 2017: Optimizing your hit rate
Altitude SF 2017: Optimizing your hit rateAltitude SF 2017: Optimizing your hit rate
Altitude SF 2017: Optimizing your hit rate
 
Modern javascript localization with c-3po and the good old gettext
Modern javascript localization with c-3po and the good old gettextModern javascript localization with c-3po and the good old gettext
Modern javascript localization with c-3po and the good old gettext
 
MNPHP Scalable Architecture 101 - Feb 3 2011
MNPHP Scalable Architecture 101 - Feb 3 2011MNPHP Scalable Architecture 101 - Feb 3 2011
MNPHP Scalable Architecture 101 - Feb 3 2011
 
20190516 web security-basic
20190516 web security-basic20190516 web security-basic
20190516 web security-basic
 
Caching with Varnish
Caching with VarnishCaching with Varnish
Caching with Varnish
 
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
 
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
 
OWASP Top 10 - Checkmarx Presentation at Polytechnic Institute of Cávado and Ave
OWASP Top 10 - Checkmarx Presentation at Polytechnic Institute of Cávado and AveOWASP Top 10 - Checkmarx Presentation at Polytechnic Institute of Cávado and Ave
OWASP Top 10 - Checkmarx Presentation at Polytechnic Institute of Cávado and Ave
 
Choosing A Proxy Server - Apachecon 2014
Choosing A Proxy Server - Apachecon 2014Choosing A Proxy Server - Apachecon 2014
Choosing A Proxy Server - Apachecon 2014
 

Mais de Thijs Feryn

10 things that helped me advance my career - PHP UK Conference 2024
10 things that helped me advance my career - PHP UK Conference 202410 things that helped me advance my career - PHP UK Conference 2024
10 things that helped me advance my career - PHP UK Conference 2024Thijs Feryn
 
Distributed load testing with K6 - NDC London 2024
Distributed load testing with K6 - NDC London 2024Distributed load testing with K6 - NDC London 2024
Distributed load testing with K6 - NDC London 2024Thijs Feryn
 
HTTP headers that make your website go faster - devs.gent November 2023
HTTP headers that make your website go faster - devs.gent November 2023HTTP headers that make your website go faster - devs.gent November 2023
HTTP headers that make your website go faster - devs.gent November 2023Thijs Feryn
 
Living on the edge - EBU Horizons 2023
Living on the edge - EBU Horizons 2023Living on the edge - EBU Horizons 2023
Living on the edge - EBU Horizons 2023Thijs Feryn
 
Distributed Load Testing with k6 - DevOps Barcelona
Distributed Load Testing with k6 - DevOps BarcelonaDistributed Load Testing with k6 - DevOps Barcelona
Distributed Load Testing with k6 - DevOps BarcelonaThijs Feryn
 
Core web vitals meten om je site sneller te maken - Combell Partner Day 2023
Core web vitals meten om je site sneller te maken - Combell Partner Day 2023Core web vitals meten om je site sneller te maken - Combell Partner Day 2023
Core web vitals meten om je site sneller te maken - Combell Partner Day 2023Thijs Feryn
 
HTTP headers that make your website go faster
HTTP headers that make your website go fasterHTTP headers that make your website go faster
HTTP headers that make your website go fasterThijs Feryn
 
HTTP headers that will make your website go faster
HTTP headers that will make your website go fasterHTTP headers that will make your website go faster
HTTP headers that will make your website go fasterThijs Feryn
 
Distributed load testing with k6
Distributed load testing with k6Distributed load testing with k6
Distributed load testing with k6Thijs Feryn
 
HTTP logging met Varnishlog - PHPWVL 2022
HTTP logging met Varnishlog - PHPWVL 2022HTTP logging met Varnishlog - PHPWVL 2022
HTTP logging met Varnishlog - PHPWVL 2022Thijs Feryn
 
Taking Laravel to the edge with HTTP caching and Varnish
Taking Laravel to the edge with HTTP caching and VarnishTaking Laravel to the edge with HTTP caching and Varnish
Taking Laravel to the edge with HTTP caching and VarnishThijs Feryn
 
Build your own CDN with Varnish - Confoo 2022
Build your own CDN with Varnish - Confoo 2022Build your own CDN with Varnish - Confoo 2022
Build your own CDN with Varnish - Confoo 2022Thijs Feryn
 
Caching the uncacheable with Varnish - DevDays 2021
Caching the uncacheable with Varnish - DevDays 2021Caching the uncacheable with Varnish - DevDays 2021
Caching the uncacheable with Varnish - DevDays 2021Thijs Feryn
 
Developing cacheable backend applications - Appdevcon 2019
Developing cacheable backend applications - Appdevcon 2019Developing cacheable backend applications - Appdevcon 2019
Developing cacheable backend applications - Appdevcon 2019Thijs Feryn
 
How Cloud addresses the needs of todays internet - Korazon 2018
How Cloud addresses the needs of todays internet - Korazon 2018How Cloud addresses the needs of todays internet - Korazon 2018
How Cloud addresses the needs of todays internet - Korazon 2018Thijs Feryn
 

Mais de Thijs Feryn (15)

10 things that helped me advance my career - PHP UK Conference 2024
10 things that helped me advance my career - PHP UK Conference 202410 things that helped me advance my career - PHP UK Conference 2024
10 things that helped me advance my career - PHP UK Conference 2024
 
Distributed load testing with K6 - NDC London 2024
Distributed load testing with K6 - NDC London 2024Distributed load testing with K6 - NDC London 2024
Distributed load testing with K6 - NDC London 2024
 
HTTP headers that make your website go faster - devs.gent November 2023
HTTP headers that make your website go faster - devs.gent November 2023HTTP headers that make your website go faster - devs.gent November 2023
HTTP headers that make your website go faster - devs.gent November 2023
 
Living on the edge - EBU Horizons 2023
Living on the edge - EBU Horizons 2023Living on the edge - EBU Horizons 2023
Living on the edge - EBU Horizons 2023
 
Distributed Load Testing with k6 - DevOps Barcelona
Distributed Load Testing with k6 - DevOps BarcelonaDistributed Load Testing with k6 - DevOps Barcelona
Distributed Load Testing with k6 - DevOps Barcelona
 
Core web vitals meten om je site sneller te maken - Combell Partner Day 2023
Core web vitals meten om je site sneller te maken - Combell Partner Day 2023Core web vitals meten om je site sneller te maken - Combell Partner Day 2023
Core web vitals meten om je site sneller te maken - Combell Partner Day 2023
 
HTTP headers that make your website go faster
HTTP headers that make your website go fasterHTTP headers that make your website go faster
HTTP headers that make your website go faster
 
HTTP headers that will make your website go faster
HTTP headers that will make your website go fasterHTTP headers that will make your website go faster
HTTP headers that will make your website go faster
 
Distributed load testing with k6
Distributed load testing with k6Distributed load testing with k6
Distributed load testing with k6
 
HTTP logging met Varnishlog - PHPWVL 2022
HTTP logging met Varnishlog - PHPWVL 2022HTTP logging met Varnishlog - PHPWVL 2022
HTTP logging met Varnishlog - PHPWVL 2022
 
Taking Laravel to the edge with HTTP caching and Varnish
Taking Laravel to the edge with HTTP caching and VarnishTaking Laravel to the edge with HTTP caching and Varnish
Taking Laravel to the edge with HTTP caching and Varnish
 
Build your own CDN with Varnish - Confoo 2022
Build your own CDN with Varnish - Confoo 2022Build your own CDN with Varnish - Confoo 2022
Build your own CDN with Varnish - Confoo 2022
 
Caching the uncacheable with Varnish - DevDays 2021
Caching the uncacheable with Varnish - DevDays 2021Caching the uncacheable with Varnish - DevDays 2021
Caching the uncacheable with Varnish - DevDays 2021
 
Developing cacheable backend applications - Appdevcon 2019
Developing cacheable backend applications - Appdevcon 2019Developing cacheable backend applications - Appdevcon 2019
Developing cacheable backend applications - Appdevcon 2019
 
How Cloud addresses the needs of todays internet - Korazon 2018
How Cloud addresses the needs of todays internet - Korazon 2018How Cloud addresses the needs of todays internet - Korazon 2018
How Cloud addresses the needs of todays internet - Korazon 2018
 

Último

Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusZilliz
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...apidays
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistandanishmna97
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityWSO2
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesrafiqahmad00786416
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxRustici Software
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyKhushali Kathiriya
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamUiPathCommunity
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfOrbitshub
 

Último (20)

Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 

Developing cacheable PHP applications - Confoo 2018

  • 7.
  • 10.
  • 13.
  • 16. With ReCaPro * User ReCaPro Server * Reverse Caching Proxy
  • 18.
  • 19. HTTP caching mechanisms Expires: Sat, 09 Sep 2017 14:30:00 GMT Cache-control: public, max-age=3600, s-maxage=86400 Cache-control: private, no-cache, no-store
  • 21. ✓Stateless ✓Well-defined TTL ✓Cache / no-cache per resource ✓Cache variations ✓Conditional requests ✓HTTP fragments for non- cacheable content In an ideal world
  • 24.
  • 25.
  • 30.
  • 32.
  • 34. composer require symfony/flex composer require annotations twig translation
  • 35. <?php namespace AppController; use SymfonyComponentHttpFoundationRequest; use SensioBundleFrameworkExtraBundleConfigurationRoute; use SymfonyBundleFrameworkBundleControllerController; class DefaultController extends Controller { /** * @Route("/", name="home") */ public function index() { return $this->render('index.twig'); } } src/Controller/DefaultController.php
  • 36. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/html" xmlns:hx="http://purl.org/NET/hinclude"> <head> <title>{% block title %}{% endblock %}</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="container-fluid"> {{ include('header.twig') }} <div class="row"> <div class="col-sm-3 col-lg-2"> {{ include('nav.twig') }} </div> <div class="col-sm-9 col-lg-10"> {% block content %}{% endblock %} </div> </div> {{ include('footer.twig') }} </div> </body> </html> templates/base.twig
  • 37. {% extends "base.twig" %} {% block title %}Home{% endblock %} {% block content %} <div class="page-header"> <h1>{{ 'example' | trans }}</h1> </div> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris consequat orci eget libero sollicitudin, non ultrices turpis mollis. Aliquam sit amet tempus elit. Ut viverra risus enim, ut venenatis justo accumsan nec. Praesent a dolor tellus. Maecenas non mauris leo. Pellentesque lobortis turpis at dapibus laoreet. Mauris rhoncus nulla et urna mollis, et lobortis magna ornare. Etiam et sapien consequat, egestas felis sit amet, dignissim enim.</p> <p>Quisque quis mollis justo, imperdiet fermentum velit. Aliquam nulla justo, consectetur et diam non, luctus commodo metus. Vestibulum fermentum efficitur nulla non luctus. Nunc lorem nunc, mollis id efficitur et, aliquet sit amet ante. Sed ipsum turpis, vehicula eu semper eu, malesuada eget leo. Vestibulum venenatis dui id pulvinar suscipit. Etiam nec massa pharetra justo pharetra dignissim quis non magna. Integer id convallis lectus. Nam non ullamcorper metus. Ut vestibulum ex ut massa posuere tincidunt. Vestibulum hendrerit neque id lorem rhoncus aliquam. Duis a facilisis metus, a faucibus nulla.</p> {% endblock %} templates/index.twig
  • 38. <nav class="navbar navbar-default navbar-fixed-side"> <ul class="nav"> <li><a href="{{ url('home') }}">Home</a></li> <li><a href="{{ url('login') }}">{{ 'log_in' | trans }}</a></li> <li><a href="{{ url('private') }}">Private</a></li> </ul> </nav> templates/nav.twig
  • 40. home: Home welcome : Welcome to the site rendered : Rendered at %date% example : An example page log_in : Log in login : Login log_out : Log out username : Username password : Password private : Private privatetext : Looks like some very private data translations/messages.en.yml
  • 41. home: Start welcome : Welkom op de site rendered : Samengesteld op %date% example : Een voorbeeldpagina log_in : Inloggen login : Login log_out : Uitloggen username : Gebruikersnaam password : Wachtwoord private : Privé privatetext : Deze tekst ziet er vrij privé uit translations/messages.nl.yml
  • 42. <?php namespace AppEventListener; use SymfonyComponentHttpKernelEventGetResponseEvent; class LocaleListener { public function onKernelRequest(GetResponseEvent $event) { $request = $event->getRequest(); $preferredLanguage = $request->getPreferredLanguage(); if(null !== $preferredLanguage) { $request->setLocale($preferredLanguage); } } } src/EventListener/LocaleListener.php
  • 43. services: _defaults: autowire: true autoconfigure: true public: false App: resource: '../src/*' exclude: '../src/{Entity,Migrations,Tests,Kernel.php}' AppController: resource: '../src/Controller' tags: ['controller.service_arguments'] AppEventListenerLocaleListener: tags: - { name: kernel.event_listener, event: kernel.request, priority: 100} config/services.yml
  • 45.
  • 46.
  • 50. <?php namespace AppController; use SymfonyComponentHttpFoundationRequest; use SensioBundleFrameworkExtraBundleConfigurationRoute; use SymfonyBundleFrameworkBundleControllerController; class DefaultController extends Controller { /** * @Route("/", name="home") */ public function index() { return $this ->render('index.twig') ->setSharedMaxAge(500) ->setPublic(); } }
  • 52. Only fetch payload that has changed
  • 55. Conditional requests HTTP/1.1 200 OK Host: localhost Etag: 7c9d70604c6061da9bb9377d3f00eb27 Content-type: text/html; charset=UTF-8 Hello world output GET / HTTP/1.1 Host: localhost User-Agent: curl/7.48.0
  • 56. Conditional requests HTTP/1.0 304 Not Modified Host: localhost Etag: 7c9d70604c6061da9bb9377d3f00eb27 GET / HTTP/1.1 Host: localhost User-Agent: curl/7.48.0 If-None-Match: 7c9d70604c6061da9bb9377d3f00eb27
  • 57. Conditional requests HTTP/1.1 200 OK Host: localhost Last-Modified: Fri, 22 Jul 2016 10:11:16 GMT Content-type: text/html; charset=UTF-8 Hello world output GET / HTTP/1.1 Host: localhost User-Agent: curl/7.48.0
  • 58. Conditional requests HTTP/1.0 304 Not Modified Host: localhost Last-Modified: Fri, 22 Jul 2016 10:11:16 GMT GET / HTTP/1.1 Host: localhost User-Agent: curl/7.48.0 If-Last-Modified: Fri, 22 Jul 2016 10:11:16 GMT
  • 60. <?php namespace AppEventListener; use SymfonyBridgeMonologLogger; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpKernelEventGetResponseEvent; use SymfonyComponentHttpKernelEventFilterResponseEvent; use SymfonyBundlesRedisBundleRedisClient as RedisClient; class ConditionalRequestListener { protected $redis; protected $logger; public function __construct(RedisClient $redis, Logger $logger) { $this->redis = $redis; $this->logger = $logger; } protected function isModified(Request $request, $etag) { if ($etags = $request->getETags()) { return in_array($etag, $etags) || in_array('*', $etags); } return true; } ... src/EventListener/ConditionalRequestListener.php
  • 61. { $this->redis = $redis; $this->logger = $logger; } protected function isModified(Request $request, $etag) { if ($etags = $request->getETags()) { return in_array($etag, $etags) || in_array('*', $etags); } return true; } public function onKernelRequest(GetResponseEvent $event) { $request = $event->getRequest(); $etag = $this->redis->get('etag:'.md5($request->getUri())); if(!$this->isModified($request,$etag)) { $event->setResponse(Response::create('Not Modified',Response::HTTP_NOT_MODIFIED)); } } public function onKernelResponse(FilterResponseEvent $event) { $response = $event->getResponse(); $request = $event->getRequest(); $etag = md5($response->getContent()); $response->setEtag($etag); if($this->isModified($request,$etag)) { $this->redis->set('etag:'.md5($request->getUri()),$etag); } } } src/EventListener/ConditionalRequestListener.php
  • 64. /** * @Route("/private", name="private") */ public function private() { $response = $this ->render('private.twig') ->setPrivate(); $response->headers->addCacheControlDirective('no-store'); return $response; } Dot not cache private page
  • 66.
  • 70. <esi:include src="/header" /> Edge Side Includes ✓Placeholder ✓Parsed by Varnish ✓Output is a composition of blocks ✓State per block ✓TTL per block
  • 73. ✓ Server-side ✓ Standardized ✓ Processed on the “edge”, no in the browser ✓ Generally faster Edge-Side Includes - Sequential - One fails, all fail - Limited implementation in Varnish
  • 74. ✓ Client-side ✓ Common knowledge ✓ Parallel processing ✓ Graceful degradation AJAX - Processed by the browser - Extra roundtrips - Somewhat slower
  • 76. <div class="container-fluid">
 {{ include('header.twig') }}
 <div class="row">
 <div class="col-sm-3 col-lg-2">
 {{ include('nav.twig') }}
 </div>
 <div class="col-sm-9 col-lg-10">
 {% block content %}{% endblock %}
 </div>
 </div>
 {{ include('footer.twig') }}
 </div> <div class="container-fluid">
 {{ render_esi(url('header')) }}
 <div class="row">
 <div class="col-sm-3 col-lg-2">
 {{ render_esi(url('nav')) }}
 </div>
 <div class="col-sm-9 col-lg-10">
 {% block content %}{% endblock %}
 </div>
 </div>
 {{ render_esi(url('footer')) }}
 </div>
  • 77. <div class="container-fluid"> <esi:include src="/header" /> <div class="row"> <div class="col-sm-3 col-lg-2"> <esi:include src="/nav" /> </div> <div class="col-sm-9 col-lg-10"> <div class="page-header"> <h1>An example page <small>Rendered at 2017-05-17 16:57:14</small></h1> </div> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris consequat orci eget libero sollicitudin,…</p> </div> </div> <esi:include src="/footer" /> </div>
  • 78. /** * @Route("/", name="home") */ public function index() { return $this ->render('index.twig') ->setPublic() ->setSharedMaxAge(500); } /** * @Route("/header", name="header") */ public function header() { $response = $this ->render('header.twig') ->setPrivate(); $response->headers->addCacheControlDirective('no-store'); return $response; } /** * @Route("/footer", name="footer") */ public function footer() { $response = $this->render('footer.twig'); $response ->setSharedMaxAge(500) ->setPublic(); return $response; } /** * @Route("/nav", name="nav") */ public function nav() { $response = $this->render('nav.twig'); $response ->setVary('X-Login',false) ->setSharedMaxAge(500) ->setPublic(); return $response; } Controller action per fragment
  • 81. <?php namespace AppEventListener; use SymfonyComponentHttpKernelEventFilterResponseEvent; class VaryListener { public function onKernelResponse(FilterResponseEvent $event) { $response = $event->getResponse(); $response->setVary('Accept-Language',false); } } src/EventListener/VaryListener.php
  • 82.
  • 83. ✓Navigation page ✓Private page Weak spots Not cached because of stateful content
  • 85. Replace PHP session with JSON Web Tokens
  • 87. eyJzdWIiOiJhZG1pbiIsIm V4cCI6MTQ5NTUyODc1Niwi bG9naW4iOnRydWV9 { "alg": "HS256",  "typ": "JWT" } { "sub": "admin", "exp": 1495528756, "login": true } HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret ) eyJhbGciOiJIUzI1NiIsI nR5cCI6IkpXVCJ9 u4Idy- SYnrFdnH1h9_sNc4OasOR BJcrh2fPo1EOTre8
  • 89. sub jwt { std.log("Ready to perform some JWT magic"); if(cookie.isset("jwt_cookie")) { #Extract header data from JWT var.set("token", cookie.get("jwt_cookie")); var.set("header", regsub(var.get("token"),"([^.]+).[^.]+.[^.]+","1")); var.set("type", regsub(digest.base64url_decode(var.get("header")),{"^.*?"typ"s*:s*"(w+)".*?$"},"1")); var.set("algorithm", regsub(digest.base64url_decode(var.get("header")),{"^.*?"alg"s*:s*"(w+)".*?$"},"1")); #Don't allow invalid JWT header if(var.get("type") == "JWT" && var.get("algorithm") == "HS256") { #Extract signature & payload data from JWT var.set("rawPayload",regsub(var.get("token"),"[^.]+.([^.]+).[^.]+$","1")); var.set("signature",regsub(var.get("token"),"^[^.]+.[^.]+.([^.]+)$","1")); var.set("currentSignature",digest.base64url_nopad_hex(digest.hmac_sha256(var.get("key"),var.get("header") + "." + var.get("rawPayload")))); var.set("payload", digest.base64url_decode(var.get("rawPayload"))); var.set("exp",regsub(var.get("payload"),{"^.*?"exp"s*:s*([0-9]+).*?$"},"1")); var.set("jti",regsub(var.get("payload"),{"^.*?"jti"s*:s*"([a-z0-9A-Z_-]+)".*?$"},"1")); var.set("userId",regsub(var.get("payload"),{"^.*?"uid"s*:s*"([0-9]+)".*?$"},"1")); var.set("roles",regsub(var.get("payload"),{"^.*?"roles"s*:s*"([a-z0-9A-Z_-, ]+)".*?$"},"1")); #Only allow valid userId if(var.get("userId") ~ "^d+$") { #Don't allow expired JWT if(std.time(var.get("exp"),now) >= now) { #SessionId should match JTI value from JWT if(cookie.get(var.get("sessionCookie")) == var.get("jti")) { #Don't allow invalid JWT signature if(var.get("signature") == var.get("currentSignature")) { #The sweet spot set req.http.X-login="true"; } else { std.log("JWT: signature doesn't match. Received: " + var.get("signature") + ", expected: " + var.get("currentSignature")); } } else { std.log("JWT: session cookie doesn't match JTI." + var.get("sessionCookie") + ": " + cookie.get(var.get("sessionCookie")) + ", JTI:" + var.get("jti")); } } else { std.log("JWT: token has expired"); } } else { std.log("UserId '"+ var.get("userId") +"', is not numeric"); } } else { std.log("JWT: type is not JWT or algorithm is not HS256"); } std.log("JWT processing finished. UserId: " + var.get("userId") + ". X-Login: " + req.http.X-login); } #Look for full private content if(req.url ~ "/node/2" && req.url !~ "^/user/login") { if(req.http.X-login != "true") { return(synth(302,"/user/login?destination=" + req.url)); } } } Insert incomprehensible Varnish VCL code here …
  • 90. X-Login: true End result: X-Login: false Custom request header set by Varnish
  • 92. Vary: Accept-Language, X-Login Content for logged-in & anonymous differs
  • 93. <script language="JavaScript">
 function getCookie(name) {
 var value = "; " + document.cookie;
 var parts = value.split("; " + name + "=");
 if (parts.length == 2) return parts.pop().split(";").shift();
 }
 function parseJwt (token) {
 var base64Url = token.split('.')[1];
 var base64 = base64Url.replace('-', '+').replace('_', '/');
 return JSON.parse(window.atob(base64));
 };
 $(document).ready(function(){
 if ($.cookie('token') != null ){
 var token = parseJwt($.cookie("token"));
 $("#usernameLabel").html(', ' + token.sub);
 }
 });
 </script> Parse JWT in Javascript
  • 96. <?php namespace AppService; use FirebaseJWTJWT; use SymfonyComponentHttpFoundationCookie; class JwtAuthentication { protected $key; protected $username; protected $password; public function __construct($key,$username,$password) { $this->key = $key; $this->username = $username; $this->password = $password; } public function jwt($username) { return JWT::encode([ 'sub'=>$username, 'exp'=>time() + (4 * 24 * 60 * 60), 'login'=>true, ],$this->key); } public function createCookie($username) { return new Cookie("token",$this->jwt($username), time() + (3600 * 48), '/', null, false, false); } public function validate($token) { src/Service/JwtAuthentication.php
  • 97. $this->password = $password; } public function jwt($username) { return JWT::encode([ 'sub'=>$username, 'exp'=>time() + (4 * 24 * 60 * 60), //'exp'=>time() + 60, 'login'=>true, ],$this->key); } public function createCookie($username) { return new Cookie("token",$this->jwt($username), time() + (3600 * 48), '/', null, false, false); } public function validate($token) { try { $data = JWT::decode($token,$this->key,['HS256']); $data = (array)$data; if($data['sub'] !== $this->username) { return false; } return true; } catch(UnexpectedValueException $e) { return false; } } } src/Service/JwtAuthentication.php
  • 99. ###> JWT authentication ### JWT_KEY=SlowWebSitesSuck JWT_USERNAME=admin JWT_PASSWORD=$2y$10$431rvq1qS9ewNFP0Gti/o.kBbuMK4zs8IDTLlxm5uzV7cbv8wKt0K ###< JWT authentication ### .env
  • 100. /** * @Route("/login", name="login", methods="GET") */ public function login(Request $request, JwtAuthentication $jwt) { if($jwt->validate($request->cookies->get('token'))) { return new RedirectResponse($this->generateUrl('home')); } $response = $this->render('login.twig',['loginLogoutUrl'=>$this- >generateUrl('login'),'loginLogoutLabel'=>'log_in']); $response ->setSharedMaxAge(500) ->setVary('X-Login',false) ->setPublic(); return $response; } /** * @Route("/login", name="loginpost", methods="POST") */ public function loginpost(Request $request, JwtAuthentication $jwt) { $username = $request->get('username'); $password = $request->get('password'); if(!$username || !$password || getenv('JWT_USERNAME') != $username || ! password_verify($password,getenv('JWT_PASSWORD'))) { return new RedirectResponse($this->generateUrl('login')); } $response = new RedirectResponse($this->generateUrl('home')); $response->headers->setCookie($jwt->createCookie($username)); return $response; } src/Controller/DefaultController.php
  • 101. /** * @Route("/logout", name="logout") */ public function logout() { $response = new RedirectResponse($this->generateUrl('login')); $response->headers->clearCookie('token'); return $response; } src/Controller/DefaultController.php
  • 102. /** * @Route("/nav", name="nav") */ public function nav(Request $request, JwtAuthentication $jwt) { if($jwt->validate($request->cookies->get('token'))) { $loginLogoutUrl = $loginLogoutUrl = $this->generateUrl('logout'); $loginLogoutLabel = 'log_out'; } else { $loginLogoutUrl = $this->generateUrl('login'); $loginLogoutLabel = 'log_in'; } $response = $this->render('nav.twig', ['loginLogoutUrl'=>$loginLogoutUrl,'loginLogoutLabel'=>$loginLogoutLabel]); $response ->setVary('X-Login',false) ->setSharedMaxAge(500) ->setPublic(); return $response; } src/Controller/DefaultController.php
  • 103. {% extends "base.twig" %} {% block title %}Login{% endblock %} {% block content %} <div class="page-header"> <h1>{{ 'log_in' | trans }}</h1> </div> <form class="form-horizontal" method="post" action="{{ url('loginpost') }}"> <div class="form-group"> <label for="usernameInput" class="col-sm-2 control-label">{{ 'username' | trans }}</label> <div class="col-sm-6"> <input type="text" name="username" class="form-control" id="usernameInput" placeholder="{{ 'username' | trans }}"> </div> </div> <div class="form-group"> <label for="passwordInput" class="col-sm-2 control-label">{{ 'password' | trans }}</label> <div class="col-sm-6"> <input type="password" name="password" class="form-control" id="passwordInput" placeholder="{{ 'password' | trans }}"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default">{{ 'log_in' | trans }}</button> </div> </div> </form> {% endblock %} templates/login.twig
  • 104.
  • 105.
  • 107. With the proper VCL, Varnish can process the JWT and make cache variations for authenticated & anonymous content