NachoMartín
Microframework SilexNacho Martín
JORNADAS SYMFONY 2011 1-3 JULIO, CASTELLÓN DESYMFONY.COM
deSymfony 2011
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
BACKEND215 ms
FRONTEND
-5 seg imágenes -1 seg CSS -2 seg JavaScript
8.000 ms
no te pierdas esta charla
RicardClau
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?
¿los miráis alguna vez?
Una aplicación Silex típicarequire_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$app->register( ... );
$app->get('/', function() use($app) { return $app['twig']->render('portada.twig'),});
$app->run();
Una aplicación Silex típicarequire_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$app->register( ... );
$app->get('/', function() use($app) { return $app['twig']->render('portada.twig'),});
$app->run();
1
2
3
4
5
Mejorando el rendimientorequire_once __DIR__.'/../vendor/autoload.php';1
$ composer install --optimize-autoloader
Mejorando el rendimientorequire_once __DIR__.'/../vendor/autoload.php';1
$ composer install --optimize-autoloader
$ composer dump-autoload --optimize
Mejorando el rendimientorequire_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 rendimientorequire_once __DIR__.'/../vendor/autoload.php';1
// autoload_classmap.php generated by Composer
$vendorDir = dirname(dirname(__FILE__));$baseDir = dirname($vendorDir);
return array( 'Doctrine\\DBAL\\Cache\\ArrayStatement' => $vendorDir . '/doctrine/dbal/ ... /ArrayStatement.php', 'Doctrine\\DBAL\\Cache\\CacheException' => $vendorDir . '/doctrine/dbal/ ... /CacheException.php', // ... 'Monolog\\Formatter\\ChromePHPFormatter' => $vendorDir . '/monolog/... /ChromePHPFormatter.php', 'Monolog\\Formatter\\FormatterInterface' => $vendorDir . '/monolog/ ... /FormatterInterface.php', // ... 'Pimple' => $vendorDir . '/pimple/pimple/lib/Pimple.php', 'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php',
vendor/composer/autoload_classmap.php optimizado
1.762 elementos
Revisa tu composer.jsonrequire_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.jsonrequire_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ípicarequire_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$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 Silex\Application(); 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 Silex\Application(); 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
Configurando la aplicación$app = new Silex\Application();
$app = new Silex\Application([ 'request.http_port' => 80]);
Configurando la aplicación$app = new Silex\Application();
$app = new Silex\Application([ 'request.http_port' => 80]);
$app = new Silex\Application([ 'request.http_port' => 80, 'request.https_port' => 443, 'debug' => false, 'charset' => 'UTF-8', 'locale' => 'en',]);
Configurando la aplicación$app = new Silex\Application();
$app = new Silex\Application([ 'request.http_port' => 80, 'mi_app.mi_opcion_1' => '...', 'mi_app.mi_opcion_2' => '...',]);
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 Silex\Provider\DoctrineServiceProvider(), [ '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
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 resumen1. Se carga un array de ~1.500 elementos.
2. Se crea un objeto Silex\Application.
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
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 malPHP_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 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
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
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 Silex\Provider\ServiceControllerServiceProvider()); 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
penaliza mucho el rendimiento
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 Silex\Provider\SecurityServiceProvider());// ... 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 Silex\Provider\SecurityServiceProvider());// ... 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 Silex\Provider\SecurityServiceProvider());// ... 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 Silex\Provider\SecurityServiceProvider());// ... 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 Silex\Provider\SecurityServiceProvider());// ... 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 Silex\Provider\SecurityServiceProvider());// ... 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 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 middlewaresrequire_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$app->register( ... );
$app->get('/', function() use($app) { return $app['twig']->render('portada.twig'),});
$app->run();
Mismo ejemplo sin middlewaresrequire_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$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 middlewaresrequire_once __DIR__.'/../vendor/autoload.php'; $app = new Silex\Application();
$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(...);});
Anatomía de un ServiceProvideruse Silex\ServiceProviderInterface;
class XXXServiceProvider implements 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 ServiceProvideruse Silex\ServiceProviderInterface;
class XXXServiceProvider implements 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 ServiceProvideruse Silex\ServiceProviderInterface;
class XXXServiceProvider implements 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 serviciosclass 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 serviciosclass ServiceControllerServiceProvider implements ServiceProviderInterface{ public function register(Application $app) { $app['resolver'] = $app->share($app->extend('resolver', function ($resolver, $app) { return new ServiceControllerResolver($resolver, $app); })); }
public function boot(Application $app) { // ... }}
Silex 1.0
public function before($callback, $priority = 0){ $this['dispatcher']->addListener( KernelEvents::REQUEST, function (GetResponseEvent $event) use ($callback) { // ...}
Los métodos on(), before(), after(), error() fuerzan la creación del dispatcher y todas sus dependencias.
Lazy dispatcher
public function before($callback, $priority = 0){ $this['dispatcher'] = $this->share($this->extend('dispatcher', function ($dispatcher, $app) use ($callback, $priority) { $dispatcher->addListener( KernelEvents::REQUEST, $callback, $priority ); return $dispatcher; }));}
En Silex 1.1, los métodos on(), before(), after(), error() crean el dispatcher en el último momento.
No instales Silex así
composer.json has been updatedLoading composer repositories with package informationUpdating 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 fileGenerating autoload files
$ cd mi_proyecto$ composer require silex/silex 1.0
MICRO DOES NOT MEAN LESS CODE. MICRO MEANS LESS STRUCTURE AND LESS DECISIONS MADE FOR YOU.FABIEN POTENCIERCREADOR 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)
Silex Kitchen Sink Edition
+ + + AsseticDoctrine
Silex HTML5 Boilerplate
Twitter Bootstrap
$ composer create-project lyrixx/Silex-Kitchen-Edition mi_proyecto 1.0
Controladores frontales
index.php index_dev.php
Para las aplicaciones complejas usamos seis entornos de ejecución:
DEV PROD TEST BETA STAGING ROLLOUT
Fuente: http://37signals.com/svn/posts/3535-beyond-the-default-rails-environments
37signals
TRAITS IS JUST COMPILER-ASSISTED COPY-AND-PASTE
STEFAN MARRSOFTWARE LANGUAGES LAB / VRIJE UNIVERSITEIT
Traits en PHPtrait metodosComunes { function metodo1() { ... } function metodo2() { ... }}
class miClase { use metodosComunes; // ...}
Traits en PHPtrait metodosComunes { function metodo1() { ... } function metodo2() { ... }}
class miClase { use metodosComunes; // ...}
$objeto = new MiClase();$objeto->metodo1();$objeto->metodo2();
Traits en PHPtrait metodosComunes { function metodo1() { ... } function metodo2() { ... }}
class miClase { use metodosComunes; // ...}
$objeto = new MiClase();$objeto->metodo1();$objeto->metodo2();
PHP 5.4o superior
FormTraitMonologTrait
SecurityTrait
SwiftmailerTrait
TranslationTrait
TwigTraitUrlGeneratorTrait
traits
Traits en aplicaciones Silexrequire_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$app->register( ... );$app->get(...);
$app->run();
Traits en aplicaciones Silexrequire_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$app->register( ... );$app->get(...);
$app->run();
class Application extends Silex\Application { use Silex\Application\TwigTrait; use Silex\Application\UrlGeneratorTrait;}
$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) );});
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
ResponsibleServiceProviderFormateo 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\ Silex\ResponsibleServiceProvider());
APIs con múltiples formatos$app->get('/api/...', function(Request $request) { $datos = ...
return $datos;});
ResponsibleServiceProviderclass 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']) ); }}
ResponsibleListenerclass 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) ));
// ...
ResponsibleListenerclass 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) ));
// ...
ResponsibleListenerclass 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) ));
// ...
ResponsibleListenerclass 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 en Silex-Skeleton
$app['db.options'] = array( 'dbname' => '...', 'user' => '...', 'password' => '...');$app['debug'] = false;
config/prod.php
config/dev.phprequire __DIR__.'/prod.php';
$app['db.options'] = array( 'dbname' => '...', 'user' => '...', 'password' => '...');$app['debug'] = true;
Igor Wiedler
github.com/igorw/ConfigServiceProvider
ConfigServiceProviderAñ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 Igorw\Silex\ ConfigServiceProvider("config.json"));
igorw / ConfigServiceProvider
$ composer require igorw/ConfigServiceProvider
$app->register(new Igorw\Silex\ ConfigServiceProvider("config.json"));
{ "debug": true}
$app['debug'] = true;
"config.yml""config.php""config.toml"
$app->register(new Igorw\Silex\ ConfigServiceProvider("config.json"));
Múltiples formatos
$app->register(new Igorw\Silex\
ConfigServiceProvider("config.yml"));$app->register(new Igorw\Silex\
ConfigServiceProvider("params.php"));$app->register(new Igorw\Silex\
ConfigServiceProvider("dev.json"));
Múltiples archivos
$app->register(new Igorw\Silex\ConfigServiceProvider( __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 Igorw\Silex\ConfigServiceProvider( __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 Igorw\Silex\ConfigServiceProvider( __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 Igorw\Silex\ConfigServiceProvider( __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 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 );});
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 );});
SILEX
TU APLICACIÓN
SILEX
APLICACIÓN PHP
Aplicaciones acopladasdependes 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('/', 'Acme\Controladores::index');
use Silex\Application;use Symfony\Component\HttpFoundation\Request; namespace Acme { class Controladores { public function index(Application $app, Request $request) { // ... return $app['twig']->render('portada.twig'), } }}
Controladores Silex en clases$app->get('/', 'Acme\Controladores::index');
use Silex\Application;use Symfony\Component\HttpFoundation\Request; namespace Acme { class Controladores { public function index(Application $app, Request $request) { // ... return $app['twig']->render('portada.twig'), } }}
Controladores Silex en clases$app->get('/', 'Acme\Controladores::index');
use Silex\Application;use Symfony\Component\HttpFoundation\Request; 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 Acme\Controladores;
$app['controladores'] = $app->share(function(Request $request) use ($app) { return new Controladores($app, $request);});
use Silex\Application;use Symfony\Component\HttpFoundation\Request; 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 Acme\Controladores;
$app['controladores'] = $app->share(function(Request $request) use ($app) { return new Controladores($app, $request);});
use Silex\Application;use Symfony\Component\HttpFoundation\Request; 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 Acme\Controladores;
$app['controladores'] = $app->share(function(Request $request) use ($app) { return new Controladores($app, $request);});
use Silex\Application;use Symfony\Component\HttpFoundation\Request; 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'), }}
IgorWiedler
github.com/igorw/doucheswag
DaveMarshall
speakerdeck.com/igorw/silex-an-implementation-detail
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 propioclass 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 controladoresclass 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 controladoresclass 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. AuctionViewclass AuctionView{ public function __construct(AuctionRepository $repo) { // ... }
public function __invoke(AuctionViewRequest $request) { // ... }}
1. AuctionViewclass AuctionView{ public function __construct(AuctionRepository $repo) { // ... }
public function __invoke(AuctionViewRequest $request) { // ... }}
método mágico:objeto()
1. AuctionViewclass AuctionView{ // ...
public function __invoke(AuctionViewRequest $request) { $auction = $this->repo->find($request->id);
$view = AuctionViewDto::fromEntity($auction);
return new AuctionViewResponse($view); }}
1. AuctionViewclass AuctionView{ // ...
public function __invoke(AuctionViewRequest $request) { $auction = $this->repo->find($request->id);
$view = AuctionViewDto::fromEntity($auction);
return new AuctionViewResponse($view); }}
Request()
1. AuctionViewclass AuctionView{ // ...
public function __invoke(AuctionViewRequest $request) { $auction = $this->repo->find($request->id);
$view = AuctionViewDto::fromEntity($auction);
return new AuctionViewResponse($view); }}
Request()
Doctrine
1. AuctionViewclass 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. AuctionViewRequestclass 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['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;}));
Inline templates (Ruby / Sinatra)require 'sinatra'
get '/' do haml :indexend
__END__
@@ layout%html = yield
@@ index%div.title Hello world.
Inline templates (Ruby / Sinatra)require 'sinatra'
get '/' do haml :indexend
__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 :indexend
__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-providerPermite utilizar plantillas Mustache en Silex, incluyendo soporte para las inline templates.
__halt_compiler() en PHAR<?php/* This file is part of the Silex framework. (c) Fabien Potencier <[email protected]> */
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
plantillaportada
plantillacategoria
mustache / silex-provider
$ composer require mustache/silex-provider 1.0
$app->register(
new \Mustache\Silex\Provider\MustacheServiceProvider, array(
'mustache.loader' => new Mustache_Loader_InlineLoader(
__FILE__, __COMPILER_HALT_OFFSET__ )));
mustache / silex-provider
$ composer require mustache/silex-provider 1.0
$app->register(
new \Mustache\Silex\Provider\MustacheServiceProvider, 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 \Mustache\Silex\Provider\MustacheServiceProvider, 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_InlineLoaderclass Mustache_Loader_InlineLoader implements Mustache_Loader
{ // ...
protected function loadTemplates() { $this->templates = array(); $data = file_get_contents($this->fileName, false, null, $this->offset); foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) { if (trim($chunk)) { list($name, $content) = explode("\n", $chunk, 2); $this->templates[trim($name)] = trim($content); } } }}
GonzaloAyuso
FranMoreno
MatthiasNoback
gonzalo123.com/tag/silex/
showmethecode.es/category/php/silex/
php-and-symfony.matthiasnoback.nl/category/silex/