SlideShare a Scribd company logo
1 of 265
Download to read offline
20-22 junio 2013
Madrid
SILEX AL LÍMITE
Javier Eguiluz
deSymfony
¡muchas gracias a nuestros patrocinadores!
deSymfony
ME PRESENTO
Sobre mí
Javier Eguiluz
Formador y programador
entusiasta de Symfony.
javiereguiluz.com
2011
Nacho
Martín
Microframework Silex
Nacho Martín
JORNADAS SYMFONY 2011 1-3 JULIO, CASTELLÓN DESYMFONY.COM
deSymfony 2011
AGENDA
RENDIMIENTO
AL LÍMITE
CARACTERÍSTICAS
AL LÍMITE
RENDIMIENTO
AL LÍMITE
CARACTERÍSTICAS
AL LÍMITE
RENDIMIENTO
AL LÍMITE
CARACTERÍSTICAS
AL LÍMITE
PRIMERA PARTE
RENDIMIENTO
AL LÍMITE
PASOS PREVIOS
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
no te pierdas
esta charla
Miquel
Company
2. Tu aplicación «casi» no importa
TU APLICACIÓN
S.O.
BBDD SERVIDOR WEB
PHPAPC
VARNISH
2. Tu aplicación «casi» no importa
TU APLICACIÓN
S.O.
BBDD SERVIDOR WEB
PHPAPC
VARNISH
TU APLICACIÓN
no te pierdas
esta charla
Ricard
Clau
y tampoco te pierdas su
blog: ricardclau.com
3. Nada importa si no lo miras
MySQL Slow
Query Log
APC log
¿cada cuánto
miráis estos
archivos?
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?
APC log
APC log
HttpCacheESI.php
APC log
APC log
'http_cache.esi' => null
Cristina
Quintana
no te pierdas
esta charla
SILEX
INTERNALS
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();
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
Mejorando el rendimiento
require_once __DIR__.'/../vendor/autoload.php';1
Mejorando el rendimiento
require_once __DIR__.'/../vendor/autoload.php';1
$ composer install --optimize-autoloader
Mejorando el rendimiento
require_once __DIR__.'/../vendor/autoload.php';1
$ composer install --optimize-autoloader
$ composer dump-autoload --optimize
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
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
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",
...
}
}
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?
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
✔
Mejorando el rendimiento
$app = new SilexApplication();2
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'] = ...
}
}
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
mini truco
Configurando la aplicación
$app = new SilexApplication();
$app = new SilexApplication([
'request.http_port' => 80
]);
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',
]);
Configurando la aplicación
$app = new SilexApplication();
$app = new SilexApplication([
'request.http_port' => 80,
'mi_app.mi_opcion_1' => '...',
'mi_app.mi_opcion_2' => '...',
]);
Mejorando el rendimiento
$app->register( ... );3
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;
}
}
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;
}
}
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;
}
}
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
Mejorando el rendimiento
$app->get('/', function() use($app) {
return $app['twig']->render('portada.twig'),
});
4
Mejorando el rendimiento
$app->run();5
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
mini truco
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
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
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
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);
// ...
TIPOS DE
RESPUESTAS
Tipos de respuestas Silex / Sf2
Response
RedirectResponse
StreamedResponse
JsonResponse
BinaryFileResponse
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
);
Tipos de respuestas Silex / Sf2
Response
RedirectResponse
StreamedResponse
JsonResponse
BinaryFileResponse
Response vs. BinaryFileResponse
$app->get('/', function () use ($app) {
return new Response(
file_get_contents($archivo)
);
});
Response vs. BinaryFileResponse
$app->get('/', function () use ($app) {
return new Response(
file_get_contents($archivo)
);
});
Rendimiento
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);
});
Response vs. BinaryFileResponse
X-Sendfile-Type
X-Accel-Redirect
X-Accel-Mapping
Response vs. BinaryFileResponse
APLICACIÓN
APACHE / NGINX
KERNEL
X-Sendfile-Type
X-Accel-Redirect
X-Accel-Mapping
SILEX
Response vs. BinaryFileResponse
APLICACIÓN
APACHE / NGINX
KERNELsendfile()
X-Sendfile-Type
X-Accel-Redirect
X-Accel-Mapping
SILEX
REUTILIZANDO
APLICACIONES
usuarios
web/
index.php
/...
/noticias
/
Una aplicación Silex típica
usuarios
web/
index.phpadministrador
/...
/noticias
/
Una aplicación Silex típica
usuarios
web/
index.phpadministrador
/...
/noticias
/
Una aplicación Silex típica
/admin
/admin/...
/...
/noticias
/
Mezclando frontend y backend
$app->get('/admin', function() { ... });
$app->get('/admin/noticias', function() { ... });
$app->get('/noticias', function() { ... });
$app->get('/', function() { ... });
Mezclando frontend y backend
$app->get('/admin', function() { ... });
$app->get('/admin/noticias', function() { ... });
$app->get('/noticias', function() { ... });
$app->get('/', function() { ... });
¡horrible!
Mezclando frontend y backend
$app->mount(
'/admin', new AdminControllerProvider()
);
$app->get('/noticias', function() { ... });
$app->get('/', function() { ... });
Mezclando frontend y backend
$app->mount(
'/admin', new AdminControllerProvider()
);
$app->get('/noticias', function() { ... });
$app->get('/', function() { ... });
$app->register(
new SilexProviderServiceControllerServiceProvider()
);
penaliza el
rendimiento
Mezclando frontend y backend
$app->mount(
'/admin', new AdminControllerProvider()
);
$app->get('/noticias', function() { ... });
$app->get('/', function() { ... });
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) { ... });
}
}
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) { ... });
}
}
BACKEND
usuarios administrador
FRONTEND
Seguridad obligatoria en frontend
BACKEND
usuarios administrador
FRONTEND
Seguridad obligatoria en frontend
BACKEND
usuarios administrador
FRONTEND
Seguridad obligatoria en frontend
BACKEND
usuarios administrador
FRONTEND
Seguridad obligatoria en frontend
penaliza mucho
el rendimiento
1 proyecto
2 aplicaciones
N módulos
frontend y backend
1 proyecto
1 aplicación
N bundles
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
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
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
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;
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;
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;
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;
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;
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;
/admin
/admin/...
/...
/noticias
/
usuarios
administrador
web/index.php
/...
/noticias
/
Aplicaciones Silex compartidas
web/backend.php
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.
MIDDLEWARES
Middlewares de aplicación
$app->before(function ($request) {
// ...
});
$app->after(function ($request, $response) {
// ...
});
$app->finish(function ($request, $response) {
// ...
});
Middlewares de controlador
$before = function ($request) use ($app) {
// ...
};
$after = function ($request, $response) use ($app) {
// ...
};
$app->get('...', function () {
// ...
})
->before($before)
->after($after);
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
);
}
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);
}
Middlewares «obligatorios»
$app->before(function() use ($app) {
if (!$app['security']->isGranted('ROLE_ADMIN')) {
return $app->redirect('/');
}
});
Middlewares «prescindibles»
$app->before(function(Request $request) {
$request->query->set('token', '...');
$request->server->set('SERVER_ADDR', '::1');
});
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();
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();
}
// ...
}
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);
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(...);
});
Mismo ejemplo «sin middlewares»
$app->finish(function(Request $request) use ($app) {
$elapsed = 1000 * (
microtime(true) - $request->server->get('REQUEST_TIME_FLOAT')
);
$app['monolog']->addDebug(...);
});
SERVICE
PROVIDERS
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) { // ... });
}
}
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
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
Servicio básico
$app['logger'] = function () {
return new Logger();
};
se crea una nueva
instancia cada vez
que lo usas
Servicio compartido
$app['logger'] = $app->share(function () {
return new Logger();
});
se reutiliza la
misma instancia
una y otra vez
Extendiendo servicios
$app['logger'] = function () {
return new Logger();
};
$app['logger'] = function () {
$logger = new Logger();
$logger->metodoXXX();
return $logger;
};
lo que tengo
lo que quiero
Extendiendo servicios
$app['logger'] = function () {
return new Logger();
};
$app['logger'] = $app->extend('logger', function($logger, $app) {
$logger->metodoXXX();
return $logger;
});
Extendiendo servicios
$app['logger'] = function () {
return new Logger();
};
$app['logger'] = $app->extend('logger', function($logger, $app) {
$logger->metodoXXX();
return $logger;
});
Extendiendo servicios
$app['logger'] = function () {
return new Logger();
};
$app['logger'] = $app->extend('logger', function($logger, $app) {
$logger->metodoXXX();
return $logger;
});
modifica este servicio
Extendiendo servicios
$app['logger'] = function () {
return new Logger();
};
$app['logger'] = $app->extend('logger', function($logger, $app) {
$logger->metodoXXX();
return $logger;
});
modifica este servicio
Extendiendo servicios
$app['logger'] = function () {
return new Logger();
};
$app['logger'] = $app->extend('logger', function($logger, $app) {
$logger->metodoXXX();
return $logger;
});
Extendiendo servicios
$app['logger'] = function () {
return new Logger();
};
$app['logger'] = $app->extend('logger', function($logger, $app) {
$logger->metodoXXX();
return $logger;
});
lo que devuelva
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;
}));
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;
}));
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.1
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.
SEGUNDA PARTE
CARACTERÍSTICAS
AL LÍMITE
INSTALACIÓN
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
silex/silex 1.0
silex/silex 1.0
¿controladores?¿configuración?
¿index.php?
MICRO DOES NOT MEAN
LESS CODE. MICRO
MEANS LESS STRUCTURE
AND LESS DECISIONS
MADE FOR YOU.
FABIEN POTENCIER
CREADOR DE SYMFONY, SILEX Y TWIG
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)
fabpot/Silex-Skeleton
fabpot/Silex-Skeleton
Controladores
frontales
fabpot/Silex-Skeleton
Tu código
fabpot/Silex-Skeleton
Plantillas
fabpot/Silex-Skeleton
Configuración
y registro de
servicios
fabpot/Silex-Skeleton
Igual que
Symfony2
Silex Kitchen Sink Edition
$ composer create-project lyrixx/Silex-Kitchen-Edition
mi_proyecto
1.0
Silex Kitchen Sink Edition
+ + +Assetic
Doctrine
Silex HTML5
Boilerplate
Twitter
Bootstrap
$ composer create-project lyrixx/Silex-Kitchen-Edition
mi_proyecto
1.0
mini truco
Controladores frontales
index.php index_dev.php
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
Controladores frontales
index.php index_dev.php
prod.php dev.php
Controladores frontales
index.php index_dev.php
prod.php dev.php beta.php staging.php
TRAITS
TRAITS IS JUST
COMPILER-ASSISTED
COPY-AND-PASTE
STEFAN MARR
SOFTWARE LANGUAGES LAB / VRIJE UNIVERSITEIT
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
FormTrait
MonologTrait
SecurityTrait
SwiftmailerTrait
TranslationTrait
TwigTrait
UrlGeneratorTrait
traits
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);
});
Usando UrlGeneratorTrait
$app->get('/', function () use ($app) {
$url = $app->path($ruta, $params);
$urlAbsoluta = $app->url($ruta, $params);
});
Sin usar TwigTrait
$app->get('/', function () use ($app) {
return new Response(
$app['twig']->render('plantilla.twig', $params),
200,
['Cache-Control' => 'public, s-maxage=3600']
);
});
Usando TwigTrait
$app->get('/', function () use ($app) {
return $app->render(
'plantilla.twig',
$params,
(new Response)->setTtl(3600)
);
});
RESPONSIBLE
CONTROLLERS
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']
);
}
});
APIs con múltiples formatos
$app->get('/api/...', function(Request $request) {
$datos = ...
return $datos;
});
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).
ResponsibleServiceProvider
$ composer require
tobiassjosten/ResponsibleServiceProvider
$app->register(new Tobiassjosten
SilexResponsibleServiceProvider()
);
APIs con múltiples formatos
$app->get('/api/...', function(Request $request) {
$datos = ...
return $datos;
});
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)
));
// ...
CONFIGURACIÓN
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;
Igor
Wiedler
github.com/igorw/
ConfigServiceProvider
ConfigServiceProvider
Añade un servicio de configuración
a Silex con soporte para archivos
YAML, JSON, PHP, TOML.
igorw / ConfigServiceProvider
$ composer require igorw/ConfigServiceProvider
$app->register(new IgorwSilex
ConfigServiceProvider("config.json")
);
igorw / ConfigServiceProvider
$ composer require igorw/ConfigServiceProvider
$app->register(new IgorwSilex
ConfigServiceProvider("config.json")
);
{
"debug": true
}
$app['debug'] = true;
"config.yml"
"config.php"
"config.toml"
$app->register(new IgorwSilex
ConfigServiceProvider("config.json")
);
Múltiples formatos
$app->register(new IgorwSilex
ConfigServiceProvider("config.yml")
);
$app->register(new IgorwSilex
ConfigServiceProvider("params.php")
);
$app->register(new IgorwSilex
ConfigServiceProvider("dev.json")
);
Múltiples archivos
$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%"
}
$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%"
}
$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%"
}
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');
}
$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>
EXCEPCIONES
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
);
});
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
);
});
INBOX
Starred
Drafts
All mail
Spam
INBOX
Starred
Drafts
All mail
Spam
1
INBOX
Starred
Drafts
All mail
Spam
12
Google y los códigos de estado
50X
40X
20X
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
);
});
APLICACIONES
DESACOPLADAS
APLICACIÓN PHP
SILEX
SILEX
TU APLICACIÓN
Aplicaciones acopladas
dependes totalmente
de Silex
SILEX
TU APLICACIÓN
SILEX
APLICACIÓN PHP
Aplicaciones acopladas
dependes totalmente
de Silex
aplicación PHP
desacoplada de Silex
Controladores Silex clásicos
$app->get('/', function() use($app) {
// ...
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 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'),
}
}
Igor
Wiedler
github.com/igorw/doucheswag
Dave
Marshall
speakerdeck.com/igorw/
silex-an-implementation-detail
TU APLICACIÓN SILEX
SILEX
TU APLICACIÓN PHP
Aplicaciones desacopladas
SILEX
TU APLICACIÓN PHP
Aplicaciones desacopladas
TU APLICACIÓN SILEX
desacoplada
de Silex
interfaz entre
tu aplicación
y Silex
Controlador clásico
$app->get('/auction/{id}', function() use($app) {
$datos = $this->app['db']->fetchAssoc(...);
return $app['twig']->render('auction_view.twig');
});
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);
});
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
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)
{
// ...
}
}
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
Desacoplando aplicaciones
TU APLICACIÓN
PHP
AuctionViewResponse
AuctionViewRequestAuctionViewRequest
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
2. Usando mis propios objetos
$app->...
->convert('request', function ($_, Request $request) {
return new AuctionViewRequest(
$request->attributes->get('id')
);
});
2. Usando mis propios objetos
$app->...
->convert('request', function ($_, Request $request) {
return new AuctionViewRequest(
$request->attributes->get('id')
);
});
Silex
2. Usando mis propios objetos
$app->...
->convert('request', function ($_, Request $request) {
return new AuctionViewRequest(
$request->attributes->get('id')
);
});
Silex
Mi aplicación PHP
2. AuctionViewRequest
class AuctionViewRequest
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
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
3. Generando la respuesta
$app->...
->value('controller', 'auction_view')
3. Generando la respuesta
$app->...
->value('controller', 'auction_view')
¿?¿?¿?
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;
}));
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;
}));
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;
}));
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;
}));
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;
}));
Una visión general
src
Douche
DoucheWeb
Una visión general
src
Douche
DoucheWeb
Silex
PHP
Una visión general
DoucheWeb
app.php
views
ControllerResolver.php
ServiceProvider.php
Una visión general
Douche
Entity Exception
Interactor Repository
Service Storage
Value View
SINGLE-FILE
APPLICATIONS
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
Justin
Hileman
github.com/bobthecow/
mustache-silex-provider
mustache / silex-provider
Permite utilizar plantillas Mustache
en Silex, incluyendo soporte para
las inline templates.
__halt_compiler()
__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
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
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()
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>
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>
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);
}
}
}
}
Referencias
Gonzalo
Ayuso
Fran
Moreno
Matthias
Noback
gonzalo123.com/
tag/silex/
showmethecode.es/
category/php/silex/
php-and-symfony
.matthiasnoback.nl/
category/silex/
GRACIAS.
PREGUNTAS
puedes valorar esta charla en
joind.in/talk/view/8851
Contacto
Javier Eguiluz
javiereguiluz.com
twitter.com/javiereguiluz
github.com/javiereguiluz
linkedin.com/in/javiereguiluz

