forget about index.php and build you applications around http - phpers cracow
TRANSCRIPT
FORGET ABOUT INDEX.PHPBUILD YOUR APPLICATIONS AROUND HTTP!
Kacper Gunia @cakper Software Engineer @SensioLabsUK
Symfony Certified Developer
PHPers Silesia @PHPersPL
Good old daysflickr.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…
How HTTP works?flickr.com/see-‐through-‐the-‐eye-‐of-‐g/4278744230
Request
Kamehameha!
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 simpleflickr.com/wildhaber/5936335464
Requestflickr.com/haniamir/3318727924
GET /index.php?name=Kacper HTTP/1.1 Host: localhost
GET /index.php?name=Kacper HTTP/1.1 Host: localhost
I want to see…
GET /index.php?name=Kacper HTTP/1.1 Host: localhost
…this resource!
GET /index.php?name=Kacper HTTP/1.1 Host: localhost
And I know it should be on localhost
GET /index.php?name=Kacper HTTP/1.1 Host: localhost
Psst, I’m using 1.1 version of HTTP protocol
Responseflickr.com/aftab/3364835006
HTTP/1.1 200 OK Content-‐type: text/html
Hello Kacper!
HTTP/1.1 200 OK Content-‐type: text/html
Hello Kacper!
OK man, I’ve found it!
HTTP/1.1 200 OK Content-‐type: text/html
Hello Kacper!
And it’s an HTML
HTTP/1.1 200 OK 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]Res
pons
e
Req
uest
What if we create objects from Request & Response?
Object-oriented HTTPflickr.com/mohammadafshar/9571051345
GET /index.php?name=Kacper HTTP/1.1 Host: localhost
$request-‐>getMethod(); GET $request-‐>getPathInfo(); /
HTTP/1.1 200 OK Content-‐type: text/html
Hello Kacper!
$response-‐>getStatusCode(); 200 $response-‐>getContent(); Hello Kacper!
HttpFoundationflickr.com/rubempjr/8050505443
“The HttpFoundation component defines an object-orientedlayer for the HTTP
specification”Symfony.com
Requestflickr.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 secure header or the standard one
$request-‐>isXmlHttpRequest();
Verify AJAX request
$request = Request::create( '/', 'GET', ['name' => 'Kacper'] );
Simulate a Request
Responseflickr.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
Kamehameha!
Response
Front Controllerflickr.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 Kernelflickr.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 Dispatcherflickr.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 Eventflickr.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 Componentflickr.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 Controllerflickr.com/rightbrainphotography/480979176
interface ControllerResolverInterface { public function getController( Request $request );
public function getArguments( Request $request, $controller ); }
Controller is a PHP callable
The kernel.controller Eventflickr.com/drakegoodman/12451824524
Change controller here (if you need)
and initialise data
e.g. parameters conversion
happens now
Resolve Argumentsflickr.com/joiseyshowaa/2720195951
interface ControllerResolverInterface { public function getController( Request $request );
public function getArguments( Request $request, $controller ); }
Arguments come from $request->attributes
ParameterBag
Controller Callflickr.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 Eventflickr.com/drakegoodman/14482752231
Manipulate Response
e.g. WebDebugToolbar
Send Responseflickr.com/stuckincustoms/5727003126
The kernel.terminate Eventflickr.com/drakegoodman/12203395206
Do the heavy stuff now
e.g. Send Emails
HTTP Cacheflickr.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; }
ESIflickr.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 Proxyflickr.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?
YES!
Drupal 8phpBB
SilexeZ Publish
Laravel
Kacper Gunia Software Engineer
Symfony Certified Developer
PHPers Silesia
Thanks!