DeSymfony 2012: Symfony internals

72

description

Slides de la charla 'Symfony Internals' en DeSymfony 2012

Transcript of DeSymfony 2012: Symfony internals

Page 1: DeSymfony 2012: Symfony internals
Page 2: DeSymfony 2012: Symfony internals
Page 3: DeSymfony 2012: Symfony internals

¿Quién soy?¿Quién soy?

Raúl Fraile

@raulfraile

PHP/Symfony2 freelance developer

Page 4: DeSymfony 2012: Symfony internals

¿Qué hago?¿Qué hago?

Page 5: DeSymfony 2012: Symfony internals

SensioLabsSensioLabsConnectConnect

Page 6: DeSymfony 2012: Symfony internals

¿Por qué?¿Por qué?

Page 7: DeSymfony 2012: Symfony internals
Page 8: DeSymfony 2012: Symfony internals

app[_dev].phpapp[_dev].php

Controlador frontal

Page 9: DeSymfony 2012: Symfony internals

app[_dev].phpapp[_dev].php

// web/app[_env].php

1. require_once __DIR__.'/../app/bootstrap.php.cache';2. require_once __DIR__.'/../app/AppKernel.php';

3. use Symfony\Component\HttpFoundation\Request;

4. $kernel = new AppKernel('dev', true);5. $kernel->loadClassCache();6. $kernel->handle(Request::createFromGlobals())->send();

Page 10: DeSymfony 2012: Symfony internals

bootstrap.php.cachebootstrap.php.cache

Clases/namespaces en

un único fichero.

Page 11: DeSymfony 2012: Symfony internals

bootstrap.php.cachebootstrap.php.cache

// app/bootstrap.php.cache

namespace { require_once __DIR__.'/autoload.php'; }

namespace Symfony\Component\DependencyInjection{ interface ContainerAwareInterface { function setContainer(ContainerInterface $container = null); } ...}

Page 12: DeSymfony 2012: Symfony internals

bootstrap.php.cachebootstrap.php.cache

Reduce operaciones I/O

Page 13: DeSymfony 2012: Symfony internals

bootstrap.php.cachebootstrap.php.cache

Carga autoload.php

Page 14: DeSymfony 2012: Symfony internals

autoload.phpautoload.php// app/autoload.php

use Symfony\Component\ClassLoader\UniversalClassLoader;

$loader = new UniversalClassLoader();$loader->registerNamespaces(array( 'Symfony' => array( __DIR__.'/../vendor/symfony/src', __DIR__.'/../vendor/bundles'), 'Assetic' => __DIR__.'/../vendor/assetic/src',));$loader->registerPrefixes(array( 'Twig_' => __DIR__.'/../vendor/twig/lib',));

Page 15: DeSymfony 2012: Symfony internals

ClassLoaderClassLoader

Autoload de

clases/interfaces en

Symfony2

Page 16: DeSymfony 2012: Symfony internals

ClassLoader

Page 17: DeSymfony 2012: Symfony internals

ClassLoaderClassLoader

Implementa PSR-0

Page 18: DeSymfony 2012: Symfony internals

PSR-0PSR-0

Aprobado por el “Framework Interop Group”

github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md

Page 19: DeSymfony 2012: Symfony internals

PSR-0PSR-0

Fully Qualified Name

Filesystem

\Symfony\Core\Request\Zend\Mail\Message

[vendor_path]/Symfony/Core/Request.php[vendor_path]/Zend/Mail/Message.php

Page 20: DeSymfony 2012: Symfony internals

ClassLoaderClassLoader// namespaced class name$namespace = substr($class, 0, $pos);foreach ($this->namespaces as $ns => $dirs) { if (0 !== strpos($namespace, $ns)) { continue; }

foreach ($dirs as $dir) { $className = substr($class, $pos + 1); $file = $dir . DIR_SEPARATOR . str_replace('\\',DIR_SEPARATOR, $namespace) . DIR_SEPARATOR . str_replace('_', DIR_SEPARATOR, $className) . '.php'; if (file_exists($file)) { return $file; } }}

