FORGET ABOUT INDEX.PHP
BUILD YOUR APPLICATIONS AROUND HTTP!
Kacper Gunia @cakper
Software Engineer @SensioLabsUK
Symfony Certified Developer
PHPers Silesia @PHPersPL
Good old days
flickr.com/linkahwai/5162310920
Hello world in PHP“ ” + tutorial
Gojko’s two facts about
programming web:
1)Ctrl-C
2)Ctrl-V
<?php	
  
!
$name	
  =	
  $_GET['name'];	
  
echo	
  "Hello	
  $name!";
It works! :D
but…
and so on… ;)
How HTTP works?
flickr.com/see-­‐through-­‐the-­‐eye-­‐of-­‐g/4278744230
Request
Kabooooom!
Response
This is what HTTP is about!
Request
Response
This is what Your App is about!
Request
Response
“(…) the goal of your
application is always to
interpret a request and
create the appropriate
response based on your
application logic.”
Symfony.com
HTTP is simple
flickr.com/wildhaber/5936335464
Request
flickr.com/haniamir/3318727924
GET	
  /index.php?name=Kacper	
  HTTP/1.1	
  
Host:	
  localhost:8000
GET	
  /index.php?name=Kacper	
  HTTP/1.1	
  
Host:	
  localhost:8000
I want to see…
GET	
  /index.php?name=Kacper	
  HTTP/1.1	
  
Host:	
  localhost:8000
…this resource!
GET	
  /index.php?name=Kacper	
  HTTP/1.1	
  
Host:	
  localhost:8000
And I know it should be on localhost
GET	
  /index.php?name=Kacper	
  HTTP/1.1	
  
Host:	
  localhost:8000
Psst, I’m using 1.1 version of HTTP protocol
Response
flickr.com/aftab/3364835006
HTTP/1.1	
  200	
  OK	
  
Host:	
  localhost:8000	
  
Content-­‐type:	
  text/html	
  
!
Hello	
  Kacper!
HTTP/1.1	
  200	
  OK	
  
Host:	
  localhost:8000	
  
Content-­‐type:	
  text/html	
  
!
Hello	
  Kacper!
OK man, I’ve found it!
HTTP/1.1	
  200	
  OK	
  
Host:	
  localhost:8000	
  
Content-­‐type:	
  text/html	
  
!
Hello	
  Kacper!
And it’s an HTML
HTTP/1.1	
  200	
  OK	
  
Host:	
  localhost:8000	
  
Content-­‐type:	
  text/html	
  
!
Hello	
  Kacper!
Hello World!
[METH]	
  [REQUEST-­‐URI]	
  HTTP/[VER]	
  
[Field1]:	
  [Value1]	
  
[Field2]:	
  [Value2]	
  
!
[request	
  body,	
  if	
  any]
HTTP/[VER]	
  [CODE]	
  [TEXT]	
  
[Field1]:	
  [Value1]	
  
[Field2]:	
  [Value2]	
  
!
[response	
  body]
ResponseRequest
What if we create objects
from Request & Response?
Object-oriented HTTP
flickr.com/mohammadafshar/9571051345
GET	
  /index.php?name=Kacper	
  HTTP/1.1	
  
Host:	
  localhost:8000
$request-­‐>getMethod();	
  	
  	
  GET	
  
$request-­‐>getPathInfo();	
  /
HTTP/1.1	
  200	
  OK	
  
Host:	
  localhost:8000	
  
Content-­‐type:	
  text/html	
  
!
Hello	
  Kacper!
$response-­‐>getStatusCode();	
  200	
  
$response-­‐>getContent();	
  	
  	
  	
  Hello	
  Kacper!
HttpFoundation
flickr.com/rubempjr/8050505443
“The HttpFoundation
component defines
an object-oriented

layer for the HTTP
specification”
Symfony.com
Request
flickr.com/haniamir/3318727924
$request	
  =	
  Request::createFromGlobals();	
  
!
$request	
  =	
  new	
  Request(	
  
	
  	
  	
  	
  $_GET,	
  
	
  	
  	
  	
  $_POST,	
  
	
  	
  	
  	
  array(),	
  
	
  	
  	
  	
  $_COOKIE,	
  
	
  	
  	
  	
  $_FILES,	
  
	
  	
  	
  	
  $_SERVER	
  
);
!
	
  	
  	
  	
  $_GET	
  	
  	
  	
  	
  $request-­‐>query	
  	
  
	
  	
  	
  	
  $_POST	
  	
  	
  	
  $request-­‐>request	
  
	
  	
  	
  	
  $_COOKIE	
  	
  $request-­‐>cookies	
  
	
  	
  	
  	
  $_FILES	
  	
  	
  $request-­‐>files	
  
	
  	
  	
  	
  $_SERVER	
  	
  $request-­‐>server	
  
