Advertisement

Silex al límite

Formador especializado en nuevas tecnologías at SensioLabs
Jun. 25, 2013
Advertisement

More Related Content

Advertisement

Recently uploaded(20)

Silex al límite

  1. 20-22 junio 2013 Madrid SILEX AL LÍMITE Javier Eguiluz deSymfony
  2. ¡muchas gracias a nuestros patrocinadores! deSymfony
  3. ME PRESENTO
  4. Sobre mí Javier Eguiluz Formador y programador entusiasta de Symfony. javiereguiluz.com
  5. 2011
  6. Nacho Martín Microframework Silex Nacho Martín JORNADAS SYMFONY 2011 1-3 JULIO, CASTELLÓN DESYMFONY.COM deSymfony 2011
  7. AGENDA
  8. RENDIMIENTO AL LÍMITE CARACTERÍSTICAS AL LÍMITE
  9. RENDIMIENTO AL LÍMITE CARACTERÍSTICAS AL LÍMITE
  10. RENDIMIENTO AL LÍMITE CARACTERÍSTICAS AL LÍMITE
  11. PRIMERA PARTE RENDIMIENTO AL LÍMITE
  12. PASOS PREVIOS
  13. 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
  14. 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
  15. 1. El backend «casi» no importa BACKEND 215 ms FRONTEND 8.000 ms
  16. no te pierdas esta charla Miquel Company
  17. 2. Tu aplicación «casi» no importa TU APLICACIÓN S.O. BBDD SERVIDOR WEB PHPAPC VARNISH
  18. 2. Tu aplicación «casi» no importa TU APLICACIÓN S.O. BBDD SERVIDOR WEB PHPAPC VARNISH TU APLICACIÓN
  19. no te pierdas esta charla Ricard Clau y tampoco te pierdas su blog: ricardclau.com
  20. 3. Nada importa si no lo miras MySQL Slow Query Log APC log ¿cada cuánto miráis estos archivos?
  21. 3. Nada importa si no lo miras MySQL Slow Query Log APC log ¿cada cuánto miráis estos archivos? ¿los miráis alguna vez?
  22. APC log
  23. APC log HttpCacheESI.php
  24. APC log
  25. APC log 'http_cache.esi' => null
  26. Cristina Quintana no te pierdas esta charla
  27. SILEX INTERNALS
  28. Una aplicación Silex típica require_once __DIR__.'/../vendor/autoload.php'; $app = new SilexApplication(); $app->register( ... ); $app->get('/', function() use($app) { return $app['twig']->render('portada.twig'), }); $app->run();
  29. Una aplicación Silex típica require_once __DIR__.'/../vendor/autoload.php'; $app = new SilexApplication(); $app->register( ... ); $app->get('/', function() use($app) { return $app['twig']->render('portada.twig'), }); $app->run(); 1 2 3 4 5
  30. Mejorando el rendimiento require_once __DIR__.'/../vendor/autoload.php';1
  31. Mejorando el rendimiento require_once __DIR__.'/../vendor/autoload.php';1 $ composer install --optimize-autoloader
  32. Mejorando el rendimiento require_once __DIR__.'/../vendor/autoload.php';1 $ composer install --optimize-autoloader $ composer dump-autoload --optimize
  33. Mejorando el rendimiento require_once __DIR__.'/../vendor/autoload.php';1 $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'SessionHandlerInterface' => $vendorDir . '/symfony/http-foundation/Symfony/Component/ HttpFoundation/Resources/stubs/SessionHandlerInterface.php', ); vendor/composer/autoload_classmap.php original
  34. Mejorando el rendimiento require_once __DIR__.'/../vendor/autoload.php';1 // autoload_classmap.php generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'DoctrineDBALCacheArrayStatement' => $vendorDir . '/doctrine/dbal/ ... /ArrayStatement.php', 'DoctrineDBALCacheCacheException' => $vendorDir . '/doctrine/dbal/ ... /CacheException.php', // ... 'MonologFormatterChromePHPFormatter' => $vendorDir . '/monolog/... /ChromePHPFormatter.php', 'MonologFormatterFormatterInterface' => $vendorDir . '/monolog/ ... /FormatterInterface.php', // ... 'Pimple' => $vendorDir . '/pimple/pimple/lib/Pimple.php', 'PsrLogAbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php', vendor/composer/autoload_classmap.php optimizado 1.762 elementos
  35. Revisa tu composer.json require_once __DIR__.'/../vendor/autoload.php';1 { "require": { "php": ">=5.3.3", "silex/silex": "~1.0", "silex/web-profiler": "1.0.*", "symfony/stopwatch": "~2.2", "twig/twig": "~1.13", "symfony/form": "~2.2", "symfony/translation": "~2.2", "symfony/validator": "~2.2", ... } }
  36. Revisa tu composer.json require_once __DIR__.'/../vendor/autoload.php';1 { "require": { "php": ">=5.3.3", "silex/silex": "~1.0", "silex/web-profiler": "1.0.*", "symfony/stopwatch": "~2.2", "twig/twig": "~1.13", "symfony/form": "~2.2", "symfony/translation": "~2.2", "symfony/validator": "~2.2", ... } } ¿realmente los usas?
  37. Una aplicación Silex típica require_once __DIR__.'/../vendor/autoload.php'; $app = new SilexApplication(); $app->register( ... ); $app->get('/', function() use($app) { return $app['twig']->render('portada.twig'), }); $app->run(); 1 2 3 4 5 ✔
  38. Mejorando el rendimiento $app = new SilexApplication();2
  39. Mejorando el rendimiento $app = new SilexApplication();2 class Application extends Pimple implements HttpKernelInterface, TerminableInterface { public function __construct() { $this['logger'] = ... $this['routes'] = ... $this['controllers'] = ... $this['route_factory'] = ... $this['exception_handler'] = ... $this['dispatcher'] = ... } }
  40. Mejorando el rendimiento $app = new SilexApplication();2 class Application extends Pimple implements HttpKernelInterface, TerminableInterface { public function __construct() { $this['logger'] = ... $this['routes'] = ... $this['controllers'] = ... $this['route_factory'] = ... $this['exception_handler'] = ... $this['dispatcher'] = ... } } ~100 líneas ~12 servicios
  41. mini truco
  42. Configurando la aplicación $app = new SilexApplication(); $app = new SilexApplication([ 'request.http_port' => 80 ]);
  43. Configurando la aplicación $app = new SilexApplication(); $app = new SilexApplication([ 'request.http_port' => 80 ]); $app = new SilexApplication([ 'request.http_port' => 80, 'request.https_port' => 443, 'debug' => false, 'charset' => 'UTF-8', 'locale' => 'en', ]);
  44. Configurando la aplicación $app = new SilexApplication(); $app = new SilexApplication([ 'request.http_port' => 80, 'mi_app.mi_opcion_1' => '...', 'mi_app.mi_opcion_2' => '...', ]);
  45. Mejorando el rendimiento $app->register( ... );3
  46. Mejorando el rendimiento $app->register( ... );3 class Application extends Pimple implements HttpKernelInterface, TerminableInterface { public function register($provider, $values) { $this->providers[] = $provider; $provider->register($this); foreach ($values as $key => $value) { $this[$key] = $value; } }
  47. Mejorando el rendimiento $app->register( ... );3 class Application extends Pimple implements HttpKernelInterface, TerminableInterface { public function register($provider, $values) { $this->providers[] = $provider; $provider->register($this); foreach ($values as $key => $value) { $this[$key] = $value; } }
  48. Mejorando el rendimiento $app->register( ... );3 class Application extends Pimple implements HttpKernelInterface, TerminableInterface { public function register($provider, $values) { $this->providers[] = $provider; $provider->register($this); foreach ($values as $key => $value) { $this[$key] = $value; } }
  49. Mejorando el rendimiento $app->register( ... );3 $app->register( new SilexProviderDoctrineServiceProvider(), [ 'db.options' => [ 'driver' => 'pdo_mysql', 'host' => 'localhost', 'dbname' => '...', 'user' => '...', 'password' => '...', 'charset' => 'utf8', ] ]); afecta a la memoria consumida
  50. Mejorando el rendimiento $app->get('/', function() use($app) { return $app['twig']->render('portada.twig'), }); 4
  51. Mejorando el rendimiento $app->run();5
  52. 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); }
  53. 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); }
  54. 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); }
  55. 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); }
  56. 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); }
  57. 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;
  58. 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(); }
  59. 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
  60. mini truco
  61. ini_set('display_errors', 0); require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/app.php'; require __DIR__.'/../config/prod.php'; require __DIR__.'/../src/controllers.php'; if ($app['debug']) { $app->run(); } else { $app['http_cache']->run(); } Típico controlador de producción
  62. ini_set('display_errors', 0); require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/app.php'; require __DIR__.'/../config/prod.php'; require __DIR__.'/../src/controllers.php'; if ($app['debug']) { $app->run(); } else { $app['http_cache']->run(); } Típico controlador de producción innecesario
  63. ini_set('display_errors', 0); require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/app.php'; require __DIR__.'/../config/prod.php'; require __DIR__.'/../src/controllers.php'; if ($app['debug']) { $app->run(); } else { $app['http_cache']->run(); } Típico controlador de producción innecesario muy mal
  64. ini_set('display_errors', 0); require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/app.php'; require __DIR__.'/../config/prod.php'; require __DIR__.'/../src/controllers.php'; if ($app['debug']) { $app->run(); } else { $app['http_cache']->run(); } Típico controlador de producción innecesario muy mal PHP_FUNCTION(ini_set) { char *varname, *new_value; int varname_len, new_value_len; char *old_value; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &varname, &varname_len, &new_value, &new_value_len) == FAILURE) { return; } old_value = zend_ini_string(varname, varname_len + 1, 0); // ...
  65. TIPOS DE RESPUESTAS
  66. Tipos de respuestas Silex / Sf2 Response RedirectResponse StreamedResponse JsonResponse BinaryFileResponse
  67. Response vs. JsonResponse $app->get('/', function () use ($app) { return new Response( json_encode( $datos ), 200, array('Content-Type' => 'application/json') ); });
  68. 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); });
  69. 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); });
  70. 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 );
  71. Tipos de respuestas Silex / Sf2 Response RedirectResponse StreamedResponse JsonResponse BinaryFileResponse
  72. Response vs. BinaryFileResponse $app->get('/', function () use ($app) { return new Response( file_get_contents($archivo) ); });
  73. Response vs. BinaryFileResponse $app->get('/', function () use ($app) { return new Response( file_get_contents($archivo) ); }); Rendimiento
  74. 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); });
  75. 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); });
  76. Response vs. BinaryFileResponse X-Sendfile-Type X-Accel-Redirect X-Accel-Mapping
  77. Response vs. BinaryFileResponse APLICACIÓN APACHE / NGINX KERNEL X-Sendfile-Type X-Accel-Redirect X-Accel-Mapping SILEX
  78. Response vs. BinaryFileResponse APLICACIÓN APACHE / NGINX KERNELsendfile() X-Sendfile-Type X-Accel-Redirect X-Accel-Mapping SILEX
  79. REUTILIZANDO APLICACIONES
  80. usuarios web/ index.php /... /noticias / Una aplicación Silex típica
  81. usuarios web/ index.phpadministrador /... /noticias / Una aplicación Silex típica
  82. usuarios web/ index.phpadministrador /... /noticias / Una aplicación Silex típica /admin /admin/... /... /noticias /
  83. Mezclando frontend y backend $app->get('/admin', function() { ... }); $app->get('/admin/noticias', function() { ... }); $app->get('/noticias', function() { ... }); $app->get('/', function() { ... });
  84. Mezclando frontend y backend $app->get('/admin', function() { ... }); $app->get('/admin/noticias', function() { ... }); $app->get('/noticias', function() { ... }); $app->get('/', function() { ... }); ¡horrible!
  85. Mezclando frontend y backend $app->mount( '/admin', new AdminControllerProvider() ); $app->get('/noticias', function() { ... }); $app->get('/', function() { ... });
  86. Mezclando frontend y backend $app->mount( '/admin', new AdminControllerProvider() ); $app->get('/noticias', function() { ... }); $app->get('/', function() { ... }); $app->register( new SilexProviderServiceControllerServiceProvider() ); penaliza el rendimiento
  87. Mezclando frontend y backend $app->mount( '/admin', new AdminControllerProvider() ); $app->get('/noticias', function() { ... }); $app->get('/', function() { ... });
  88. Mezclando frontend y backend $app->mount( '/admin', new AdminControllerProvider() ); $app->get('/noticias', function() { ... }); $app->get('/', function() { ... }); class AdminControllerProvider implements ControllerProviderInterface { public function connect(Application $app) { $controllers = $app['controllers_factory']; $controllers->before(function() use ($app) { // seguridad }); $controllers->get('/', function (Application $app) { ... }); } }
  89. Mezclando frontend y backend $app->mount( '/admin', new AdminControllerProvider() ); $app->get('/noticias', function() { ... }); $app->get('/', function() { ... }); class AdminControllerProvider implements ControllerProviderInterface { public function connect(Application $app) { $controllers = $app['controllers_factory']; $controllers->before(function() use ($app) { // seguridad }); $controllers->get('/', function (Application $app) { ... }); } }
  90. BACKEND usuarios administrador FRONTEND Seguridad obligatoria en frontend
  91. BACKEND usuarios administrador FRONTEND Seguridad obligatoria en frontend
  92. BACKEND usuarios administrador FRONTEND Seguridad obligatoria en frontend
  93. BACKEND usuarios administrador FRONTEND Seguridad obligatoria en frontend penaliza mucho el rendimiento
  94. 1 proyecto 2 aplicaciones N módulos frontend y backend 1 proyecto 1 aplicación N bundles
  95. Aplicación backend require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/app.php'; require __DIR__.'/../config/prod.php'; require __DIR__.'/../src/controllers.php'; $app['http_cache']->run(); web/index.php
  96. Aplicación backend require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/app.php'; require __DIR__.'/../config/prod.php'; require __DIR__.'/../src/controllers.php'; $app['http_cache']->run(); web/index.php require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/backend.php'; $app->run(); web/backend.php
  97. Aplicación backend require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/app.php'; require __DIR__.'/../config/prod.php'; require __DIR__.'/../src/controllers.php'; $app['http_cache']->run(); web/index.php require_once __DIR__.'/../vendor/autoload.php'; $app = require __DIR__.'/../src/backend.php'; $app->run(); web/backend.php
  98. src/backend.php $app = require __DIR__.'/app.php'; $app->register(new SilexProviderSecurityServiceProvider()); // ... configurar seguridad ... require __DIR__.'/../config/dev.php'; require __DIR__.'/../src/controllers.php'; $app['monolog.logfile'] = __DIR__.'/../logs/backend.log'; $app->mount('/admin', new AdminControllerProvider()); return $app;
  99. src/backend.php $app = require __DIR__.'/app.php'; $app->register(new SilexProviderSecurityServiceProvider()); // ... configurar seguridad ... require __DIR__.'/../config/dev.php'; require __DIR__.'/../src/controllers.php'; $app['monolog.logfile'] = __DIR__.'/../logs/backend.log'; $app->mount('/admin', new AdminControllerProvider()); return $app;
  100. src/backend.php $app = require __DIR__.'/app.php'; $app->register(new SilexProviderSecurityServiceProvider()); // ... configurar seguridad ... require __DIR__.'/../config/dev.php'; require __DIR__.'/../src/controllers.php'; $app['monolog.logfile'] = __DIR__.'/../logs/backend.log'; $app->mount('/admin', new AdminControllerProvider()); return $app;
  101. src/backend.php $app = require __DIR__.'/app.php'; $app->register(new SilexProviderSecurityServiceProvider()); // ... configurar seguridad ... require __DIR__.'/../config/dev.php'; require __DIR__.'/../src/controllers.php'; $app['monolog.logfile'] = __DIR__.'/../logs/backend.log'; $app->mount('/admin', new AdminControllerProvider()); return $app;
  102. src/backend.php $app = require __DIR__.'/app.php'; $app->register(new SilexProviderSecurityServiceProvider()); // ... configurar seguridad ... require __DIR__.'/../config/dev.php'; require __DIR__.'/../src/controllers.php'; $app['monolog.logfile'] = __DIR__.'/../logs/backend.log'; $app->mount('/admin', new AdminControllerProvider()); return $app;
  103. src/backend.php $app = require __DIR__.'/app.php'; $app->register(new SilexProviderSecurityServiceProvider()); // ... configurar seguridad ... require __DIR__.'/../config/dev.php'; require __DIR__.'/../src/controllers.php'; $app['monolog.logfile'] = __DIR__.'/../logs/backend.log'; $app->mount('/admin', new AdminControllerProvider()); return $app;
  104. /admin /admin/... /... /noticias / usuarios administrador web/index.php /... /noticias / Aplicaciones Silex compartidas web/backend.php
  105. 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.
  106. MIDDLEWARES
  107. Middlewares de aplicación $app->before(function ($request) { // ... }); $app->after(function ($request, $response) { // ... }); $app->finish(function ($request, $response) { // ... });
  108. Middlewares de controlador $before = function ($request) use ($app) { // ... }; $after = function ($request, $response) use ($app) { // ... }; $app->get('...', function () { // ... }) ->before($before) ->after($after);
  109. El middleware before() public function before($callback, $priority = 0) { $this->on( KernelEvents::REQUEST, function ($event) use ($callback) { $ret = call_user_func($callback, $event->getRequest()); if ($ret instanceof Response) { $event->setResponse($ret); } }, $priority ); }
  110. El middleware before() public function before($callback, $priority = 0) { $this->on( KernelEvents::REQUEST, function ($event) use ($callback) { $ret = call_user_func($callback, $event->getRequest()); if ($ret instanceof Response) { $event->setResponse($ret); } }, $priority ); } public function on($eventName, $callback, $priority = 0) { // ... $dispatcher->addListener($eventName, $callback, $priority); }
  111. Middlewares «obligatorios» $app->before(function() use ($app) { if (!$app['security']->isGranted('ROLE_ADMIN')) { return $app->redirect('/'); } });
  112. Middlewares «prescindibles» $app->before(function(Request $request) { $request->query->set('token', '...'); $request->server->set('SERVER_ADDR', '::1'); });
  113. Mismo ejemplo sin middlewares require_once __DIR__.'/../vendor/autoload.php'; $app = new SilexApplication(); $app->register( ... ); $app->get('/', function() use($app) { return $app['twig']->render('portada.twig'), }); $app->run();
  114. Mismo ejemplo sin middlewares require_once __DIR__.'/../vendor/autoload.php'; $app = new SilexApplication(); $app->register( ... ); $app->get('/', function() use($app) { return $app['twig']->render('portada.twig'), }); $app->run(); public function run(Request $request = null) { if (null === $request) { $request = Request::createFromGlobals(); } // ... }
  115. Mismo ejemplo sin middlewares require_once __DIR__.'/../vendor/autoload.php'; $app = new SilexApplication(); $app->register( ... ); $app->get('/', function() use($app) { return $app['twig']->render('portada.twig'), }); $request = Request::createFromGlobals(); $request->query->set('token', '...'); $request->server->set('SERVER_ADDR', '::1'); $app->run($request);
  116. Middlewares «prescindibles» $app->before(function() use ($app) { $app['time.start'] = microtime(true); }); $app->after(function() use ($app) { $app['time.end'] = microtime(true); }); $app->finish(function() use ($app) { $elapsed = $app['time.start'] - $app['time.end']; $app['monolog']->addDebug(...); });
  117. Mismo ejemplo «sin middlewares» $app->finish(function(Request $request) use ($app) { $elapsed = 1000 * ( microtime(true) - $request->server->get('REQUEST_TIME_FLOAT') ); $app['monolog']->addDebug(...); });
  118. SERVICE PROVIDERS
  119. Anatomía de un ServiceProvider use SilexServiceProviderInterface; class XXXServiceProviderimplements ServiceProviderInterface { public function register(Application $app) { $app['xxx'] = $app->share(function () use ($app) { // ... }); $app['yyy'] = $app->share(function () { // ... }); } public function boot(Application $app) { $app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onEarlyKernelRequest'), 128); $app->before(function (Request $request) use ($app) { // ... }); $app->error(function (Exception $e) use ($app) { // ... }, 255); $app->after(function (Request $request, Response $response) use ($app) { // ... }); } }
  120. Anatomía de un ServiceProvider use SilexServiceProviderInterface; class XXXServiceProviderimplements ServiceProviderInterface { public function register(Application $app) { $app['xxx'] = $app->share(function () use ($app) { // ... }); $app['yyy'] = $app->share(function () { // ... }); } public function boot(Application $app) { $app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onEarlyKernelRequest'), 128); $app->before(function (Request $request) use ($app) { // ... }); $app->error(function (Exception $e) use ($app) { // ... }, 255); $app->after(function (Request $request, Response $response) use ($app) { // ... }); } } defines los servicios
  121. Anatomía de un ServiceProvider use SilexServiceProviderInterface; class XXXServiceProviderimplements ServiceProviderInterface { public function register(Application $app) { $app['xxx'] = $app->share(function () use ($app) { // ... }); $app['yyy'] = $app->share(function () { // ... }); } public function boot(Application $app) { $app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onEarlyKernelRequest'), 128); $app->before(function (Request $request) use ($app) { // ... }); $app->error(function (Exception $e) use ($app) { // ... }, 255); $app->after(function (Request $request, Response $response) use ($app) { // ... }); } } defines los servicios modificas la aplicación
  122. Servicio básico $app['logger'] = function () { return new Logger(); }; se crea una nueva instancia cada vez que lo usas
  123. Servicio compartido $app['logger'] = $app->share(function () { return new Logger(); }); se reutiliza la misma instancia una y otra vez
  124. Extendiendo servicios $app['logger'] = function () { return new Logger(); }; $app['logger'] = function () { $logger = new Logger(); $logger->metodoXXX(); return $logger; }; lo que tengo lo que quiero
  125. Extendiendo servicios $app['logger'] = function () { return new Logger(); }; $app['logger'] = $app->extend('logger', function($logger, $app) { $logger->metodoXXX(); return $logger; });
  126. Extendiendo servicios $app['logger'] = function () { return new Logger(); }; $app['logger'] = $app->extend('logger', function($logger, $app) { $logger->metodoXXX(); return $logger; });
  127. Extendiendo servicios $app['logger'] = function () { return new Logger(); }; $app['logger'] = $app->extend('logger', function($logger, $app) { $logger->metodoXXX(); return $logger; }); modifica este servicio
  128. Extendiendo servicios $app['logger'] = function () { return new Logger(); }; $app['logger'] = $app->extend('logger', function($logger, $app) { $logger->metodoXXX(); return $logger; }); modifica este servicio
  129. Extendiendo servicios $app['logger'] = function () { return new Logger(); }; $app['logger'] = $app->extend('logger', function($logger, $app) { $logger->metodoXXX(); return $logger; });
  130. Extendiendo servicios $app['logger'] = function () { return new Logger(); }; $app['logger'] = $app->extend('logger', function($logger, $app) { $logger->metodoXXX(); return $logger; }); lo que devuelva
  131. Extendiendo servicios compartidos $app['logger'] = $app->share(function () { return new Logger(); }); $app['logger'] = $app->share( $app->extend('logger', function($logger, $app) { $logger->metodoXXX(); return $logger; }));
  132. Extendiendo servicios compartidos $app['logger'] = $app->share(function () { return new Logger(); }); $app['logger'] = $app->share( $app->extend('logger', function($logger, $app) { $logger->metodoXXX(); return $logger; }));
  133. 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) { // ... } }
  134. 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) { // ... } }
  135. SILEX 1.1
  136. 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.
  137. 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.
  138. SEGUNDA PARTE CARACTERÍSTICAS AL LÍMITE
  139. INSTALACIÓN
  140. No instales Silex así composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing psr/log (1.0.0) - Installing symfony/routing (v2.3.0) - Installing symfony/debug (v2.3.1) - Installing symfony/http-foundation (v2.3.1) - Installing symfony/event-dispatcher (v2.3.0) - Installing symfony/http-kernel (v2.3.0) - Installing pimple/pimple (v1.0.2) - Installing silex/silex (v1.0.0) Writing lock file Generating autoload files $ cd mi_proyecto $ composer require silex/silex 1.0
  141. silex/silex 1.0
  142. silex/silex 1.0 ¿controladores?¿configuración? ¿index.php?
  143. MICRO DOES NOT MEAN LESS CODE. MICRO MEANS LESS STRUCTURE AND LESS DECISIONS MADE FOR YOU. FABIEN POTENCIER CREADOR DE SYMFONY, SILEX Y TWIG
  144. Instala una «distribución» de Silex $ composer create-project fabpot/Silex-Skeleton mi_proyecto 1.0 - Installing psr/log (1.0.0) - Installing twig/twig (v1.13.1) - Installing symfony/icu (v1.2.0) - Installing symfony/intl (v2.3.1) - Installing symfony/stopwatch (v2.3.1) - Installing symfony/twig-bridge (v2.3.1) - Installing symfony/routing (v2.3.0) - Installing symfony/debug (v2.3.1) - Installing symfony/http-foundation (v2.3.1) - Installing symfony/event-dispatcher (v2.3.0) - Installing symfony/http-kernel (v2.3.0) - Installing symfony/web-profiler-bundle (v2.3.1) - Installing pimple/pimple (v1.0.2) - Installing silex/silex (1.0.x-dev a6dde11) - Installing silex/web-profiler (v1.0.0) - Installing symfony/dom-crawler (v2.3.1) - Installing symfony/browser-kit (v2.3.1) - Installing symfony/class-loader (v2.3.1) - Installing symfony/filesystem (v2.3.1) - Installing symfony/config (v2.3.1) - Installing symfony/console (v2.3.1) - Installing symfony/css-selector (v2.3.0) - Installing symfony/finder (v2.3.1) - Installing symfony/property-access (v2.3.1) - Installing symfony/options-resolver (v2.3.1) - Installing symfony/form (v2.3.1) - Installing monolog/monolog (1.5.0) - Installing symfony/monolog-bridge (v2.3.1) - Installing symfony/process (v2.3.1) - Installing symfony/security (v2.3.1) - Installing symfony/translation (v2.3.0) - Installing symfony/validator (v2.3.1)
  145. fabpot/Silex-Skeleton
  146. fabpot/Silex-Skeleton Controladores frontales
  147. fabpot/Silex-Skeleton Tu código
  148. fabpot/Silex-Skeleton Plantillas
  149. fabpot/Silex-Skeleton Configuración y registro de servicios
  150. fabpot/Silex-Skeleton Igual que Symfony2
  151. Silex Kitchen Sink Edition $ composer create-project lyrixx/Silex-Kitchen-Edition mi_proyecto 1.0
  152. Silex Kitchen Sink Edition + + +Assetic Doctrine Silex HTML5 Boilerplate Twitter Bootstrap $ composer create-project lyrixx/Silex-Kitchen-Edition mi_proyecto 1.0
  153. mini truco
  154. Controladores frontales index.php index_dev.php
  155. 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
  156. Controladores frontales index.php index_dev.php prod.php dev.php
  157. Controladores frontales index.php index_dev.php prod.php dev.php beta.php staging.php
  158. TRAITS
  159. TRAITS IS JUST COMPILER-ASSISTED COPY-AND-PASTE STEFAN MARR SOFTWARE LANGUAGES LAB / VRIJE UNIVERSITEIT
  160. Traits en PHP trait metodosComunes { function metodo1() { ... } function metodo2() { ... } } class miClase { use metodosComunes; // ... }
  161. Traits en PHP trait metodosComunes { function metodo1() { ... } function metodo2() { ... } } class miClase { use metodosComunes; // ... } $objeto = new MiClase(); $objeto->metodo1(); $objeto->metodo2();
  162. 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
  163. FormTrait MonologTrait SecurityTrait SwiftmailerTrait TranslationTrait TwigTrait UrlGeneratorTrait traits
  164. Traits en aplicaciones Silex require_once __DIR__.'/../vendor/autoload.php'; $app = new SilexApplication(); $app->register( ... ); $app->get(...); $app->run();
  165. 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();
  166. Sin usar UrlGeneratorTrait $app->get('/', function () use ($app) { $url = $this['url_generator'] ->generate($ruta, $params); $urlAbsoluta = $this['url_generator'] ->generate($ruta, $params, true); });
  167. Usando UrlGeneratorTrait $app->get('/', function () use ($app) { $url = $app->path($ruta, $params); $urlAbsoluta = $app->url($ruta, $params); });
  168. Sin usar TwigTrait $app->get('/', function () use ($app) { return new Response( $app['twig']->render('plantilla.twig', $params), 200, ['Cache-Control' => 'public, s-maxage=3600'] ); });
  169. Usando TwigTrait $app->get('/', function () use ($app) { return $app->render( 'plantilla.twig', $params, (new Response)->setTtl(3600) ); });
  170. RESPONSIBLE CONTROLLERS
  171. APIs con múltiples formatos $app->get('/api/...', function(Request $request) { $datos = ... if ('json' == $request->getRequestFormat()) { return new JsonResponse($datos); } elseif ('xml' == $request->getRequestFormat()) { return new Response( $datosEnXml, 200, ['Content-Type' => 'application/xml'] ); } });
  172. APIs con múltiples formatos $app->get('/api/...', function(Request $request) { $datos = ... return $datos; });
  173. Tobias Sjösten github.com/tobiassjosten/ ResponsibleServiceProvider ResponsibleServiceProvider Formateo automágico de la respuesta en función del formato de la petición (JSON o XML).
  174. ResponsibleServiceProvider $ composer require tobiassjosten/ResponsibleServiceProvider $app->register(new Tobiassjosten SilexResponsibleServiceProvider() );
  175. APIs con múltiples formatos $app->get('/api/...', function(Request $request) { $datos = ... return $datos; });
  176. 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']) ); } }
  177. 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) )); // ...
  178. 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) )); // ...
  179. 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) )); // ...
  180. 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) )); // ...
  181. CONFIGURACIÓN
  182. Configuración en Silex-Skeleton $app['db.options'] = array( 'dbname' => '...', 'user' => '...', 'password' => '...' ); $app['debug'] = false; config/prod.php config/dev.php require __DIR__.'/prod.php'; $app['db.options'] = array( 'dbname' => '...', 'user' => '...', 'password' => '...' ); $app['debug'] = true;
  183. Igor Wiedler github.com/igorw/ ConfigServiceProvider ConfigServiceProvider Añade un servicio de configuración a Silex con soporte para archivos YAML, JSON, PHP, TOML.
  184. igorw / ConfigServiceProvider $ composer require igorw/ConfigServiceProvider $app->register(new IgorwSilex ConfigServiceProvider("config.json") );
  185. igorw / ConfigServiceProvider $ composer require igorw/ConfigServiceProvider $app->register(new IgorwSilex ConfigServiceProvider("config.json") ); { "debug": true } $app['debug'] = true;
  186. "config.yml" "config.php" "config.toml" $app->register(new IgorwSilex ConfigServiceProvider("config.json") ); Múltiples formatos
  187. $app->register(new IgorwSilex ConfigServiceProvider("config.yml") ); $app->register(new IgorwSilex ConfigServiceProvider("params.php") ); $app->register(new IgorwSilex ConfigServiceProvider("dev.json") ); Múltiples archivos
  188. $app->register(new IgorwSilexConfigServiceProvider( __DIR__."/config/config.json", array('debug' => true, 'version' => '2') )); Configurar en ejecución config/config.json { "enable_cache": %debug%, "api_endpoint": "http://api.acme.org/%version%" }
  189. $app->register(new IgorwSilexConfigServiceProvider( __DIR__."/config/config.json", array('debug' => true, 'version' => '2') )); Configurar en ejecución config/config.json { "enable_cache": %debug%, "api_endpoint": "http://api.acme.org/%version%" }
  190. $app->register(new IgorwSilexConfigServiceProvider( __DIR__."/config/config.json", array('debug' => true, 'version' => '2') )); Configurar en ejecución config/config.json { "enable_cache": %debug%, "api_endpoint": "http://api.acme.org/%version%" }
  191. 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'); }
  192. $env = getenv('APP_ENV') ?: 'prod'; $app->register(new IgorwSilexConfigServiceProvider( __DIR__."/../config/$env.json" )); Configuración por entorno (Silex) app.php httpd-vhosts.conf <VirtualHost *:80> ServerName acme.dev SetEnv APP_ENV dev ... </VirtualHost>
  193. EXCEPCIONES
  194. Excepciones clásicas en Silex $app->error(function (Exception $e, $code) use ($app) { if ($app['debug']) { return; } $page = 404 == $code ? '404.html' : '500.html'; return new Response( $app['twig']->render($page, array('code' => $code)), $code ); });
  195. Excepciones clásicas en Silex $app->error(function (Exception $e, $code) use ($app) { if ($app['debug']) { return; } $page = 404 == $code ? '404.html' : '500.html'; return new Response( $app['twig']->render($page, array('code' => $code)), $code ); });
  196. INBOX Starred Drafts All mail Spam
  197. INBOX Starred Drafts All mail Spam 1
  198. INBOX Starred Drafts All mail Spam 12
  199. Google y los códigos de estado 50X 40X 20X
  200. Excepciones que devuelven 404 $app->error(function (Exception $e, $code) use ($app) { if ($app['debug']) { return; } $page = 404 == $code ? '404.html' : '500.html'; return new Response( $app['twig']->render($page, array('code' => $code)), 404 ); });
  201. APLICACIONES DESACOPLADAS
  202. APLICACIÓN PHP SILEX SILEX TU APLICACIÓN Aplicaciones acopladas dependes totalmente de Silex
  203. SILEX TU APLICACIÓN SILEX APLICACIÓN PHP Aplicaciones acopladas dependes totalmente de Silex aplicación PHP desacoplada de Silex
  204. Controladores Silex clásicos $app->get('/', function() use($app) { // ... return $app['twig']->render('portada.twig'); });
  205. 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'), } } }
  206. 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'), } } }
  207. 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'), } } }
  208. 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'), } } }
  209. 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'), } } }
  210. 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'), } } }
  211. 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'), } }
  212. 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'), } }
  213. Igor Wiedler github.com/igorw/doucheswag Dave Marshall speakerdeck.com/igorw/ silex-an-implementation-detail
  214. TU APLICACIÓN SILEX SILEX TU APLICACIÓN PHP Aplicaciones desacopladas
  215. SILEX TU APLICACIÓN PHP Aplicaciones desacopladas TU APLICACIÓN SILEX desacoplada de Silex interfaz entre tu aplicación y Silex
  216. Controlador clásico $app->get('/auction/{id}', function() use($app) { $datos = $this->app['db']->fetchAssoc(...); return $app['twig']->render('auction_view.twig'); });
  217. Controlador desacoplado $app->get('/auction/{id}', 'interactor.auction_view') ->value('controller', 'auction_view') ->convert('request', function ($_, Request $request) { $id = $request->attributes->get('id') return new AuctionViewRequest($id); });
  218. Controlador desacoplado $app->get('/auction/{id}', 'interactor.auction_view') ->value('controller', 'auction_view') ->convert('request', function ($_, Request $request) { $id = $request->attributes->get('id') return new AuctionViewRequest($id); }); 1 2 3
  219. 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; }));
  220. 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]; } }
  221. 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) { ... } }
  222. 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
  223. 1. AuctionView class AuctionView { public function __construct(AuctionRepository $repo) { // ... } public function __invoke(AuctionViewRequest $request) { // ... } }
  224. 1. AuctionView class AuctionView { public function __construct(AuctionRepository $repo) { // ... } public function __invoke(AuctionViewRequest $request) { // ... } } método mágico: objeto()
  225. 1. AuctionView class AuctionView { // ... public function __invoke(AuctionViewRequest $request) { $auction = $this->repo->find($request->id); $view = AuctionViewDto::fromEntity($auction); return new AuctionViewResponse($view); } }
  226. 1. AuctionView class AuctionView { // ... public function __invoke(AuctionViewRequest $request) { $auction = $this->repo->find($request->id); $view = AuctionViewDto::fromEntity($auction); return new AuctionViewResponse($view); } } Request()
  227. 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
  228. 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
  229. Desacoplando aplicaciones TU APLICACIÓN PHP AuctionViewResponse AuctionViewRequestAuctionViewRequest
  230. Controlador desacoplado $app->get('/auction/{id}', 'interactor.auction_view') ->value('controller', 'auction_view') ->convert('request', function ($_, Request $request) { $id = $request->attributes->get('id') return new AuctionViewRequest($id); }); 1 2 3
  231. 2. Usando mis propios objetos $app->... ->convert('request', function ($_, Request $request) { return new AuctionViewRequest( $request->attributes->get('id') ); });
  232. 2. Usando mis propios objetos $app->... ->convert('request', function ($_, Request $request) { return new AuctionViewRequest( $request->attributes->get('id') ); }); Silex
  233. 2. Usando mis propios objetos $app->... ->convert('request', function ($_, Request $request) { return new AuctionViewRequest( $request->attributes->get('id') ); }); Silex Mi aplicación PHP
  234. 2. AuctionViewRequest class AuctionViewRequest { public $id; public function __construct($id) { $this->id = $id; } }
  235. Controlador desacoplado $app->get('/auction/{id}', 'interactor.auction_view') ->value('controller', 'auction_view') ->convert('request', function ($_, Request $request) { $id = $request->attributes->get('id') return new AuctionViewRequest($id); }); 1 2 3
  236. 3. Generando la respuesta $app->... ->value('controller', 'auction_view')
  237. 3. Generando la respuesta $app->... ->value('controller', 'auction_view') ¿?¿?¿?
  238. 3. Generando la respuesta $app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, $app) { $dispatcher->addListener(KernelEvents::VIEW, function ($event) use ($app) { // ... $view = $event->getControllerResult(); $controller = $request->attributes->get('controller'); $template = "$controller.html"; $view = (object) $view; $body = $app['mustache']->render($template, $view); $response = new Response($body); $event->setResponse($response); }); return $dispatcher; }));
  239. 3. Generando la respuesta $app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, $app) { $dispatcher->addListener(KernelEvents::VIEW, function ($event) use ($app) { // ... $view = $event->getControllerResult(); $controller = $request->attributes->get('controller'); $template = "$controller.html"; $view = (object) $view; $body = $app['mustache']->render($template, $view); $response = new Response($body); $event->setResponse($response); }); return $dispatcher; }));
  240. 3. Generando la respuesta $app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, $app) { $dispatcher->addListener(KernelEvents::VIEW, function ($event) use ($app) { // ... $view = $event->getControllerResult(); $controller = $request->attributes->get('controller'); $template = "$controller.html"; $view = (object) $view; $body = $app['mustache']->render($template, $view); $response = new Response($body); $event->setResponse($response); }); return $dispatcher; }));
  241. 3. Generando la respuesta $app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, $app) { $dispatcher->addListener(KernelEvents::VIEW, function ($event) use ($app) { // ... $view = $event->getControllerResult(); $controller = $request->attributes->get('controller'); $template = "$controller.html"; $view = (object) $view; $body = $app['mustache']->render($template, $view); $response = new Response($body); $event->setResponse($response); }); return $dispatcher; }));
  242. 3. Generando la respuesta $app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, $app) { $dispatcher->addListener(KernelEvents::VIEW, function ($event) use ($app) { // ... $view = $event->getControllerResult(); $controller = $request->attributes->get('controller'); $template = "$controller.html"; $view = (object) $view; $body = $app['mustache']->render($template, $view); $response = new Response($body); $event->setResponse($response); }); return $dispatcher; }));
  243. Una visión general src Douche DoucheWeb
  244. Una visión general src Douche DoucheWeb Silex PHP
  245. Una visión general DoucheWeb app.php views ControllerResolver.php ServiceProvider.php
  246. Una visión general Douche Entity Exception Interactor Repository Service Storage Value View
  247. SINGLE-FILE APPLICATIONS
  248. Inline templates (Ruby / Sinatra) require 'sinatra' get '/' do haml :index end __END__ @@ layout %html = yield @@ index %div.title Hello world.
  249. 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
  250. 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
  251. Justin Hileman github.com/bobthecow/ mustache-silex-provider mustache / silex-provider Permite utilizar plantillas Mustache en Silex, incluyendo soporte para las inline templates.
  252. __halt_compiler()
  253. __halt_compiler() en PHAR <?php /* This file is part of the Silex framework. (c) Fabien Potencier <fabien@symfony.com> */ Phar::mapPhar('silex.phar'); require_once 'phar://silex.phar/vendor/autoload.php'; // ... __halt_compiler(); ?> 'j silex.phar&src/Silex/ExceptionListenerWrapper.php. 콩Q.ÜŽÅ ¶src/Silex/HttpCache.php¡ 콩Q¡•¯2—³¶!src/Silex/Route/SecurityTrait.php: 콩Q:ŽsµÌ¶ src/Silex/ControllerResolver.php 콩Q Ä–±2¶src/Silex/WebTestCase.php» 콩Q»ÝW¹¶src/Silex/Controller.php8 콩Q8 código PHP que se ejecuta datos de la aplicación
  254. Inline templates (PHP / Silex) $app->get('...', function() use ($app) { // ... }); __halt_compiler(); @@ portada <ul> {{#secciones}}<li> <a href="{{ enlace }}">{{ titulo }}</a> </li>{{/secciones}} </ul> @@ categoria <h1>{{ titulo }}</h1> controladores plantilla portada plantilla categoria
  255. 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__ ) ));
  256. 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
  257. 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()
  258. Single-File Application $app->get('/', function() use ($app) { return $app['mustache']->render('portada', array(‘secciones’ => array(...))); }); $app->get('/{slug}', function($slug) use ($app) { return $app['mustache']->render('categoria', array(‘titulo’ => $slug)); }); __halt_compiler(); @@ portada <ul> {{#secciones}} <li><a href="{{ enlace }}">{{ titulo }}</a></li> {{/secciones}} </ul> @@ categoria <h1>{{ titulo }}</h1>
  259. Single-File Application $app->get('/', function() use ($app) { return $app['mustache']->render('portada', array(‘secciones’ => array(...))); }); $app->get('/{slug}', function($slug) use ($app) { return $app['mustache']->render('categoria', array(‘titulo’ => $slug)); }); __halt_compiler(); @@ portada <ul> {{#secciones}} <li><a href="{{ enlace }}">{{ titulo }}</a></li> {{/secciones}} </ul> @@ categoria <h1>{{ titulo }}</h1>
  260. Mustache_Loader_InlineLoader class Mustache_Loader_InlineLoaderimplements Mustache_Loader { // ... protected function loadTemplates() { $this->templates = array(); $data = file_get_contents($this->fileName, false, null, $this->offset); foreach (preg_split("/^@@(?= [wd.]+$)/m", $data, -1) as $chunk) { if (trim($chunk)) { list($name, $content) = explode("n", $chunk, 2); $this->templates[trim($name)] = trim($content); } } } }
  261. Referencias
  262. Gonzalo Ayuso Fran Moreno Matthias Noback gonzalo123.com/ tag/silex/ showmethecode.es/ category/php/silex/ php-and-symfony .matthiasnoback.nl/ category/silex/
  263. GRACIAS.
  264. PREGUNTAS puedes valorar esta charla en joind.in/talk/view/8851
  265. Contacto Javier Eguiluz javiereguiluz.com twitter.com/javiereguiluz github.com/javiereguiluz linkedin.com/in/javiereguiluz
Advertisement