Page 21: DeSymfony 2012: Symfony internals

ClassLoaderClassLoader

$loader->findFile( 'Symfony\Component\HttpFoundation\Request');

/Sites/desymfony/app/../vendor/symfony/src/ Symfony/Component/HttpFoundation/Request.php

Page 22: DeSymfony 2012: Symfony internals

app[_dev].phpapp[_dev].php

// web/app[_env].php

1. require_once __DIR__.'/../app/bootstrap.php.cache';2. require_once __DIR__.'/../app/AppKernel.php';

3. use Symfony\Component\HttpFoundation\Request;

4. $kernel = new AppKernel('dev', true);5. $kernel->loadClassCache();6. $kernel->handle(Request::createFromGlobals())->send();

Page 23: DeSymfony 2012: Symfony internals

AppKernel.phpAppKernel.php// src/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel{ public function registerBundles() { $bundles = array( new Symfony\Bundle\TwigBundle\TwigBundle(), ... ); return $bundles; }

public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); }}

Page 24: DeSymfony 2012: Symfony internals

app[_dev].phpapp[_dev].php

// web/app[_env].php

1. require_once __DIR__.'/../app/bootstrap.php.cache';2. require_once __DIR__.'/../app/AppKernel.php';

3. use Symfony\Component\HttpFoundation\Request;

4. $kernel = new AppKernel('dev', true);5. $kernel->loadClassCache();6. $kernel->handle(Request::createFromGlobals())->send();

EnvironmentDebug

Page 25: DeSymfony 2012: Symfony internals

AppKernel.phpAppKernel.php

If (true === $debug) {

} else {

Guarda inicio petición (microtime)

display_errors = 1

error_reporting = -1

DebugUniversalClassLoader

}display_errors = 0

Page 26: DeSymfony 2012: Symfony internals

app[_dev].phpapp[_dev].php

// web/app[_env].php

1. require_once __DIR__.'/../app/bootstrap.php.cache';2. require_once __DIR__.'/../app/AppKernel.php';

3. use Symfony\Component\HttpFoundation\Request;

4. $kernel = new AppKernel('dev', true);5. $kernel->loadClassCache();6. $kernel->handle(Request::createFromGlobals())->send();

Page 27: DeSymfony 2012: Symfony internals

LoadClassCacheLoadClassCache

Objetivo: mapear FQN y

paths de las principales

clases/interfaces

Page 28: DeSymfony 2012: Symfony internals

LoadClassCacheLoadClassCache

Se cachea en

classes.map y

classes.php.meta

Page 29: DeSymfony 2012: Symfony internals

app[_dev].phpapp[_dev].php

// web/app[_env].php

1. require_once __DIR__.'/../app/bootstrap.php.cache';2. require_once __DIR__.'/../app/AppKernel.php';

3. use Symfony\Component\HttpFoundation\Request;

4. $kernel = new AppKernel('dev', true);5. $kernel->loadClassCache();6. $kernel->handle(Request::createFromGlobals())->send();

Page 30: DeSymfony 2012: Symfony internals

RequestRequest

Componente

HttpFoundation

Page 31: DeSymfony 2012: Symfony internals

RequestRequest

Abstracción OO de

una petición HTTP

Page 32: DeSymfony 2012: Symfony internals

RequestRequest

GET /index.php HTTP/1.1␍␊ Host: test.com␍␊ Accept-Language:en;q=0.8␍␊ Accept-Encoding:gzip␍␊ User-Agent: Mozilla/5.0␍␊ ␍␊

$_GET

$_POST

$_COOKIE

$_FILES

$_SERVER

Request

queryrequestcookies

filesserver

headers

getHostgetClientIp

...

Page 33: DeSymfony 2012: Symfony internals

app[_dev].phpapp[_dev].php

// web/app[_env].php

1. require_once __DIR__.'/../app/bootstrap.php.cache';2. require_once __DIR__.'/../app/AppKernel.php';

3. use Symfony\Component\HttpFoundation\Request;

4. $kernel = new AppKernel('dev', true);5. $kernel->loadClassCache();6. $kernel->handle(Request::createFromGlobals())->send();