!
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  $request-­‐>headers	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  $request-­‐>attributes
ParameterBag instances
$name	
  =	
  isset($_GET['name'])	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  ?	
  $_GET['name']	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  :	
  "World";
$name	
  =	
  $request	
  
	
  	
  	
  	
  	
  	
  	
  	
  -­‐>query	
  
	
  	
  	
  	
  	
  	
  	
  	
  -­‐>get('name',	
  'World');
$request-­‐>isSecure();
Verify configured header or standard one
$request-­‐>isXmlHttpRequest();
Verify AJAX request
$request	
  =	
  Request::create(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  '/',	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  'GET',	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ['name'	
  =>	
  'Kacper']	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  );
Simulate a Request
Response
flickr.com/aftab/3364835006
$response	
  =	
  new	
  Response(	
  
	
  	
  	
  	
  ‘Hello	
  Kacper!’,	
  
	
  	
  	
  	
  Response::HTTP_OK,	
  
	
  	
  	
  	
  ['content-­‐type'	
  =>	
  'text/html']	
  
);	
  
!
$response-­‐>prepare($request);	
  
$response-­‐>send();
$response	
  =	
  new	
  RedirectResponse(

	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  'http://example.com/'	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  );
Redirect Response
$response	
  =	
  new	
  JsonResponse();	
  
$response-­‐>setData(['name'	
  =>	
  'Kacper']);
JSON Response
Let’s use them together!
$kernel	
  =	
  new	
  AppKernel('dev',	
  true);	
  
!
$request	
  =	
  Request::createFromGlobals();	
  
$response	
  =	
  $kernel-­‐>handle($request);	
  
$response-­‐>send();	
  
!
$kernel-­‐>terminate($request,	
  $response);
Symfony app_dev.php
$kernel	
  =	
  new	
  AppKernel('dev',	
  true);	
  
!
$request	
  =	
  Request::createFromGlobals();	
  
$response	
  =	
  $kernel-­‐>handle($request);	
  
$response-­‐>send();	
  
!
$kernel-­‐>terminate($request,	
  $response);
Symfony app_dev.php
Reminds something? ;)
Request
Kabooooom!
Response
Front Controller
flickr.com/cedwardbrice/8334047708
”The Front Controller
consolidates all request
handling by channeling
requests through a single
handler object (…)”
MartinFowler.com
$kernel	
  =	
  new	
  AppKernel('dev',	
  true);	
  
!
$request	
  =	
  Request::createFromGlobals();	
  
$response	
  =	
  $kernel-­‐>handle($request);	
  
$response-­‐>send();	
  
!
$kernel-­‐>terminate($request,	
  $response);
Let’s go deeper…
HTTP Kernel
flickr.com/stuckincustoms/6341844005
“The HttpKernel
component provides a
structured process for
converting a Request into
a Response by making use
of the EventDispatcher.”
Symfony.com
interface	
  HttpKernelInterface	
  
{	
  
	
  	
  	
  	
  const	
  MASTER_REQUEST	
  =	
  1;	
  
	
  	
  	
  	
  const	
  SUB_REQUEST	
  =	
  2;	
  
!
	
  	
  	
  	
  /**	
  
	
  	
  	
  	
  	
  *	
  @return	
  Response	
  A	
  Response	
  instance	
  
	
  	
  	
  	
  	
  */	
  
	
  	
  	
  	
  public	
  function	
  handle(	
  
	
  	
  	
  	
  	
  	
  	
  	
  Request	
  $request,	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  $type	
  =	
  self::MASTER_REQUEST,	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  $catch	
  =	
  true);	
  
}
How Symfony transforms
Request into Response?
Event Dispatcher
flickr.com/parksjd/11847079564
“The EventDispatcher
component provides tools
that allow your application
components to communicate
with each other by
dispatching events and
listening to them.”
Symfony.com
EventDispatcher is
an implementation
of Mediator pattern
$dispatcher	
  =	
  new	
  EventDispatcher();	
  