More Related Content

What's hot

DeSymfony 2012: Symfony internals
DeSymfony 2012: Symfony internalsDeSymfony 2012: Symfony internals
DeSymfony 2012: Symfony internalsRaul Fraile
 
Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0zfconfua
 
Clase 7 el modelo
Clase 7  el modeloClase 7  el modelo
Clase 7 el modelohydras_cs
 
Adi Fatol - Ce e nou in PHP 5.3?
Adi Fatol - Ce e nou in PHP 5.3?Adi Fatol - Ce e nou in PHP 5.3?
Adi Fatol - Ce e nou in PHP 5.3?phpGeekMeet_ro
 
Introduction à Marionette
Introduction à MarionetteIntroduction à Marionette
Introduction à MarionetteRaphaël Lemaire
 
Drupal Cms Prezentace
Drupal Cms PrezentaceDrupal Cms Prezentace
Drupal Cms PrezentaceTomáš Kafka
 
Php & mysql
Php & mysqlPhp & mysql
Php & mysqlola98z
 
Migrating Legacy Web Applications to AngularJS
Migrating Legacy Web Applications to AngularJSMigrating Legacy Web Applications to AngularJS
Migrating Legacy Web Applications to AngularJSBTI360
 
Silex. Микрофреймворк для микроприложений
Silex. Микрофреймворк для микроприложенийSilex. Микрофреймворк для микроприложений
Silex. Микрофреймворк для микроприложенийSoftline
 