Page 34: DeSymfony 2012: Symfony internals

HttpKernelHttpKernel

Corazón de Symfony

Page 35: DeSymfony 2012: Symfony internals

HttpKernelHttpKernel

Gestiona entorno

formado por bundles,

DIC...

Page 36: DeSymfony 2012: Symfony internals

Inicialización de

bundles y DIC

$kernel->boot()$kernel->boot()

Page 37: DeSymfony 2012: Symfony internals

Se cargan los bundles

definidos en

AppKernel::registerBundles()

$kernel->initializeBundles()$kernel->initializeBundles()

Page 38: DeSymfony 2012: Symfony internals

Se genera usando el

ContainerBuilder del

DependencyInjection

$kernel->initializeContainer()$kernel->initializeContainer()

Page 39: DeSymfony 2012: Symfony internals

ContainerBuilderContainerBuilder

// example.com/src/container.phpuse Symfony\Component\DependencyInjection;use Symfony\Component\DependencyInjection\Reference;

$sc = new DependencyInjection\ContainerBuilder(); $sc->register('context', 'Symfony\Component\Routing\RequestContext');$sc->register('matcher', 'Symfony\Component\Routing\Matcher\UrlMatcher') ->setArguments(array($routes, new Reference('context')));

$sc->register('framework', 'Simplex\Framework') ->setArguments(array(new Reference('dispatcher'), new Reference('resolver'))) ;

http://fabien.potencier.org/article/62/create-your-own-framework-on-top-of-the-symfony2-components-part-12

Page 40: DeSymfony 2012: Symfony internals

{rootDir}{Environment}

[Debug]ProjectContainer

$kernel->initializeContainer()$kernel->initializeContainer()

Page 41: DeSymfony 2012: Symfony internals

Por cada bundle se ejecuta

Bundle::build() y se

registran sus extensiones

$kernel->initializeContainer()$kernel->initializeContainer()

Page 42: DeSymfony 2012: Symfony internals

Por cada bundle se asigna

el 'container' y se ejecuta

el boot()

$kernel->boot()$kernel->boot()

Page 43: DeSymfony 2012: Symfony internals

Objetivo: Devolver un

objeto Response

$kernel->handle()$kernel->handle()

Page 44: DeSymfony 2012: Symfony internals

Se lanza en cuanto

llega la petición

Evento kernel.requestEvento kernel.request

Page 45: DeSymfony 2012: Symfony internals

Un listener puede

devolver un Response

y 'finalizar' la ejecución

Evento kernel.requestEvento kernel.request

Page 46: DeSymfony 2012: Symfony internals

FrameworkBundle lo

utiliza para rellenar el

valor de _controller

Evento kernel.requestEvento kernel.request

Page 47: DeSymfony 2012: Symfony internals

Utiliza un RouterMatcher

(autogenerado por comp.

Routing)

RouterListenerRouterListener

Page 48: DeSymfony 2012: Symfony internals

RouterListenerRouterListener

// app/cache/dev/appdevUrlMatcher.php