$dispatcher-­‐>addListener(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  'foo.action',	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  function	
  (Event	
  $event)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  do	
  whatever	
  you	
  need	
  
});	
  
!
$event	
  =	
  new	
  Event();	
  
$dispatcher-­‐>dispatch('foo.action',	
  $event);
The kernel.request Event
flickr.com/drakegoodman/13479419575
Manipulate your
Request here…
…you can even
return a Response!
public	
  function	
  onKernelRequest(GetResponseEvent	
  $event)	
  
{	
  
	
  	
  	
  	
  $request	
  =	
  $event-­‐>getRequest();	
  
	
  	
  	
  	
  if	
  ($request-­‐>query-­‐>get('name')	
  ===	
  'Kacper')	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $event-­‐>setResponse(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  new	
  Response("We	
  don't	
  like	
  you!")	
  
	
  	
  	
  	
  	
  	
  	
  	
  );	
  
	
  	
  	
  	
  }	
  
}
…or e.g. detect
device, location…
…and routing is
resolved here
The Routing Component
flickr.com/checksam/12814058644
“The Routing
component maps an
HTTP request to a set
of configuration
variables.”
Symfony.com
$route	
  =	
  new	
  Route('/',	
  array('controller'	
  =>	
  'HelloController'));	
  
$routes	
  =	
  new	
  RouteCollection();	
  
$routes-­‐>add('hello_route',	
  $route);	
  
!
$context	
  =	
  new	
  RequestContext();	
  
$context-­‐>fromRequest($request);	
  
!
$matcher	
  =	
  new	
  UrlMatcher($routes,	
  $context);	
  
!
$parameters	
  =	
  $matcher-­‐>match('/');	
  
//	
  ['controller'	
  =>	
  'HelloController',	
  '_route'	
  =>	
  'hello_route']
$route	
  =	
  new	
  Route('/',	
  array('controller'	
  =>	
  'HelloController'));	
  
$routes	
  =	
  new	
  RouteCollection();	
  
$routes-­‐>add('hello_route',	
  $route);	
  
!
$context	
  =	
  new	
  RequestContext();	
  
$context-­‐>fromRequest($request);	
  
!
$matcher	
  =	
  new	
  UrlMatcher($routes,	
  $context);	
  
!
$parameters	
  =	
  $matcher-­‐>match('/');	
  
//	
  ['controller'	
  =>	
  'HelloController',	
  '_route'	
  =>	
  'hello_route']
$route	
  =	
  new	
  Route('/',	
  array('controller'	
  =>	
  'HelloController'));	
  
$routes	
  =	
  new	
  RouteCollection();	
  
$routes-­‐>add('hello_route',	
  $route);	
  
!
$context	
  =	
  new	
  RequestContext();	
  
$context-­‐>fromRequest($request);	
  
!
$matcher	
  =	
  new	
  UrlMatcher($routes,	
  $context);	
  
!
$parameters	
  =	
  $matcher-­‐>match('/');	
  
//	
  ['controller'	
  =>	
  'HelloController',	
  '_route'	
  =>	
  'hello_route']
$route	
  =	
  new	
  Route('/',	
  array('controller'	
  =>	
  'HelloController'));	
  
$routes	
  =	
  new	
  RouteCollection();	
  
$routes-­‐>add('hello_route',	
  $route);	
  
!
$context	
  =	
  new	
  RequestContext();	
  
$context-­‐>fromRequest($request);	
  
!
$matcher	
  =	
  new	
  UrlMatcher($routes,	
  $context);	
  
!
$parameters	
  =	
  $matcher-­‐>match('/');	
  
//	
  ['controller'	
  =>	
  'HelloController',	
  '_route'	
  =>	
  'hello_route']
$route	
  =	
  new	
  Route('/',	
  array('controller'	
  =>	
  'HelloController'));	
  
$routes	
  =	
  new	
  RouteCollection();	
  
$routes-­‐>add('hello_route',	
  $route);	
  
!
$context	
  =	
  new	
  RequestContext();	
  
$context-­‐>fromRequest($request);	
  
!
$matcher	
  =	
  new	
  UrlMatcher($routes,	
  $context);	
  
!
$parameters	
  =	
  $matcher-­‐>match('/');	
  
//	
  ['controller'	
  =>	
  'HelloController',	
  '_route'	
  =>	
  'hello_route']
Resolve Controller
flickr.com/rightbrainphotography/480979176
interface	
  ControllerResolverInterface	
  