Javascript and jQuery for Mobile
Javascript and jQuery for MobileJavascript and jQuery for Mobile
Javascript and jQuery for MobileIvano Malavolta
 

What's hot (20)

Add tag shortcode
Add tag shortcodeAdd tag shortcode
Add tag shortcode
 
es6.concurrency()
es6.concurrency()es6.concurrency()
es6.concurrency()
 
jQuery PLUGIN
jQuery PLUGINjQuery PLUGIN
jQuery PLUGIN
 
DeSymfony 2012: Symfony internals
DeSymfony 2012: Symfony internalsDeSymfony 2012: Symfony internals
DeSymfony 2012: Symfony internals
 
Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0
 
Extjs 4
Extjs 4Extjs 4
Extjs 4
 
Clase 7 el modelo
Clase 7  el modeloClase 7  el modelo
Clase 7 el modelo
 
Adi Fatol - Ce e nou in PHP 5.3?
Adi Fatol - Ce e nou in PHP 5.3?Adi Fatol - Ce e nou in PHP 5.3?
Adi Fatol - Ce e nou in PHP 5.3?
 
Index2
Index2Index2
Index2
 
Introduction à Marionette
Introduction à MarionetteIntroduction à Marionette
Introduction à Marionette
 
Jquery Plugin
Jquery PluginJquery Plugin
Jquery Plugin
 
