come portare il profiler di symfony2 in drupal8
DESCRIPTION
Molti progetti PHP open source hanno adottato Symfony2 come base per la loro prossima versione, tra questi c'è anche il CMS Drupal (http://drupal.org). In questo talk vedremo come scrivere un modulo per Drupal8 in modo da sfruttare il più possibile il suo nuovo motore Symfony2, dall'integrazione con il service container alla gestione degli eventi, dal routing a Twig. Verrà usato come esempio il modulo webprofiler (http://drupal.org/project/webprofiler) per dimostrare come un bundle per Symfony2 possa essere trasformato in un modulo per Drupal8 e integrato facilmente nel sistema.TRANSCRIPT
COME PORTARE IL PROFILER DI SYMFONY2 IN DRUPAL8Luca Lusso
Senior Software Architect and Drupal Expert - - [email protected] @lussoluca drupal.org/u/lussoluca
AGENDAIntoduzioneDal bundle al moduloAnatomia di un moduloRoutingService containerEventiTwig
INTRODUZIONE
SYMFONY2 ALLA BASE DI DIVERSI PRODOTTI:phpBBLaraveleZ publishPiwikDrupal8...
I’m not a Drupal developer, but I do know a lotabout Drupal 8. I know how the event system
works, what a service is, how it relates to adependency injection container and how the
deepest and darkest of Drupal’s request-response workflow looks.
How? Because I’m a Symfony developer. And if you
want to get a jumpstart on Drupal 8, you shouldbe to. I’m not saying use Symfony instead of
Drupal - they each solve very different problems.Use both.
Ryan Weaver - KnpLabs
COMPONENTI DI SYMFONY2 IN DRUPAL8Class loaderCss selectorDebugDependency injectionEvent dispatcherHTTP foundationHTTP kernelProcessRoutingSerializerTranslationValidatorYaml
ALTRI COMPONENTI INCLUSI IN DRUPAL8twig/twigdoctrine/commondoctrine/annotationsguzzlehttp/guzzlekriswallsmith/asseticsymfony-cmf/routingeasyrdf/easyrdfphpunit/phpunitphpunit/phpunit-mock-objectszendframework/zend-feedmikey179/vfsStreamstack/builderegulias/email-validator
STATO DEL PROGETTO8.0.x-beta1
Le API sono stabili anche se qualche dettaglio potrebbe ancoracambiare
www.drupal.org/project/drupal
WARNINGDrupal8 NON è un'applicazione full-stack Symfony, è un
software PHP che si basa su alcune componenti di Symfony pernon reinventare la ruota
Ad esempio non si può prendere un bundle di Symfony e"installarlo" su un sito Drupal8
DAL BUNDLE AL MODULO
WEBPROFILER BUNDLELa toolbar di debug e profiling inclusa nel full-stack Symfony è
contribuita da tre distinte componenti:
Le funzionalità sono fornite da classi nel namespaceSymfony\Component\HttpKernel\Profiler nel componenteHttpKernel (data collecting, storage, ...)L'interfaccia utente (toolbar e pagine di report) sono fornitedal bundle WebProfilerBundleIl tutto è configurato dal bundle FrameworkBundle (passi dicompilazione e registrazione dei data_collector)
MODULO WEBPROFILERIn Drupal non abbiamo i bundle ma i moduli:
Le funzionalità sono fornite da classi nel namespaceSymfony\Component\HttpKernel\Profiler nel componenteHttpKernel ==> big win!!L'interfaccia utente (toolbar e pagine di report) e laconfigurazione (passi di compilazione e registrazione deidata_collector) sono fornite dal modulo webprofiler
oppure
www.drupal.org/project/webprofiler
github.com/lussoluca/webprofiler
ANATOMIA DI UN MODULO
WEBPROFILER.INFO.YMLname: Web Profilertype: moduledescription: 'Drupal Web Profiler.'package: Developmentversion: 8.x-1.1-beta1core: 8.xconfigure: webprofiler.admin_configure
WEBPROFILER.ROUTING.YMLwebprofiler.toolbar: path: '/profiler/{profile}' defaults: _controller: '\Drupal\webprofiler\Controller\WebprofilerController::toolbarAction' options: parameters: profile: type: 'webprofiler:token' requirements: _permission: 'view webprofiler toolbar'
webprofiler.profiler: path: '/admin/reports/profiler/view/{profile}' defaults: _content: '\Drupal\webprofiler\Controller\WebprofilerController::profilerAction' _title: 'Webprofiler' options: parameters: profile: type: 'webprofiler:token' requirements: _permission: 'access webprofiler'
WEBPROFILER.SERVICES.YMLservices: logger.channel.webprofiler: class: Drupal\Core\Logger\LoggerChannel factory_method: get factory_service: logger.factory arguments: ['webprofiler']
profiler.file_storage: class: Symfony\Component\HttpKernel\Profiler\FileProfilerStorage arguments: ['%data_collector.storage%'] tags: - { name: webprofiler_storage, title:'File storage' }
profiler.database_storage: class: Drupal\webprofiler\Profiler\DatabaseProfilerStorage arguments: ['@database'] tags: - { name: webprofiler_storage, title:'Database storage' }
profiler.storage_manager: class: Drupal\webprofiler\Profiler\ProfilerStorageManager
profiler.storage: class: Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface factory_class: Drupal\webprofiler\Profiler\ProfilerStorageFactory factory_method: getProfilerStorage arguments: ['@config.factory', '@service_container']
profiler: class: Drupal\webprofiler\Profiler\Profiler
CARTELLA SRC (PSR/4)
CARTELLA TEMPLATES
FILE SPECIFICI DRUPAL8webprofiler.module -> codice procedurale (hook), nonobbligatoriowebprofiler.install -> codice eseguito all'installazione eaggiornamento del modulo (procedurale), non obbligatoriowebprofiler.permissions.yml -> permessi aggiunti dal modulo,non obbligatoriowebprofiler.links.menu.yml -> voci di menu aggiunte dalmodulo, non obbligatoriowebprofiler.links.task.yml -> task (una sorta di link contestuali)aggiunti dal modulo, non obbligatoriowebprofiler.libraries.yml -> librerie (insiemi di css e javascript)aggiunti dal modulo, non obbligatorio
ROUTINGMolto simile a Symfony_content -> ritorna un render array di Drupal, che verràtraformato in HTML e incluso nel "main content" di una pagina_controller -> ritorna il contenuto direttamente senza passaredal livello di theming di Drupal_form -> si aspetta una classe che implementaDrupal\Core\Form\FormInterface (usato nel caso in cui il"main content" sia una form)_entity[_view|_list|_form] -> per lavorare sulle entità (il modelin Drupal), ritorna un render array per il dettaglio, la lista o laform di inserimento/update di un'entità
https://www.drupal.org/node/2092643
ROUTINGGestisce il controllo di accesso alle risorse (_permission, _role,_access, _entity_access, ...)Gestisce la conversione dei parametri della url
webprofiler.toolbar: path: '/profiler/{profile}' defaults: _controller: '\Drupal\webprofiler\Controller\WebprofilerController::toolbarAction' options: parameters: profile: type: 'webprofiler:token' requirements: _permission: 'view webprofiler toolbar'
SERVICE CONTAINERPer registrare i vari data_collector ho bisogno di aggiungere un
passo di compilazione durante la costruzione del servicecontainer
SERVICE CONTAINERPer aggiungere un passo di compilazione in Symfony2 devo fare:
class FrameworkBundle extends Bundle{ public function build(ContainerBuilder $container) { parent::build($container);
$container->addCompilerPass(new ProfilerPass());
SERVICE CONTAINERPer aggiungere un passo di compilazione in Drupal8 devo fare:class WebprofilerServiceProvider extends ServiceProviderBase {
public function register(ContainerBuilder $container) { $container->addCompilerPass(new ProfilerPass());
L'implementazione della classe ProfilerPass è la stessa inentrambi i casi ==> big win!!
SERVICE CONTAINERPer poter profilare e analizzare un sotto-sistema di Drupal quello
che abbiamo fatto è stato sostituire una dato servizio con unanostra implementazione:
// Replaces the existing cache_factory service to be able to collect the// requested data.$container->setDefinition('cache_factory.default', $container->getDefinition('cache_factory'));
$container->register('cache_factory', 'Drupal\webprofiler\Cache\CacheFactoryWrapper') ->addArgument(new Reference('cache_factory.default')) ->addArgument(new Reference('webprofiler.cache')) ->addMethodCall('setContainer', array(new Reference('service_container')));
EVENTIwebprofiler.WebprofilerEventSubscriber: class: Drupal\webprofiler\EventSubscriber\WebprofilerEventSubscriber arguments: ['@current_user', '@url_generator'] tags: - { name: event_subscriber }
class WebprofilerEventSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents() { return array( KernelEvents::RESPONSE => array('onKernelResponse', -128), ); }
public function onKernelResponse(FilterResponseEvent $event) { $response = $event->getResponse(); $request = $event->getRequest();
Stesso approccio di Symfony
EVENTIDurante lo sviluppo di Drupal8 si pensava che gli eventi di
Symfony avrebbero rimpiazzato del tutto gli hook di Drupal,invece molti hook sono stati mantenuti e fanno parte dell'eredità
procedurale delle versioni precedenti del CMS.
Gli hook in effetti sono l'unica cosa che "stona" nella nuovaarchitettura di Drupal8
Issue #1509164: Use Symfony EventDispatcher for hook system
TWIGUno degli hook rimasti è quello per definire i template
(hook_theme(), va messo dentro nomemodulo.module)function webprofiler_theme() { return array( 'webprofiler_toolbar' => array( 'template' => 'Profiler/webprofiler_toolbar', 'variables' => array('token' => NULL, 'templates' => array(), 'profile' => NULL, 'profiler_url' ), 'webprofiler_panel' => array( 'template' => 'Profiler/webprofiler_panel', 'variables' => array('template' => array(), 'profile' => NULL, 'name' => NULL, 'summary' ), ),
template => file *.html.twigvariables => elenco delle variabili che il template "accetta" ininput
TWIGTipicamente un controller in Drupal ritorna un render array,
ossia un array associativo PHP che Drupal sa come trasformarein HTML
public function profilerAction(Profile $profile) {
[...]
'#theme' => 'webprofiler_panel', '#template' => $template, '#profile' => $profile, '#name' => $name, '#summary' => $collector->getPanelSummary(), '#content' => $collector->getPanel(), ) ); } }
TWIG{{ template.renderblock('panel', {'token': profile.token,'name': name,'content': content}) }}<div class="summary" style="display:none">{{ summary }}</div>
templates/Profiler/webprofiler_panel.html.twig
TWIGIl backoffice è stato ristrutturato per adattarsi alle specifichesull'interfaccia di Drupal quindi in questo caso non abbiamo
potuto usare i file twig originali :-(
TWIGI css e i javascript invece sono rimasti esattamente gli stessi (più o
meno)!
TWIGAbbiamo esteso twig per aggiungere nuove funzioni, nel nostro
caso ne abbiamo aggiunte 2: url e pathservices: webprofiler.twig_extension: class: Drupal\webprofiler\Twig\RoutingExtension arguments: ['@url_generator']
class WebprofilerServiceProvider extends ServiceProviderBase { public function alter(ContainerBuilder $container) { $container->getDefinition('twig') ->addMethodCall('addExtension', array(new Reference('webprofiler.twig_extension'))); }
class RoutingExtension extends \Twig_Extension { public function getPath($name, $parameters = array(), $options = array()) { $options['absolute'] = FALSE; return $this->urlGenerator->generateFromRoute($name, $parameters, $options); }
API SPECIFICHEDrupal ha una sua versione delle Form API (soprattutto perragioni storiche) e non usa l'implementazione di Symfony. Lavalidazione però è fatta utilizzando il componente Validator diSymfony (e la libreria egulias/email-validator per la validazionedegli indirizzi email)Drupal non usa Doctrine per la persistenza dei dati maun'implementazione custom bastata su PDO (e ha un Entitymanager specifico)
DATA COLLECTORSOltre ai data_collector presenti in Symfony ne abbiamoaggiunti di specifici per Drupal (views, blocchi, cache, ...)Abbiamo aggiunto anche data_collector che potrebbero essereriportati in Symfony stesso, ad esempio il collector dei serviziAbbiamo in programma di arricchire il dati profilati coninformazioni provenienti da un profiler di codice, come XHProfo uprofiler ( )in parte è già stato fatto
SVILUPPI FUTURIGraficiAnalisi statistiche per il monitoraggio automatico delleperformance (media, mediana, massimo, minimo, ...)Diff tra profili differenti
LINKS (un
po' vecchiotto)
Symfony2 meets Drupal 8Porting Symfony Acme Demo Bundle as a Drupal 8 Module
Want to be a Drupal 8 Expert? Start with SymfonyBuild a Drupal 8 ModuleThe state of Webprofiler
SEI UN SYMFONY DEVELOPER?ALLORA GIÀ CONOSCI DRUPAL8
WE ARE HIRING!
DRUPALDAY MILANO 2014
14 novembre conferenza gratuita15 novembre trainig gratuito e sprint
Deadline CFP 12 ottobre