class appdevUrlMatcher extends RedirectableUrlMatcher{ ... public function match($pathinfo) { ... // _demo_hello if (0 === strpos($pathinfo, '/demo/hello') && preg_match('#^/demo/hello/(?P<name>[^/]+?)$#s', $pathinfo, $m)) { return array_merge($this->mergeDefaults($m, array( '_controller' => 'Acme\\DemoBundle\\Controller\\DemoController::helloAction') ), array( '_route' => '_demo_hello')); } ... }

Page 49: DeSymfony 2012: Symfony internals

Encargado de devolver un

controlador + argumentos

a partir de _controller

ControllerResolverControllerResolver

Page 50: DeSymfony 2012: Symfony internals

Agrupa componentes y

librerías para crear un

framework MVC

FrameworkBundleFrameworkBundle

Page 51: DeSymfony 2012: Symfony internals

Además, ofrece...

FrameworkBundleFrameworkBundle

Page 52: DeSymfony 2012: Symfony internals

php app/console

FrameworkBundleFrameworkBundle

Page 53: DeSymfony 2012: Symfony internals

FrameworkBundleFrameworkBundle// app/console

#!/usr/bin/env php<?php

require_once __DIR__.'/bootstrap.php.cache';require_once __DIR__.'/AppKernel.php';

use Symfony\Bundle\FrameworkBundle\Console\Application;use Symfony\Component\Console\Input\ArgvInput;

$input = new ArgvInput();$env = $input->getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev');$debug = !$input->hasParameterOption(array('--no-debug', ''));

$kernel = new AppKernel($env, $debug);$application = new Application($kernel);$application->run();

Page 54: DeSymfony 2012: Symfony internals

Comandos

FrameworkBundleFrameworkBundle

assets:install

cache:clear

cache:warmup

container:debug

router:dump-apache

router:debug

Page 55: DeSymfony 2012: Symfony internals

Controller

FrameworkBundleFrameworkBundle

Page 56: DeSymfony 2012: Symfony internals

FrameworkBundleFrameworkBundle

// src/Acme/DemoBundle/Controller/DemoController

namespace Acme\DemoBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DemoController extends Controller{ public function helloAction($name) { ... }}

Page 57: DeSymfony 2012: Symfony internals

Y mucho más: ESI,

WebTestCase,

DataCollectors...

FrameworkBundleFrameworkBundle

Page 58: DeSymfony 2012: Symfony internals

Una vez definido el

controlador, se lanza el

evento

Evento kernel.controllerEvento kernel.controller

Page 59: DeSymfony 2012: Symfony internals

Se puede cambiar,

pasando un Callable

Evento kernel.controllerEvento kernel.controller

Page 60: DeSymfony 2012: Symfony internals

Se lanza si el Controller

no devuelve un objeto

Response

Evento kernel.viewEvento kernel.view

Page 61: DeSymfony 2012: Symfony internals

Objetivo: construir un

objeto Response del

return del Controller

Evento kernel.viewEvento kernel.view

Page 62: DeSymfony 2012: Symfony internals

Permite reemplazar o

modificar el objeto

Response

Evento kernel.responseEvento kernel.response

Page 63: DeSymfony 2012: Symfony internals

Oportunidad para

convertir una Exception

en un Response

Evento kernel.exceptionEvento kernel.exception

Page 64: DeSymfony 2012: Symfony internals

Todos heredan de

KernelEvent

EventosEventos

getRequestType(): MASTER_REQUEST o SUB_REQUEST

getKernel();

getRequest();

Page 65: DeSymfony 2012: Symfony internals

HTTP/1.1 200 OK Content-type: text/html Date:Thu, 31 May 2012 17:54:50 GMT

<!DOCTYPE HTML> <html lang="es"> <head> <meta charset="utf-8"> ...

Response

Headers

Version

Content

Status code

Status text

Charset

ResponseResponse

Page 66: DeSymfony 2012: Symfony internals

// web/app[_env].php

1. require_once __DIR__.'/../app/bootstrap.php.cache';2. require_once __DIR__.'/../app/AppKernel.php';

3. use Symfony\Component\HttpFoundation\Request;

4. $kernel = new AppKernel('dev', true);5. $kernel->loadClassCache();6. $kernel->handle(Request::createFromGlobals())->send();

app[_dev].phpapp[_dev].php

Page 67: DeSymfony 2012: Symfony internals

Envía las cabeceras y el

contenido

Response::send()Response::send()

Page 68: DeSymfony 2012: Symfony internals

Response::send()Response::send()

Page 69: DeSymfony 2012: Symfony internals

https://github.com/raulfraile/internals-desymfony2012

DemoDemo

Page 70: DeSymfony 2012: Symfony internals

¡Gracias!

Page 71: DeSymfony 2012: Symfony internals

http://www.flickr.com/photos/connectirmeli/7233514862

http://www.flickr.com/photos/barretthall/6070677596

http://www.flickr.com/photos/f-oxymoron/5005673112/

FotografíasFotografías

Page 72: DeSymfony 2012: Symfony internals

¿Preguntas?¿Preguntas?