PHP Profiling
PHP ProfilingPHP Profiling
PHP Profiling
 
Drupal Cms Prezentace
Drupal Cms PrezentaceDrupal Cms Prezentace
Drupal Cms Prezentace
 
Php & mysql
Php & mysqlPhp & mysql
Php & mysql
 
JavaScript
JavaScriptJavaScript
JavaScript
 
Migrating Legacy Web Applications to AngularJS
Migrating Legacy Web Applications to AngularJSMigrating Legacy Web Applications to AngularJS
Migrating Legacy Web Applications to AngularJS
 
Silex. Микрофреймворк для микроприложений
Silex. Микрофреймворк для микроприложенийSilex. Микрофреймворк для микроприложений
Silex. Микрофреймворк для микроприложений
 
Javascript and jQuery for Mobile
Javascript and jQuery for MobileJavascript and jQuery for Mobile
Javascript and jQuery for Mobile
 
Wek14 mysql 2
Wek14 mysql 2Wek14 mysql 2
Wek14 mysql 2
 
Advanced JQuery
 Advanced JQuery Advanced JQuery
Advanced JQuery
 

More from Javier Eguiluz

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonyJavier Eguiluz
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)Javier Eguiluz
 
Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Javier Eguiluz
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricksJavier Eguiluz
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Javier Eguiluz
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPJavier Eguiluz
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Javier Eguiluz
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoJavier Eguiluz
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendJavier Eguiluz
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosJavier Eguiluz
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Javier Eguiluz
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas SymfonyJavier Eguiluz
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - AnexosJavier Eguiluz
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4Javier Eguiluz
 
Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3Javier Eguiluz
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2Javier Eguiluz
 

