1. El backend «casi» no importa
BACKEND
-10 ms BBDD
-200 ms PHP
-5 ms Apache
215 ms
FRONTEND
-5 seg imágenes
-1 seg CSS
-2 seg JavaScript
8 seg
1. El backend «casi» no importa
BACKEND
-10 ms BBDD
-200 ms PHP
-5 ms Apache
215 ms
FRONTEND
-5 seg imágenes
-1 seg CSS
-2 seg JavaScript
8 seg8.000 ms
1. El backend «casi» no importa
BACKEND
215 ms
FRONTEND
8.000 ms
Mejorando el rendimiento
$app->run();5
public function run(Request $request = null)
{
if (null === $request) {
$request = Request::createFromGlobals();
}
$response = $this->handle($request);
$response->send();
$this->terminate($request, $response);
}
Mejorando el rendimiento
$app->run();5
public function run(Request $request = null)
{
if (null === $request) {
$request = Request::createFromGlobals();
}
$response = $this->handle($request);
$response->send();
$this->terminate($request, $response);
}
Mejorando el rendimiento
$app->run();5
public function run(Request $request = null)
{
if (null === $request) {
$request = Request::createFromGlobals();
}
$response = $this->handle($request);
$response->send();
$this->terminate($request, $response);
}
Mejorando el rendimiento
$app->run();5
public function run(Request $request = null)
{
if (null === $request) {
$request = Request::createFromGlobals();
}
$response = $this->handle($request);
$response->send();
$this->terminate($request, $response);
}
Mejorando el rendimiento
$app->run();5
public function run(Request $request = null)
{
if (null === $request) {
$request = Request::createFromGlobals();
}
$response = $this->handle($request);
$response->send();
$this->terminate($request, $response);
}
Mejorando el rendimiento
$app->run();5
public function run(Request $request = null)
{
if (null === $request) {
$request = Request::createFromGlobals();
}
$response = $this->handle($request);
$response->send();
$this->terminate($request, $response);
}
$this->boot();
$response = $this['kernel']->
handle($request, $type, $catch);
return $response;
Mejorando el rendimiento
$app->run();5
public function run(Request $request = null)
{
if (null === $request) {
$request = Request::createFromGlobals();
}
$response = $this->handle($request);
$response->send();
$this->terminate($request, $response);
}
$this->boot();
$response = $this['kernel']->
handle($request, $type, $catch);
return $response;
foreach ($this->providers as $provider) {
$provider->boot();
}
En resumen
1. Se carga un array de ~1.500 elementos.
2. Se crea un objeto SilexApplication.
3. Se ejecuta el register() de todos los servicios.
4. Se crea un objeto Request.
5. Se ejecuta el boot() de todos los servicios.
6. Se crea un objeto Response (con tu controlador).
7. Se envía la respuesta al usuario.
8. Se ejecuta el terminate() de la aplicación.
App. Silex
Response vs. JsonResponse
$app->get('/', function () use ($app) {
return new Response(
json_encode( $datos ), 200,
array('Content-Type' => 'application/json')
);
});
Response vs. JsonResponse
$app->get('/', function () use ($app) {
return new Response(
json_encode( $datos ), 200,
array('Content-Type' => 'application/json')
);
});
$app->get('/', function () use ($app) {
return new JsonResponse($datos);
});
Response vs. JsonResponse
$app->get('/', function () use ($app) {
return new Response(
json_encode( $datos ), 200,
array('Content-Type' => 'application/json')
);
});
$app->get('/', function () use ($app) {
return new JsonResponse($datos);
});
$app->get('/', function () use ($app) {
return $app->json($datos);
});
Response vs. JsonResponse
$app->get('/', function () use ($app) {
return new Response( json_encode( $datos ), ... );
});
class JsonResponse extends Response
{
// RFC4627-compliant JSON
$this->data = json_encode($datos,
JSON_HEX_TAG | JSON_HEX_APOS
| JSON_HEX_AMP | JSON_HEX_QUOT
);
Response vs. BinaryFileResponse
$app->get('/', function () use ($app) {
return new Response(
file_get_contents($archivo)
);
});
Rendimiento
$app->get('/', function () use ($app) {
return new BinaryFileResponse($archivo);
});
Response vs. BinaryFileResponse
$app->get('/', function () use ($app) {
return new Response(
file_get_contents($archivo)
);
});
Rendimiento
$app->get('/', function () use ($app) {
return new BinaryFileResponse($archivo);
});
$app->get('/', function () use ($app) {
return $app->sendFile($archivo);
});
Ventajas de reutilizar aplicaciones
• Sólo se aplica la seguridad en la
aplicación backend.
• El rendimiento de la parte pública no
se ve afectado.
• En el backend están disponibles todas
las rutas y controladores públicos.
Extendiendo servicios
$app['logger'] = function () {
return new Logger();
};
$app['logger'] = function () {
$logger = new Logger();
$logger->metodoXXX();
return $logger;
};
lo que tengo
lo que quiero
Ejemplo extensión servicios
class ServiceControllerServiceProvider
implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['resolver'] = $app->share($app->extend('resolver',
function ($resolver, $app) {
return new ServiceControllerResolver($resolver, $app);
}));
}
public function boot(Application $app) { // ... }
}
Ejemplo extensión servicios
class ServiceControllerServiceProvider
implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['resolver'] = $app->share($app->extend('resolver',
function ($resolver, $app) {
return new ServiceControllerResolver($resolver, $app);
}));
}
public function boot(Application $app) { // ... }
}
Silex 1.0
public function before($callback, $priority = 0)
{
$this['dispatcher']->addListener(
KernelEvents::REQUEST,
function (GetResponseEvent $event) use ($callback) {
// ...
}
Los métodos on(), before(), after(), error() fuerzan la
creación del dispatcher y todas sus dependencias.
Lazy dispatcher
public function before($callback, $priority = 0)
{
$this['dispatcher'] = $this->share($this->extend('dispatcher',
function ($dispatcher, $app) use ($callback, $priority) {
$dispatcher->addListener(
KernelEvents::REQUEST, $callback, $priority
);
return $dispatcher;
}));
}
En Silex 1.1, los métodos on(), before(), after(),
error() crean el dispatcher en el último momento.
Controladores frontales
index.php index_dev.php
Para las aplicaciones complejas usamos
seis entornos de ejecución:
DEV PROD TEST BETA STAGING ROLLOUT
Fuente: http://37signals.com/svn/posts/3535-beyond-the-default-rails-environments
37signals
Traits en PHP
trait metodosComunes {
function metodo1() { ... }
function metodo2() { ... }
}
class miClase {
use metodosComunes;
// ...
}
Traits en PHP
trait metodosComunes {
function metodo1() { ... }
function metodo2() { ... }
}
class miClase {
use metodosComunes;
// ...
}
$objeto = new MiClase();
$objeto->metodo1();
$objeto->metodo2();
Traits en PHP
trait metodosComunes {
function metodo1() { ... }
function metodo2() { ... }
}
class miClase {
use metodosComunes;
// ...
}
$objeto = new MiClase();
$objeto->metodo1();
$objeto->metodo2();
PHP 5.4
o superior
Traits en aplicaciones Silex
require_once __DIR__.'/../vendor/autoload.php';
$app = new SilexApplication();
$app->register( ... );
$app->get(...);
$app->run();
Traits en aplicaciones Silex
require_once __DIR__.'/../vendor/autoload.php';
$app = new SilexApplication();
$app->register( ... );
$app->get(...);
$app->run();
class Application extends SilexApplication {
use SilexApplicationTwigTrait;
use SilexApplicationUrlGeneratorTrait;
}
$app = new Application();
Sin usar UrlGeneratorTrait
$app->get('/', function () use ($app) {
$url = $this['url_generator']
->generate($ruta, $params);
$urlAbsoluta = $this['url_generator']
->generate($ruta, $params, true);
});
ResponsibleServiceProvider
class ResponsibleServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
if (empty($app['serializer'])) {
$app->register(new SerializerServiceProvider());
}
}
public function boot(Application $app)
{
$app['dispatcher']->addSubscriber(
new ResponsibleListener($app['serializer'])
);
}
}
ResponsibleListener
class ResponsibleListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(KernelEvents::VIEW => array('onKernelView', -10));
}
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$result = $event->getControllerResult();
$supported = array('json', 'xml');
$accepted = $request->getAcceptableContentTypes();
// ...
$event->setResponse(new Response(
$this->encoder->encode($result, $format),
200, array('Content-Type' => $type)
));
// ...
ResponsibleListener
class ResponsibleListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(KernelEvents::VIEW => array('onKernelView', -10));
}
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$result = $event->getControllerResult();
$supported = array('json', 'xml');
$accepted = $request->getAcceptableContentTypes();
// ...
$event->setResponse(new Response(
$this->encoder->encode($result, $format),
200, array('Content-Type' => $type)
));
// ...
ResponsibleListener
class ResponsibleListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(KernelEvents::VIEW => array('onKernelView', -10));
}
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$result = $event->getControllerResult();
$supported = array('json', 'xml');
$accepted = $request->getAcceptableContentTypes();
// ...
$event->setResponse(new Response(
$this->encoder->encode($result, $format),
200, array('Content-Type' => $type)
));
// ...
ResponsibleListener
class ResponsibleListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(KernelEvents::VIEW => array('onKernelView', -10));
}
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$result = $event->getControllerResult();
$supported = array('json', 'xml');
$accepted = $request->getAcceptableContentTypes();
// ...
$event->setResponse(new Response(
$this->encoder->encode($result, $format),
200, array('Content-Type' => $type)
));
// ...
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(
__DIR__.'/config/config_'.$this->getEnvironment().'.yml'
);
}
Configuración por entorno (Sf2)
app/AppKernel.php
public function getEnvironment()
{
return $this->container->getParameter('kernel.environment');
}
Controladores Silex en clases
$app->get('/', 'AcmeControladores::index');
use SilexApplication;
use SymfonyComponentHttpFoundationRequest;
namespace Acme {
class Controladores {
public function index(Application $app, Request $request) {
// ...
return $app['twig']->render('portada.twig'),
}
}
}
Controladores Silex en clases
$app->get('/', 'AcmeControladores::index');
use SilexApplication;
use SymfonyComponentHttpFoundationRequest;
namespace Acme {
class Controladores {
public function index(Application $app, Request $request) {
// ...
return $app['twig']->render('portada.twig'),
}
}
}
Controladores Silex en clases
$app->get('/', 'AcmeControladores::index');
use SilexApplication;
use SymfonyComponentHttpFoundationRequest;
namespace Acme {
class Controladores {
public function index(Application $app, Request $request) {
// ...
return $app['twig']->render('portada.twig'),
}
}
}
Controladores Silex en servicios
$app->get('/', "controladores:indexAction");
use AcmeControladores;
$app['controladores'] = $app->share(function(Request $request) use ($app) {
return new Controladores($app, $request);
});
use SilexApplication;
use SymfonyComponentHttpFoundationRequest;
namespace Acme {
class Controladores {
protected $app;
protected $request;
public function __construct(Application $app, Request $request) {
$this->app = $app;
$this->request = $request;
}
public function indexAction() {
// ...
return $app['twig']->render('portada.twig'),
}
}
}
Controladores Silex en servicios
$app->get('/', "controladores:indexAction");
use AcmeControladores;
$app['controladores'] = $app->share(function(Request $request) use ($app) {
return new Controladores($app, $request);
});
use SilexApplication;
use SymfonyComponentHttpFoundationRequest;
namespace Acme {
class Controladores {
protected $app;
protected $request;
public function __construct(Application $app, Request $request) {
$this->app = $app;
$this->request = $request;
}
public function indexAction() {
// ...
return $app['twig']->render('portada.twig'),
}
}
}
Controladores Silex en servicios
$app->get('/', "controladores:indexAction");
use AcmeControladores;
$app['controladores'] = $app->share(function(Request $request) use ($app) {
return new Controladores($app, $request);
});
use SilexApplication;
use SymfonyComponentHttpFoundationRequest;
namespace Acme {
class Controladores {
protected $app;
protected $request;
public function __construct(Application $app, Request $request) {
$this->app = $app;
$this->request = $request;
}
public function indexAction() {
// ...
return $app['twig']->render('portada.twig'),
}
}
}
La aplicación no está desacoplada
La aplicación sigue dependiendo de
los objetos de Silex ($app, $request).
class Controladores {
public function __construct(Application $app, Request $request) {
$this->app = $app;
$this->request = $request;
}
public function indexAction() {
// ...
return $app['twig']->render('portada.twig'),
}
}
La aplicación no está desacoplada
La aplicación sigue dependiendo de
los objetos de Silex ($app, $request).
class Controladores {
public function __construct(Application $app, Request $request) {
$this->app = $app;
$this->request = $request;
}
public function indexAction() {
// ...
return $app['twig']->render('portada.twig'),
}
}
1. Controladores como servicios
$app->get('/auction/{id}', 'interactor.auction_view')
$app['resolver'] = $app->share($app->extend('resolver',
function ($resolver, $app) {
$resolver = new ControllerResolver($resolver, $app);
return $resolver;
}));
1. ControllerResolver propio
class ControllerResolver implements ControllerResolverInterface
{
public function getController(Request $request)
{
$controller = $request->attributes->get('_controller');
if (!is_string($controller) || !isset($this->container[$controller])) {
return $this->resolver->getController($request);
}
return $this->container[$controller];
}
}
1. Definiendo los controladores
class ServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['interactor.auction_view'] = $app->share(function () {
return new AuctionView( ... );
});
// ...
}
public function boot(Application $app) { ... }
}
1. Definiendo los controladores
class ServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['interactor.auction_view'] = $app->share(function () {
return new AuctionView( ... );
});
// ...
}
public function boot(Application $app) { ... }
}
clase de mi
aplicación PHP
1. AuctionView
class AuctionView
{
public function __construct(AuctionRepository $repo)
{
// ...
}
public function __invoke(AuctionViewRequest $request)
{
// ...
}
}
método mágico:
objeto()
1. AuctionView
class AuctionView
{
// ...
public function __invoke(AuctionViewRequest $request)
{
$auction = $this->repo->find($request->id);
$view = AuctionViewDto::fromEntity($auction);
return new AuctionViewResponse($view);
}
}
1. AuctionView
class AuctionView
{
// ...
public function __invoke(AuctionViewRequest $request)
{
$auction = $this->repo->find($request->id);
$view = AuctionViewDto::fromEntity($auction);
return new AuctionViewResponse($view);
}
}
Request()
1. AuctionView
class AuctionView
{
// ...
public function __invoke(AuctionViewRequest $request)
{
$auction = $this->repo->find($request->id);
$view = AuctionViewDto::fromEntity($auction);
return new AuctionViewResponse($view);
}
}
Request()
Doctrine
1. AuctionView
class AuctionView
{
// ...
public function __invoke(AuctionViewRequest $request)
{
$auction = $this->repo->find($request->id);
$view = AuctionViewDto::fromEntity($auction);
return new AuctionViewResponse($view);
}
}
Request()
Doctrine
Twig
Inline templates (Ruby / Sinatra)
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Hello world.
Inline templates (Ruby / Sinatra)
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Hello world.
controladores, rutas y
plantillas en 1 solo archivo
Inline templates (Ruby / Sinatra)
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Hello world.
controladores, rutas y
plantillas en 1 solo archivo
mustache / silex-provider
$ composer require mustache/silex-provider 1.0
$app->register(
new MustacheSilexProviderMustacheServiceProvider, array(
'mustache.loader' => new Mustache_Loader_InlineLoader(
__FILE__, __COMPILER_HALT_OFFSET__
)
));
mustache / silex-provider
$ composer require mustache/silex-provider 1.0
$app->register(
new MustacheSilexProviderMustacheServiceProvider, array(
'mustache.loader' => new Mustache_Loader_InlineLoader(
__FILE__, __COMPILER_HALT_OFFSET__
)
));
este mismo
archivo
mustache / silex-provider
$ composer require mustache/silex-provider 1.0
$app->register(
new MustacheSilexProviderMustacheServiceProvider, array(
'mustache.loader' => new Mustache_Loader_InlineLoader(
__FILE__, __COMPILER_HALT_OFFSET__
)
));
este mismo
archivo
el lugar en el que se encuentre la
llamada a __halt_compiler()