{	
  
	
  	
  	
  	
  public	
  function	
  getController(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Request	
  $request	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  );	
  
!
	
  	
  	
  	
  public	
  function	
  getArguments(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Request	
  $request,	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  $controller	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  );	
  
}
Controller is
a PHP callable
The kernel.controller Event
flickr.com/drakegoodman/12451824524
Change controller
here (if you need)
and initialise data
e.g. parameters
conversion
happens now
Resolve Arguments
flickr.com/joiseyshowaa/2720195951
interface	
  ControllerResolverInterface	
  
{	
  
	
  	
  	
  	
  public	
  function	
  getController(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Request	
  $request	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  );	
  
!
	
  	
  	
  	
  public	
  function	
  getArguments(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Request	
  $request,	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  $controller	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  );	
  
}
Arguments
come from
$request->attributes
ParameterBag
Controller Call
flickr.com/taspicsvns/11768808836
Time for your
application logic
Return
Response object
Optional:
The kernel.view event
flickr.com/drakegoodman/11006558364
Transform
non-Response
into Response
e.g.
@Template
annotation
e.g. transform
arrays into
JSON Responses
The kernel.response Event
flickr.com/drakegoodman/14482752231
Manipulate
Response
e.g.
WebDebugToolbar
Send Response
flickr.com/stuckincustoms/5727003126
The kernel.terminate Event
flickr.com/drakegoodman/12203395206
Do the heavy
stuff now
e.g.
Send Emails
HTTP Cache
flickr.com/soldiersmediacenter/403524071
Cache-Control
Expires
Cache-Control
Expires
$response	
  =	
  new	
  Response();	
  
!
$response-­‐>setPublic();	
  
$response-­‐>setMaxAge(600);	
  
$response-­‐>setSharedMaxAge(600);
Validation
public	
  function	
  indexAction(Request	
  $request,	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  $name)	
  
{	
  
	
  	
  	
  	
  $response	
  =	
  new	
  Response("Hello	
  $name");	
  
	
  	
  	
  	
  $response-­‐>setETag(md5($response-­‐>getContent()));	
  
	
  	
  	
  	
  $response-­‐>setPublic();	
  
	
  	
  	
  	
  $response-­‐>isNotModified($request);	
  
!
	
  	
  	
  	
  return	
  $response;	
  
}
ESI
flickr.com/nasamarshall/6950477589
“The ESI specification
describes tags you can
embed in your pages
to communicate with
the gateway cache.”
Symfony.com
<!DOCTYPE	
  html>	
  
<html>	
  
	
  	
  	
  	
  <body>	
  
	
  	
  	
  	
  <!-­‐-­‐	
  ...	
  content	
  -­‐-­‐>	
  
!
	
  	
  	
  	
  <!-­‐-­‐	
  Embed	
  the	
  content	
  of	
  another	
  page	
  -­‐-­‐>	
  
	
  	
  	
  	
  <esi:include	
  src="http://..."/>	
  
!
	
  	
  	
  	
  <!-­‐-­‐	
  ...	
  content	
  -­‐-­‐>	
  
	
  	
  	
  	
  </body>	
  
</html>
But I don’t have Varnish!
Symfony2 Reverse Proxy
flickr.com/zacharyz/3950845049
$kernel	
  =	
  new	
  AppKernel('prod',	
  false);	
  
$kernel-­‐>loadClassCache();	
  
!
$kernel	
  =	
  new	
  AppCache($kernel);	
  
!
$request	
  =	
  Request::createFromGlobals();	
  
$response	
  =	
  $kernel-­‐>handle($request);	
  
$response-­‐>send();	
  
$kernel-­‐>terminate($request,	
  $response);
$kernel	
  =	
  new	
  AppKernel('prod',	
  false);	
  
$kernel-­‐>loadClassCache();	
  
!
$kernel	
  =	
  new	
  AppCache($kernel);	
  
!
$request	
  =	
  Request::createFromGlobals();	
  
$response	
  =	
  $kernel-­‐>handle($request);	
  
$response-­‐>send();	
  
$kernel-­‐>terminate($request,	
  $response);
OK, but are those things actually
used outside of Symfony?
flickr.com/tombricker/5709640847
YES!
Drupal 8
phpBB
Silex
eZ Publish
Laravel
Kacper Gunia
Software Engineer
Symfony Certified Developer
PHPers Silesia
Thanks!
joind.in/10880

Forget about index.php and build you applications around HTTP!