More from Javier Eguiluz (20)

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
 
Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8
 
Twig tips and tricks
Twig tips and tricksTwig tips and tricks
Twig tips and tricks
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHP
 
Wallpaper Notifier
Wallpaper NotifierWallpaper Notifier
Wallpaper Notifier
 
Backend (sf2Vigo)
Backend (sf2Vigo)Backend (sf2Vigo)
Backend (sf2Vigo)
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseño
 
Desymfony 2011 - Twig
Desymfony 2011 - TwigDesymfony 2011 - Twig
Desymfony 2011 - Twig
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: Backend
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas Symfony
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - Anexos
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2
 

Silex al límite

  • 1. 20-22 junio 2013 Madrid SILEX AL LÍMITE Javier Eguiluz deSymfony
  • 2. ¡muchas gracias a nuestros patrocinadores! deSymfony
  • 4. Sobre mí Javier Eguiluz Formador y programador entusiasta de Symfony. javiereguiluz.com
  • 6. Nacho Martín Microframework Silex Nacho Martín JORNADAS SYMFONY 2011 1-3 JULIO, CASTELLÓN DESYMFONY.COM deSymfony 2011
  • 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?
  • 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
  • 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' => '...', ]);
  • 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
  • 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
  • 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); // ...
  • 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); });
  • 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
  • 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) { ... }); } }
  • 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;
  • 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.
  • 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(...); });
  • 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) { // ... } }
  • 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.
  • 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
  • 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)
  • 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
  • 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
  • 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
  • 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) ); });
  • 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; });
  • 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) )); // ...
  • 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;
  • 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>
  • 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 ); });
  • 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 ); });
  • 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'), } }
  • 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
  • 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; }));
  • 246. Una visión general Douche Entity Exception Interactor Repository Service Storage Value View
  • 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.
  • 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); } } } }
  • 264. PREGUNTAS puedes valorar esta charla en joind.in/talk/view/8851