The Components Book Version: 2.7

Dimensione: px
Iniziare la visualizzazioe della pagina:

Download "The Components Book Version: 2.7"

Transcript

1 The Components Book Version:. generated on August, 0

2 The Components Book (.) This work is licensed under the Attribution-Share Alike.0 Unported license (http://creativecommons.org/ licenses/by-sa/.0/). You are free to share (to copy, distribute and transmit the work), and to remix (to adapt the work) under the following conditions: Attribution: You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). Share Alike: If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license. For any reuse or distribution, you must make clear to others the license terms of this work. The information in this book is distributed on an as is basis, without warranty. Although every precaution has been taken in the preparation of this work, neither the author(s) nor SensioLabs shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in this work. If you find typos or errors, feel free to report them by creating a ticket on the Symfony ticketing system (http://github.com/symfony/symfony-docs/issues). Based on tickets and users feedback, this book is continuously updated.

3 Contents at a Glance Installare e usare i componenti di Symfony... Il componente ClassLoader... Class Loader PSR Caricatore di classi PSR-... MapClassLoader... Cache di Class Loader... Debug di un ClassLoader... Generatore di classi di mappe... Il componente Config... Caricare risorse... Cache basata sulle risorse... Definire e processare valori di configurazione... Il componente Console... Uso di Console... Cambiare comando predefinito...0 Come costruire un'applicazione in un singolo comando... Capire come sono gestiti i parametri della console... Usare gli eventi... Uso del Logger... Aiutante Dialog... Aiutante Formatter... <no title>... Barra di progressione...0 Aiutante Progress... Aiutante Question... Tabella... Aiutante Table... <no title>... Il componente CssSelector...0 Il componente Debug... Debug di ClassLoader... Il componente DependencyInjection... Tipi di iniezione Introduzione ai parametri... 0 Lavorare con parametri e definizioni del contenitore... 0 Compilazione del contenitore... generated on August, 0 Contents at a Glance iii

4 Usare i tag nei servizi... 0 Usare un factory per creare servizi... Configurare servizi con un configuratore di servizi... Come gestire le dipendenze comuni con servizi genitori... Configurazione avanzata del contenitore... Servizi pigri... Flusso di costruzione del contenitore... Il componente DomCrawler... Il componente EventDispatcher... 0 Il distributore consapevole del contenitore... Oggetto evento generico... L'Event Dispatcher Immutable... Distributore di eventi tracciabile... Il componente ExpressionLanguage... Sintassi di Expression... Estendere ExpressionLanguage... Cache di espressioni analizzate... 0 Il componente Filesystem... LockHandler... Il componente Finder... Il componente Form... Creare un indovino di tipi... 0 Eventi dei form... Il componente HttpFoundation... Gestione della sessione... Configurare sessioni e gestori di salvataggio... 0 Test con le sessioni... Integrazione con sessioni legacy... Proxy fidati... 0 Il componente HttpKernel... Il componente Intl... Il componente OptionsResolver... Il componente Process... Il componente PropertyAccess... Il componente Routing Corrispondere una rotta in base all'host... 0 Il componente Security... 0 Il Firewall e il contesto di sicurezza... Autenticazione... Autorizzazione... 0 Sicurezza nel confronto di stringhe e nella generazione di numeri casuali... Il componente Serializer... Il componente Stopwatch... Il componente Templating... Aiutante slots... Aiutante per gli asset... Il componente Translation... iv Contents at a Glance Contents at a Glance

5 Uso di Translator... Aggiungere supporto per un formato personalizzato... Il componente VarDumper... Utilizzo avanzato del componente VarDumper... Il componente YAML... Il formato YAML... generated on August, 0 Contents at a Glance v

6 Chapter Installare e usare i componenti di Symfony Se si inizia un nuovo progetto (o se si ha già un progetto) che userà uno o più componenti, il modo più semplice per integrare tutto è con Composer. Composer è abbastanza intelligente da scaricare i componenti necessari e occuparsi del caricamento automatico, in modo che si può iniziare a usare immediatamente le librerie. Questo articolo approfondirà l'uso di Il componente Finder, tuttavia è applicabile all'uso di qualsiasi componente. Uso del componente Finder. Se si sta creando un nuovo progetto, creare una cartella vuota.. Creare un file chiamato composer.json e incollarvi dentro il codice seguente: Listing - $ composer require symfony/finder Il nome symfony/finder è scritto in cima alla documentazione del componente desiderato. Installare composer, se non fosse già presente sul sistem. A seconda di come lo si installa, si potrebbe avere un file composer.phar nella cartella. In questo caso, nessun problema! Basta eseguire php composer.phar require symfony/finder. Se si sa di aver bisogno di una versione specifica della libreria, aggiungerla al comando: Listing - $ composer require symfony/finder:... Scrivere il proprio codice!. generated on August, 0 Chapter : Installare e usare i componenti di Symfony

7 Una volta che Composer ha scaricato i componenti, basterà includere il file vendor/autoload.php generato da Composer stesso. Tale file si occupa di autocaricare tutte le librerie, in modo che si possano usare immediatamente: Listing - 0 // File esempio: src/script.php // cambiare il percorso in quello della cartella "vendor/" // relativamente a questo file require_once DIR.'/../vendor/autoload.php'; use Symfony\Component\Finder\Finder; $finder = new Finder(); $finder->in('../data/'); Usare tutti i componenti Se si vogliono usare tutti i componenti di Symfony, invece di aggiungerli uno per uno, si può includere il pacchetto symfony/symfony: Listing - $ composer require symfony/symfony Questo includerà anche librerie di bundle e di bridge, che potrebbero non essere effettivamente necessarie. E ora? Ora che i componenti sono installati e autocaricati, leggere la documentazione specifica dei componenti per saperne di più sul loro uso. Buon divertimento! generated on August, 0 Chapter : Installare e usare i componenti di Symfony

8 Chapter Il componente ClassLoader Il componente ClassLoader carica le classi di un progetto automaticamente, purché seguano alcune convenzioni standard di PHP. Uso Ogni volta che si usa una classe non ancora richiesta o inclusa, PHP utilizza il meccanismo di autocaricamento per delegare il caricamento di un file che definisca la classe. Symfony fornisce due autoloader, capaci di caricare classi: Class Loader PSR-0: carica classi che seguono lo standard dei nomi PSR-0; Caricatore di classi PSR-: carica classi che seguono lo standard dei nomi PSR-; MapClassLoader: carica classi che usano una mappa statica dal nome della classe al percorso del file. Inoltre, il componente ClassLoader di Symfony dispone di un insieme di classi wrapper, che si possono usare per aggiungere funzionalità agli autoloader esistenti: Cache di Class Loader Debug di un ClassLoader Installazione Si può installare il componente in due modi: Installarlo via Composer (symfony/class-loader su Packagist ); Usare il repository ufficiale su Git (https://github.com/symfony/classloader ).. https://packagist.org/packages/symfony/class-loader. https://github.com/symfony/classloader generated on August, 0 Chapter : Il componente ClassLoader

9 Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. generated on August, 0 Chapter : Il componente ClassLoader

10 Chapter Class Loader PSR-0 Se si usano classi e librerie di terze parti che seguono lo standard PSR-0, si può usare la classe ClassLoader per caricare tutte le classi del progetto. Si possono usare sia ApcClassLoader sia XcacheClassLoader per mettere in cache un'istanza di ClassLoader o di DebugClassLoader per il debug. Uso La registrazione dell'autoloader ClassLoader è semplice: Listing - 0 require_once '/path/to/src/symfony/component/classloader/classloader.php'; use Symfony\Component\ClassLoader\ClassLoader; $loader = new ClassLoader(); // per abilitare la ricerca in include_path (per esempio per i pacchetti PEAR) $loader->useincludepath(true); registrare qui spazi di nomi e prefissi, vedere più avanti $loader->register(); In un'applicazione Symfony, l'autoloader è registrato automaticamente (vedere app/ autoload.php).. generated on August, 0 Chapter : Class Loader PSR-0 0

11 Usare i metodi addprefix() o addprefixes() per registrare le classi: Listing - 0 // registra un singolo spazio di nomi $loader->addprefix('symfony', DIR.'/vendor/symfony/symfony/src'); // registra più spazi di nomi $loader->addprefixes(array( 'Symfony' => DIR.'/../vendor/symfony/symfony/src', 'Monolog' => DIR.'/../vendor/monolog/monolog/src', )); // registra un prefisso di una classe che segue le convenzioni di PEAR $loader->addprefix('twig_', DIR.'/vendor/twig/twig/lib'); $loader->addprefixes(array( 'Swift_' => DIR.'/vendor/swiftmailer/swiftmailer/lib/classes', 'Twig_' => DIR.'/vendor/twig/twig/lib', )); Si possono cercare le classi di un sotto-spazio dei nomi o di una sotto-gerarchia di classi PEAR in una lista di posizioni, per facilitare la gestione dei venditori di un sottoinsieme di classi per grandi progetti: Listing - $loader->addprefixes(array( 'Doctrine\\Common' => DIR.'/vendor/doctrine/common/lib', 'Doctrine\\DBAL\\Migrations' => DIR.'/vendor/doctrine/migrations/lib', 'Doctrine\\DBAL' => DIR.'/vendor/doctrine/dbal/lib', 'Doctrine' => DIR.'/vendor/doctrine/orm/lib', )); In questo esempio, se si prova a usare una classe nello spazio dei nomi Doctrine\Common o uno dei suoi figli, l'autoloader cercherà prima la classe sotto la cartella doctrine-common. Se non trovata, ripiegherà alla cartella predefinita Doctrine (l'ultima configurata), prima di arrendersi. L'ordine delle registrazioni dei prefissi, in questo caso, è significativo.. generated on August, 0 Chapter : Class Loader PSR-0

12 Chapter Caricatore di classi PSR- New in version.: PsrClassLoader è stato introdotto in Symfony.. Si possono caricare le librerie che seguono lo standard PSR- con PsrClassLoader. Se si gestiscono le dipendenza tramite Composer, si ha giò un autoloader comptabilie con PSR-. Usare questo caricatore in ambienti in cui Composer non sia disponibile. Tutti i componenti di Symfony seguono PSR-. Uso L'esempio seguente dimostra come si possa usare l'autoloader PsrClassLoader per il componente Yaml di Symfony. Si immagini di aver scaricato sia ClassLoader sia il componente Yaml come ZIP e di averli scompattati in una cartella libs. La struttura della cartella assomiglierà a questa: Listing - libs/ ClassLoader/ PsrClassLoader.php... Yaml/ Yaml.php... config.yml demo.php. generated on August, 0 Chapter : Caricatore di classi PSR-

13 In demo.php, si analizzerà il file config.yml. Per poterlo fare, occorre prima configurare PsrClassLoader: Listing - 0 use Symfony\Component\ClassLoader\PsrClassLoader; use Symfony\Component\Yaml\Yaml; require DIR.'/lib/ClassLoader/PsrClassLoader.php'; $loader = new PsrClassLoader(); $loader->addprefix('symfony\\component\\yaml\\', DIR.'/lib/Yaml'); $loader->register(); $data = Yaml::parse( DIR.'/config.yml'); Prima di tutto, il caricatore viene caricato manualmente, usando un'istruzione require, perché non c'è ancora un meccanismo di caricamento automatico. Con la chiamata a addprefix(), si dice al caricatore di classi di cercare classi con prefisso Symfony\Component\Yaml\ nello spazio dei nomi. Dopo aver registrato l'autoloader, il componente Yaml è pronto all'uso.. generated on August, 0 Chapter : Caricatore di classi PSR-

14 Chapter MapClassLoader La classe MapClassLoader consente di auto-caricare file tramite una mappa statica, dalle classi ai file. È utile se si usano librerie di terze parti, che non seguono lo standard PSR-0 e quindi non possono usare class loader PSR-0. Si può usare MapClassLoader insieme a class loader PSR-0, configurando e richiamando su entrambi il metodo register(). Il comportamento predefinito è di appendere MapClassLoader alla pila di auto-caricamento. Se lo si vuole usare come primo autoloader, passare true al metodo register(). In questo caso, il class loader sarà messo in cima alla pila di auto-caricamento. Uso È facile, basta passare la mappa al costruttore, quando si crea un'istanza della classe MapClassLoader: Listing - 0 require_once '/path/to/src/symfony/component/classloader/mapclassloader'; $mapping = array( 'Pippo' => '/percorso/di/pippo', 'Pluto' => '/percorso/di/pluto', ); $loader = new MapClassLoader($mapping); $loader->register();. generated on August, 0 Chapter : MapClassLoader

15 Chapter Cache di Class Loader Introduzione Trovare un file per una classe specifica può essere pesante. Per fortuna, il componente Class Loader dispone di due classi per la cache della mappatura da classe a file. Sia ApcClassLoader che XcacheClassLoader sono wrapper intorno all'oggetto che implementa un metodo findfile(), per trovare il file di una classe. Sia ApcClassLoader che XcacheClassLoader possono essere usati per la cache dell'autoloader di Composer. ApcClassLoader ApcClassLoader è un wrapper di un class loader esistente e mette in cache le chiamate al suo metodo findfile(), usando APC : Listing - require_once '/path/to/src/symfony/component/classloader/apcclassloader.php'; // istanza di una classe che implementa un metodo findfile(), come ClassLoader $loader =...; // mio_prefisso è il prefisso da usare in APC $cachedloader = new ApcClassLoader('mio_prefisso', $loader); // registra il class loader in cache. generated on August, 0 Chapter : Cache di Class Loader

16 0 $cachedloader->register(); // disattiva il loader originale, non in cache, se era stato precedentemente registrato $loader->unregister(); XcacheClassLoader XcacheClassLoader usa XCache per mettere in cache un class loader. La registrazione è semplice: Listing - 0 require_once '/path/to/src/symfony/component/classloader/xcacheclassloader.php'; // istanza di una classe che implementa un metodo findfile(), come ClassLoader $loader =...; // mio_prefisso è il prefisso da usare in XCache $cachedloader = new XcacheClassLoader('mio_prefisso', $loader); // registra il class loader in cache $cachedloader->register(); // disattiva il loader originale, non in cache, se era stato precedentemente registrato $loader->unregister();. generated on August, 0 Chapter : Cache di Class Loader

17 Chapter Debug di un ClassLoader A partire da Symfony., DebugClassLoader del componente ClassLoader è deprecato. Usare DebugClassLoader fornito dal componente Debug. generated on August, 0 Chapter : Debug di un ClassLoader

18 Chapter Generatore di classi di mappe Il caricamento di una classe è solitamente facile, con gli standard PSR-0 e PSR-. Grazie al componente ClassLoader di Symfony o al meccanismo fornito da Composer, non occorre mappare manualmente i nomi di classi ai file PHP. Oggigiorno, le librerie PHP solitamente dispongono di un supporto per il caricamento tramite Composer. A volte però capita di usare librerie di terze parti che non dispongono di un supporto per il caricamento, che costringono quindi a caricare ogni classe a mano. Per esempio, si immagini una libreria con la seguente struttura di cartelle: Listing - libreria/ pippo/ quiquoqua/ Paperone.php Pippo.php pluto/ paperino/ Pippo.php Paperrino.php Questi file contengono le seguenti classi: File libreria/pluto/paperino/paperone.php libreria/pluto/pippo.php libreria/pippo/pluto/pippo.php libreria/pippo/pluto.php Nome classe Acme\Pluto\Paperino Acme\Pluto Acme\Pippo\Pluto Acme\Pippo Per facilitare le cose, il componente ClassLoader dispone di una classe ClassMapGenerator, che rende possibile creare una mappa di nomi di classi e file.. generated on August, 0 Chapter : Generatore di classi di mappe

19 Generare una mappa di classi Per generare una mappa di classi, basta passare la cartella radice dei file delle classi al metodo createmap() : Listing - use Symfony\Component\ClassLoader\ClassMapGenerator; print_r(classmapgenerator::createmap( DIR.'/library')); Dati file e classi della tabella precedente, si dovrebbe ottenere un output come questo: Listing - Array ( [Acme\Pippo] => /var/www/library/pippo/pluto.php [Acme\Pippo\Pluto] => /var/www/library/pippo/pluto/pippo.php [Acme\Pluto\Paperino] => /var/www/library/pluto/paperino/paperone.php [Acme\Pluto] => /var/www/library/pluto/pippo.php ) Esportare la mappa di classi La scrittura della mappa di classi sulla console non è sufficiente per il caricamento automatico. Per fortuna, ClassMapGenerator dispone di un metodo dump(), per salvare la mappa di classi generata su filesystem: Listing - use Symfony\Component\ClassLoader\ClassMapGenerator; ClassMapGenerator::dump( DIR.'/library', DIR.'/class_map.php'); Questa chiamata a dump() genera la mappa di classi e la scrive nel file class_map.php nella stessa cartella, con il seguente contenuto: Listing - <?php return array ( 'Acme\\Pippo' => '/var/www/library/pippo/pluto.php', 'Acme\\Pippo\\Pluto' => '/var/www/library/pippo/pluto/pippo.php', 'Acme\\Pluto\\Baz' => '/var/www/library/pluto/paperino/paperone.php', 'Acme\\Pluto' => '/var/www/library/pluto/pippo.php', ); Invece di caricare ogni file a mano, basta generare la mappa di classi generata, per esempio usando MapClassLoader : Listing - use Symfony\Component\ClassLoader\MapClassLoader; $mapping = include DIR.'/class_map.php'; $loader = new MapClassLoader($mapping); $loader->register(); // ora si possono usare le classi:. generated on August, 0 Chapter : Generatore di classi di mappe

20 0 use Acme\Pippo; $pippo = new Pippo(); L'esempio ipotizza che si abbia già un autoloader funzionante (p.e. tramite Composer o uno dei caricatori di classi del componente ClassLoader. Oltre a esportare la mappa di classi per una cartella, si può anche passare un array di cartelle per cui generare la mappa di classi (il risultato è lo stesso dell'esempio precedente): Listing - use Symfony\Component\ClassLoader\ClassMapGenerator; ClassMapGenerator::dump( array( DIR.'/library/pluto', DIR.'/library/pippo'), DIR.'/class_map.php' );. generated on August, 0 Chapter : Generatore di classi di mappe 0

21 Chapter Il componente Config Il componente Config fornisce diverse classi che aiutano a trovare, caricare, combinare, riempire e validare valori di configurazione di ogni tipo, indipendentemente dal tipo di sorgente (file YAML, XML o INI, oppure ad esempio una base dati). IniFileLoader analizza i contenuti dei file usando la funzione parse_ini_file, quindi si possono impostare solamente parametri stringa. Per impostare tipi diversi di parametri (p.e. booleani, interi, ecc), si raccomanda l'uso di altri caricatori. Installazione Si può installare il componente in due modi: Installarlo tramite Composer (symfony/config su Packagist ); Usare il repository ufficiale su Git (https://github.com/symfony/config ). Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. Sezioni Caricare risorse Cache basata sulle risorse Definire e processare valori di configurazione. https://packagist.org/packages/symfony/config. https://github.com/symfony/config generated on August, 0 Chapter : Il componente Config

22 Chapter 0 Caricare risorse Trovare le risorse Il caricamento della configurazione solitamente inizia con la ricerca delle risorse, nella maggior parte dei casi dei file. Lo si può fare con FileLocator : Listing 0- use Symfony\Component\Config\FileLocator; $configdirectories = array( DIR.'/app/config'); $locator = new FileLocator($configDirectories); $yamluserfiles = $locator->locate('utenti.yml', null, false); Il cercatore di risorse riceve un insieme di posizioni in cui cercare file. Il primo parametro di locate() è il nome del file da cercare. Il secondo parametro può essere il percorso e, se fornito, il cercatore cercherà prima in tale cartella. Il terzo parametro indica se il cercatore debba restituire il primo file trovato oppure un array con tutte le corrispondenze. Caricatori di risorse Per ciascun tipo di risorsa (Yaml, XML, annotazioni, ecc.) va definito un caricatore. Ogni caricatore deve implementare LoaderInterface o estendere la classe astratta FileLoader, che consente di importare ricorsivamente altre risorse: Listing 0- use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Yaml\Yaml;. generated on August, 0 Chapter 0: Caricare risorse

23 0 0 class YamlUserLoader extends FileLoader public function load($resource, $type = null) $configvalues = Yaml::parse(file_get_contents($resource)); gestione dei valori di configurazione // possibile importazione di altri risorse: // $this->import('altri_utenti.yml'); public function supports($resource, $type = null) return is_string($resource) && 'yml' === pathinfo( $resource, PATHINFO_EXTENSION ); Trovare il giusto caricatore La classe LoaderResolver riceve un insieme di caricatori come primo parametro del suo costruttore. Quando una risorsa (per esempio un file XML) va caricata, cerca in questo insieme di caricatori e restituisce il caricatore che supporta questo particolare tipo di risorsa. La classe DelegatingLoader fa uso di LoaderResolver. Quando gli viene richiesto di caricare una risorsa, delega la questione a LoaderResolver. Se quest'ultimo trova un caricatore adatto, a tale caricatore sarà chiesto di caricare la risorsa: Listing 0-0 use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\DelegatingLoader; $loaderresolver = new LoaderResolver(array(new YamlUserLoader($locator))); $delegatingloader = new DelegatingLoader($loaderResolver); $delegatingloader->load( DIR.'/utenti.yml'); /* Sarà usato YamlUserLoader per caricare questa risorsa, poiché supporta file con estensione "yml" */. generated on August, 0 Chapter 0: Caricare risorse

24 Chapter Cache basata sulle risorse Quando tutte le risorse di configurazione sono state caricate, si potrebbero voler processare i valori di configurazione e combinarli un unico file. Questo file agisce da cache. I suoi contenuti non devono essere rigenerati ogni volta che gira l'applicazione, ma solo quando le risorse di configurazione vengono modificate. Per esempio, il componente Routing di Symfony consente di caricare tutte le rotte e poi di esportare un matcher di UL o un generatore di URL, basati su tali rotte. In questo caso, quando una delle risorse viene modificata (e si sta lavorando in un ambiente di sviluppo), il file generato va invalidato e rigenerato. Si può ottenere questo risultato usando la classe ConfigCache. L'esempio successivo mostra come raccogliere le risorse e generare un codice, basato sulle risorse caricate, e scrivere tale codice in cache. La cache riceve anche l'insieme di risorse usate per generare il codice. Cercando il timestamp "last modified" di tali risorse, la cache può dirci se è ancora fresca o se i suoi contenuti vanno rigenerati: Listing - 0 use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\Resource\FileResource; $cachepath = DIR.'/cache/appUserMatcher.php'; // il secondo parametro indica se si è in debug o meno $usermatchercache = new ConfigCache($cachePath, true); if (!$usermatchercache->isfresh()) // inserire un array di percorsi per il file 'utenti.yml' $yamluserfiles =...; $resources = array(); foreach ($yamluserfiles as $yamluserfile) // vedere la voce precedente "Caricare risorse" per // capire da dove viene $delegatingloader $delegatingloader->load($yamluserfile); $resources[] = new FileResource($yamlUserFile);. generated on August, 0 Chapter : Cache basata sulle risorse

25 0 // il codice per UserMatcher è generato altrove $code =...; $usermatchercache->write($code, $resources); // si potrebbe voler richiedere il codice in cache: require $cachepath; In debug, sarà creato un file.meta nella stessa cartella del file di cache stesso. Tale file.meta contiene le risorse serializzate, i cui timestamp sono usati per determinare se la cache è ancora fresca. Se non si è in debug, la cache è considerata fresca fintanto che esiste, per cui non viene generato alcun file.meta. generated on August, 0 Chapter : Cache basata sulle risorse

26 Chapter Definire e processare valori di configurazione Validare i valori di configurazione Dopo aver caricato i valori di configurazione da ogni tipo di risorsa, i valori e le loro strutture possono essere validati, usando la parte "Definition" del componente Config. Solitamente ci si aspetta che i valori di configurazione mostrino un qualche tipo di gerarchia. Inoltre, i valori dovrebbero essere di un certo tipo, ristretti in numero o all'interno di un determinato insieme di valori. Per esempio, la configurazione seguente (in YAML) mostra una chiara gerarchia e alcune regole di validazione che vi andrebbero applicate (come: "il valore per auto_connect deve essere booleano"): Listing - 0 auto_connect: true default_connection: mysql connections: mysql: host: localhost driver: mysql username: utente password: pass sqlite: host: localhost driver: sqlite memory: true username: utente password: pass Quando si caricano diversi file di configurazione, dovrebbe essere possibile fondere e sovrascrivere alcuni valori. Gli altri valori non vanno fusi e devono rimanere come prima. Inoltre, alcune chiavi sono disponibili solo quando un altra chiave ha uno specifico valore (nell'esempio precedente: la chiave memory ha senso solo quando driver è sqlite). generated on August, 0 Chapter : Definire e processare valori di configurazione

27 Definire una gerarchia di valori di configurazione con TreeBuilder Tutte le regole relative ai valori di configurazione possono essere definite tramite TreeBuilder. Un'istanza di TreeBuilder va restituita da una classe personalizzata Configuration, che implementa ConfigurationInterface : Listing - 0 namespace Acme\DatabaseConfiguration; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Builder\TreeBuilder; class DatabaseConfiguration implements ConfigurationInterface public function getconfigtreebuilder() $treebuilder = new TreeBuilder(); $rootnode = $treebuilder->root('database'); aggiungere definizioni di nodi alla radice dell'albero return $treebuilder; Aggiungere definizioni di nodi all'albero Nodi variabili Un albero contiene definizioni di nodi, che possono essere stratificati in modo semantico. Questo vuol dire che, usando l'indentazione e la notazione fluida, è possibile riflettere la reale struttura dei valori di configurazione: Listing - 0 $rootnode ->children() ->booleannode('auto_connessione') ->defaulttrue() ->end() ->scalarnode('connessione_predefinita') ->defaultvalue('predefinito') ->end() ->end() ; Lo stesso nodo radice è un nodo array e ha dei figli, come il nodo booleano auto_connect e il nodo scalare default_connection. In generale: dopo aver definito un nodo, una chiamata end() porta un gradino in alto nella gerarchia.. generated on August, 0 Chapter : Definire e processare valori di configurazione

28 Tipo di nodo Si può validare il tipo di un valore fornito, usando l'appropriata definizione di nodo. I tipi di nodo disponibili sono: scalare (tipo generico che include booleani, stringhe, interi, virgola mobile e null) booleano intero virgola mobile enum (simile a scalare, ma consente solo un insieme determinato di valori) array variabile (nessuna validazione) e sono creati con node($nome, $tipo) o con i relativi metodi scorciatoia xxxxnode($nome). Nodi di vincoli numerici I nodi numerici (virgola mobile e intero) foniscono due vincoli extra, min() e max(), che consentono di validare il valore: Listing - 0 $rootnode ->children() ->integernode('valore_positivo') ->min(0) ->end() ->floatnode('valore_grosso') ->max(e) ->end() ->integernode('valore_tra_estremi') ->min(-0)->max(0) ->end() ->end() ; Nodi enum I nodi enum forniscono un vincolo che fa corrispondere il dato inserito a una serie di valori: Listing - $rootnode ->children() ->enumnode('genere') ->values(array('maschio', 'femmina')) ->end() ->end() ; Questo restringe l'opzione genere ai valori maschio o femmina. Nodi array Si può aggiungere un livello ulteriore alla gerarchia, aggiungendo un nodo array. Il nodo array stesso potrebbe avere un insieme predefinito di nodi variabili: Listing -. generated on August, 0 Chapter : Definire e processare valori di configurazione

29 0 $rootnode ->children() ->arraynode('connection') ->children() ->scalarnode('driver')->end() ->scalarnode('host')->end() ->scalarnode('utente')->end() ->scalarnode('password')->end() ->end() ->end() ->end() ; Oppure si può definire un prototipo per ogni nodo dentro un nodo array: Listing - 0 $rootnode ->children() ->arraynode('connections') ->prototype('array') ->children() ->scalarnode('driver')->end() ->scalarnode('host')->end() ->scalarnode('utente')->end() ->scalarnode('password')->end() ->end() ->end() ->end() ->end() ; Si può usare un prototipo per aggiungere una definizione, che potrebbe essere ripetuta molte volte dentro il nodo corrente. In base alla definizione del prototipo nell'esempio precedente, è possibile avere molte array di connessione (contenenti driver, host, ecc.). Opzioni dei nodi array Prima di definire i figli di un nodo array, si possono fornire opzioni, come: useattributeaskey() Fornisce il nome di un nodo figlio, i cui valori sono usati come chiavi nell'array risultante requiresatleastoneelement() Dovrebbe esserci almeno un elemento nell'array (funziona solo se viene richiamato anche isrequired()). adddefaultsifnotset() Se dei nodi figli hanno valori predefiniti, usarli se non sono stati forniti dati espliciti. Un esempio: Listing - $rootnode ->children() ->arraynode('parameters') ->isrequired() ->requiresatleastoneelement() ->useattributeaskey('nome') ->prototype('array') generated on August, 0 Chapter : Definire e processare valori di configurazione

30 0 ; ->children() ->scalarnode('valore')->isrequired()->end() ->end() ->end() ->end() ->end() In YAML, la configurazione potrebbe essere come questa: Listing - database: parameters: param: valore: paramval In XML, ciascun nodo parameters avrebbe un attributo name (insieme a value), che sarebbe rimosso e usato come chiave per tale elemento nell'array finale. L'opzione useattributeaskey è utile per normalizzare il modo in cui gli array sono specificati tra formati diversi, come XML e YAML. Valori predefiniti e obbligatori Per tutti i tipi di nodo, è possibile definire valori predefiniti e valori di rimpiazzo nel caso in cui un nodo abbia un determinato valore: defaultvalue() Imposta un valore predefinito isrequired() Deve essere definito (ma può essere vuoto) cannotbeempty() Non può contenere un valore vuoto default*() (null, true, false), scorciatoia per defaultvalue() treat*like() (null, true, false), fornisce un valore di rimpiazzo in caso in cui il valore sia *. Listing -0 0 $rootnode ->children() ->arraynode('connection') ->children() ->scalarnode('driver') ->isrequired() ->cannotbeempty() ->end() ->scalarnode('host') ->defaultvalue('localhost') ->end() ->scalarnode('utente')->end() ->scalarnode('password')->end() ->booleannode('memory') ->defaultfalse() ->end() generated on August, 0 Chapter : Definire e processare valori di configurazione 0

31 0 0 ; ->end() ->end() ->arraynode('settings') ->adddefaultsifnotset() ->children() ->scalarnode('nome') ->isrequired() ->cannotbeempty() ->defaultvalue('valore') ->end() ->end() ->end() ->end() Sezioni facoltative Se si hanno intere sezioni facoltative e che possono essere abilitate/disabilitate, si possono sfruttare le scorciatoie canbeenabled() e canbedisabled() : Listing - 0 $arraynode ->canbeenabled() ; // è equivalente a $arraynode ->treatfalselike(array('enabled' => false)) ->treattruelike(array('enabled' => true)) ->treatnulllike(array('enabled' => true)) ->children() ->booleannode('enabled') ->defaultfalse() ; Il metodo canbedisabled è uguale, tranne per il fatto che la sezione viene abilitata in modo predefinito. Opzioni di fusione Si possono fornire opzioni aggiuntive sul processo di fusione. Per gli array: performnodeepmerging() Quando il valore è definito anche in un altro array di configurazione, non provare a fondere un array, ma sovrascrivilo completamente Per tutti i nodi: cannotbeoverwritten() non consentire che altri array di configurazione sovrascrivano il valore di questo nodo. generated on August, 0 Chapter : Definire e processare valori di configurazione

32 Aggiunta di sezioni Se occorre validare una configurazione complessa, l'albero potrebbe diventare troppo grande, si potrebbe quindi volerlo separare in sezioni. Lo si può fare creando una sezione come nodo separato e quindi aggiungendola all'albero principale con append(): Listing public function getconfigtreebuilder() $treebuilder = new TreeBuilder(); $rootnode = $treebuilder->root('database'); $rootnode ->children() ->arraynode('connection') ->children() ->scalarnode('driver') ->isrequired() ->cannotbeempty() ->end() ->scalarnode('host') ->defaultvalue('localhost') ->end() ->scalarnode('utente')->end() ->scalarnode('password')->end() ->booleannode('memory') ->defaultfalse() ->end() ->end() ->append($this->addparametersnode()) ->end() ->end() ; return $treebuilder; public function addparametersnode() $builder = new TreeBuilder(); $node = $builder->root('parameters'); $node ->isrequired() ->requiresatleastoneelement() ->useattributeaskey('nome') ->prototype('array') ->children() ->scalarnode('valore')->isrequired()->end() ->end() ->end() ; return $node; Questo è utile per evitare di ripetersi, nel caso in cui si abbiano sezioni della configurazione ripetute in posti diversi. generated on August, 0 Chapter : Definire e processare valori di configurazione

33 Normalizzazione Prima di essere processati, i file di configurazione vengono normalizzati, quindi fusi e infine si usa l'albero per validare l'array risultante. Il processo di normalizzazione si usa per rimuovere alcune differenze risultati dai vari formati di configurazione, soprattutto tra Yaml e XML. Il separatore usato nelle chiavi è tipicamente _ in Yaml e - in XML. Per esempio, auto_connect in Yaml e auto-connect. La normalizzazione rende entrambi auto_connect. La chiave interessata non sarà alterata se è mista, come pippo-pluto_muu, o se esiste già. Un'altra differenza tra Yaml e XML è il modo in cui sono rappresentati array di dati. In YAML si può avere: Listing - twig: extensions: ['twig.extension.pippo', 'twig.extension.pluto'] e in XML: Listing - <twig:config> <twig:extension>twig.extension.pippo</twig:extension> <twig:extension>twig.extension.pluto</twig:extension> </twig:config> La normalizzazione rimuove tale differenza, pluralizzando la chiave usata in XML. Si può specificare se si vuole una chiave pluralizzata in tal modo con fixxmlconfig(): Listing - $rootnode ->fixxmlconfig('extension') ->children() ->arraynode('extensions') ->prototype('scalar')->end() ->end() ->end() ; Se la pluralizzazione è irregolare, si può specificare il plurale da usare, come secondo parametro: Listing - $rootnode ->fixxmlconfig('uovo', 'uova') ->children() ->arraynode('uova') ->end() ->end() ; Oltre a sistemare queste cose, fixxmlconfig si assicura che i singoli elementi xml siano modificati in array. Quindi si potrebbe avere: Listing - <connessione>predefinito</connessione> <connessione>extra</connessione> generated on August, 0 Chapter : Definire e processare valori di configurazione

34 e a volte solo: Listing - <connessione>default</connessione> Per impostazione predefinita, connessione sarebbe un array nel primo caso e una stringa nel secondo, rendendo difficile la validazione. Ci si può assicurare che sia sempre un array con fixxmlconfig. Se necessario, si può controllare ulteriormente il processo di normalizzazione. Per esempio, si potrebbe voler consentire che una stringa sia impostata e usata come chiave particolare o che che molte chiavi siano impostate in modo esplicito. Quindi, se tutto tranne id è facoltativo, in questa configurazione: Listing - connessione: name: connessione_mysql host: localhost driver: mysql username: utente password: pass si può consentire anche il seguente: Listing -0 connection: my_mysql_connection Cambiando un valore stringa in un array associativo con name come chiave: Listing - 0 $rootnode ->children() ->arraynode('connessione') ->beforenormalization() ->ifstring() ->then(function($v) return array('name'=> $v); ) ->end() ->children() ->scalarnode('name')->isrequired() ->end() ->end() ->end() ; Regole di validazione Si possono fornire regole di validazione avanzata, usando ExprBuilder. Questa classe implementa un'interfaccia fluida per una struttura di controllo nota. Si può usare per aggiungere regole di validazione avanzate alle definizioni dei nodi, come: Listing - $rootnode ->children() ->arraynode('connessione') ->children() ->scalarnode('driver') ->isrequired() ->validate(). generated on August, 0 Chapter : Definire e processare valori di configurazione

35 0 ; ->ifnotinarray(array('mysql', 'sqlite', 'mssql')) ->theninvalid('valore non valido "%s"') ->end() ->end() ->end() ->end() ->end() Una regola di validazione ha sempre una parte "if". Si può specificare tale parte nel modo seguente: iftrue() ifstring() ifnull() ifarray() ifinarray() ifnotinarray() always() Una regola di validazione richiede anche una parte "then": then() thenemptyarray() theninvalid() thenunset() Di solito, "then" è una closure. Il suo valore di ritorno sarà usato come nuovo valore del nodo, al posto del valore originale del nodo. Processare i valori di configurazione La classe Processor usa l'albero, costruito usando TreeBuilder 0, per processare molteplici array di valori di configurazione da fondere. Se un valore non è del tipo atteso, è obbligatorio e non ancora definito oppure non può essere validato in altri modi, sarà lanciata un'eccezione. Altrimenti, il risultato è un array pulito di valori di configurazione: Listing - 0 use Symfony\Component\Yaml\Yaml; use Symfony\Component\Config\Definition\Processor; use Acme\DatabaseConfiguration; $config = Yaml::parse( file_get_contents( DIR.'/src/Matthias/config/config.yml') ); $config = Yaml::parse( file_get_contents( DIR.'/src/Matthias/config/config_extra.yml') ); $configs = array($config, $config); $processor = new Processor(); $configuration = new DatabaseConfiguration(); $processedconfiguration = $processor->processconfiguration( generated on August, 0 Chapter : Definire e processare valori di configurazione

36 ); $configuration, $configs generated on August, 0 Chapter : Definire e processare valori di configurazione

37 Chapter Il componente Console Il componente Console semplifica la creazione di eleganti e testabili comandi da terminale. Symfony viene distribuito con un componente Console, che permette di creare comandi da terminale. I comandi da terminale possono essere utilizzati per qualsiasi lavoro ripetitivo, come i lavori di cron, importazioni o lavori batch. Installazione Il componente può essere installato in due modi: Installandolo tramite Composer (symfony/console su Packagist ); Utilizzando il repository Git ufficiale (https://github.com/symfony/console ). Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. Creazione di comandi di base Per creare un comando che porga il saluto dal terminale, creare il file SalutaCommand.php, contenente il seguente codice: Listing - namespace Acme\Console\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument;. https://packagist.org/packages/symfony/console. https://github.com/symfony/console generated on August, 0 Chapter : Il componente Console

38 use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class SalutaCommand extends Command protected function configure() $this ->setname('demo:saluta') ->setdescription('saluta qualcuno') ->addargument( 'nome', InputArgument::OPTIONAL, 'Chi vuoi salutare?' ) ->addoption( 'urla', null, InputOption::VALUE_NONE, 'Se impostato, il saluto verrà urlato con caratteri maiuscoli' ) ; protected function execute(inputinterface $input, OutputInterface $output) $nome = $input->getargument('nome'); if ($nome) $testo = 'Ciao '.$nome; else $testo = 'Ciao'; if ($input->getoption('urla')) $testo = strtoupper($testo); $output->writeln($testo); Occorre anche creare il file da eseguire in linea di comando, che crea una Application e vi aggiunge comandi: Listing - 0 #!/usr/bin/env php <?php // application.php require DIR.'/vendor/autoload.php'; use Acme\Console\Command\SalutaCommand; use Symfony\Component\Console\Application; $application = new Application(); $application->add(new SalutaCommand()); $application->run(); È possibile provare il programma nel modo seguente generated on August, 0 Chapter : Il componente Console

39 Listing - $ php application.php demo:saluta Fabien Il comando scriverà, nel terminale, quello che segue: Listing - Ciao Fabien È anche possibile usare l'opzione --urla per stampare il saluto in lettere maiuscole: Listing - $ php application.php demo:saluta Fabien --urla Il cui risultato sarà: Listing - CIAO FABIEN Colorare l'output Windows non supporta i colori ANSI in modo predefinito, quindi il componente Console individua e disabilita i colori quando Windows non dà supporto. Tuttavia, se Windows non è configurato con un driver ANSI e i propri comandi di console invocano altri script che emettono sequenze di colori ANSI, saranno mostrati come sequenze di caratteri grezzi. Per abilitare il supporto ai colori ANSI su Windows, si può installare ConEmu o ANSICON. È possibile inserire il testo da stampare, all'interno di speciali tag per colorare l'output. Ad esempio: Listing - 0 // testo verde $output->writeln('<info>pippo</info>'); // testo giallo $output->writeln('<comment>pippo</comment>'); // testo nero su sfondo ciano $output->writeln('<question>pippo</question>'); // testo nero su sfondo rosso $output->writeln('<error>pippo</error>'); Si può definire un proprio stile, usando la classe OutputFormatterStyle : Listing - use Symfony\Component\Console\Formatter\OutputFormatterStyle; $style = new OutputFormatterStyle('red', 'yellow', array('bold', 'blink')); $output->getformatter()->setstyle('fire', $style); $output->writeln('<fire>pippo</fire>'); I colori di sfondo e di testo disponibili sono: black, red, green, yellow, blue, magenta, cyan e white. Le opzioni disponibili sono: bold, underscore, blink, reverse e conceal. Si possono anche impostare colori e opzioni dentro il tag:. https://code.google.com/p/conemu-maximus/. https://github.com/adoxa/ansicon/releases. generated on August, 0 Chapter : Il componente Console

40 Listing - // testo verde $output->writeln('<fg=green>pippo</fg=green>'); // testo nero su sfondo ciano $output->writeln('<fg=black;bg=cyan>pippo</fg=black;bg=cyan>'); // testo grassetto su sfondo giallo $output->writeln('<bg=yellow;options=bold>pippo</bg=yellow;options=bold>'); Livelli di verbosità New in version.: Le costanti VERBOSITY_VERY_VERBOSE e VERBOSITY_DEBUG sono state introdotte nella versione. La console dispone di tre livelli di verbosità. Tali livelli sono definiti in OutputInterface : Opzione OutputInterface::VERBOSITY_QUIET OutputInterface::VERBOSITY_NORMAL OutputInterface::VERBOSITY_VERBOSE OutputInterface::VERBOSITY_VERY_VERBOSE OutputInterface::VERBOSITY_DEBUG Valore Nessun messaggio in output Livello predefinito di verbosità Verbosità maggiore Messaggi informativi non essenziali Messaggi di debug Si può specificare il livello quieto di verbosità con l'opzione --quiet o -q. L'opzione --verbose o -v si usa quando si vuole un livello di verbosità maggiore. Se si usa il livello VERBOSITY_VERBOSE, viene mostrato lo stacktrace completo delle eccezioni. È anche possibile mostrare un messaggio in un comando solo per uno specifico livello di verbosità. Per esempio: Listing -0 if (OutputInterface::VERBOSITY_VERBOSE <= $output->getverbosity()) $output->writeln(...); Ci sono anche metodi più semantici da usare, per testare ciascun livello di verbosità: Listing - 0 if ($output->isquiet()) if ($output->isverbose()) if ($output->isveryverbose()). generated on August, 0 Chapter : Il componente Console 0

41 if ($output->isdebug()) Quando si usa il livello quieto, viene soppresso ogni output, poiché il metodo write() esce senza stampare nulla. MonologBridge fornisce una classe ConsoleHandler, che consente di mostrare messaggi sulla console. Questo è un modo più pulito rispetto a inserire le chiamate di output all'interno di condizioni. Per un esempio di utilizzo nel framework Symfony, vedere Configurare Monolog per mostrare messaggi di console. Utilizzo dei parametri nei comandi La parte più interessante dei comandi è data dalla possibilità di mettere a disposizione parametri e opzioni. I parametri sono delle stringhe, separate da spazi, che seguono il nome stesso del comando. Devono essere inseriti in un ordine preciso e possono essere opzionali o obbligatori. Ad esempio, per aggiungere un parametro opzionale cognome al precedente comando e rendere il parametro nome obbligatorio, si dovrà scrivere: Listing - 0 $this ->addargument( 'nome', InputArgument::REQUIRED, 'Chi vuoi salutare?' ) ->addargument( 'cognome', InputArgument::OPTIONAL, 'Il tuo cognome?' ); A questo punto si può accedere al parametro cognome dal codice: Listing - if ($cognome = $input->getargument('cognome')) $testo.= ' '.$cognome; Il comando potrà essere utilizzato in uno qualsiasi dei seguenti modi: Listing - $ php application.php demo:saluta Fabien $ php application.php demo:saluta Fabien Potencier È anche possibile consentire una lista di valori a un parametro (si immagini di voler salutare tutti gli amici). Lo si deve fare alla fine della lista dei parametri: Listing -. generated on August, 0 Chapter : Il componente Console

42 $this ->addargument( 'nomi', InputArgument::IS_ARRAY, 'Chi vuoi salutare (separare i nomi con uno spazio)?' ); In questo modo, si possono specificare più nomi: Listing - $ php application.php demo:saluta Fabien Ryan Bernhard Si può accedere al parametro nomi come un array: Listing - if ($nomi = $input->getargument('nomi')) $testo.= ' '.implode(', ', $nomi); Ci sono tre varianti di parametro utilizzabili: Modalità InputArgument::REQUIRED InputArgument::OPTIONAL InputArgument::IS_ARRAY Valore Il parametro è obbligatorio Il parametro è facoltativo, può essere omesso Il parametro può contenere un numero indefinito di parametri e deve essere usato alla fine della lista dei parametri Si può combinare IS_ARRAY con REQUIRED e OPTIONAL, per esempio: Listing - $this ->addargument( 'nomi', InputArgument::IS_ARRAY InputArgument::REQUIRED, 'Chi vuoi salutare (separare i nomi con uno spazio)?' ); Utilizzo delle opzioni nei comandi Diversamente dagli argomenti, le opzioni non sono ordinate (cioè possono essere specificate in qualsiasi ordine) e sono identificate dal doppio trattino (come in --urla; è anche possibile dichiarare una scorciatoia a singola lettera preceduta da un solo trattino come in -u). Le opzioni sono sempre opzionali e possono accettare valori (come in --dir=src) o essere semplici indicatori booleani senza alcuna assegnazione (come in --urla). Nulla impedisce la creazione di un comando con un'opzione che accetti in modo facoltativo un valore. Tuttavia, non c'è modo di distinguere quando l'opzione sia stata usata senza un valore (comando --urla) o quando non sia stata usata affatto (comando). In entrambi i casi, il valore recuperato per l'opzione sarebbe null. generated on August, 0 Chapter : Il componente Console

43 Ad esempio, per specificare il numero di volte in cui il messaggio di saluto sarà stampato, si può aggiungere la seguente opzione: Listing - $this ->addoption( 'ripetizioni', null, InputOption::VALUE_REQUIRED, 'Quante volte dovrà essere stampato il messaggio?', ); Ora è possibile usare l'opzione per stampare più volte il messaggio: Listing -0 for ($i = 0; $i < $input->getoption('ripetizioni'); $i++) $output->writeln($testo); In questo modo, quando si esegue il comando, sarà possibile specificare, opzionalmente, l'impostazione --ripetizioni: Listing - $ php application.php demo:saluta Fabien $ php application.php demo:saluta Fabien --ripetizioni= Nel primo esempio, il saluto verrà stampato una sola volta, visto che ripetizioni è vuoto e il suo valore predefinito è (l'ultimo parametro di addoption). Nel secondo esempio, il saluto verrà stampato volte. Ricordiamo che le opzioni non devono essere specificate in un ordine predefinito. Perciò, entrambi i seguenti esempi funzioneranno correttamente: Listing - $ php application.php demo:saluta Fabien --ripetizioni= --urla $ php application.php demo:saluta Fabien --urla --ripetizioni= Ci sono possibili varianti per le opzioni: Opzione Valore InputOption::VALUE_IS_ARRAY Questa opzione accetta valori multipli (p.e. --dir=/pippo -- dir=/pluto) InputOption::VALUE_NONE Non accettare alcun valore per questa opzione (come in -- urla) InputOption::VALUE_REQUIRED InputOption::VALUE_OPTIONAL Il valore è obbligatorio (come in ripetizioni=), l'opzione stessa è comunque facoltativa L'opzione può avere un valore o meno (p.e. urla o urla=forte) È possibile combinare VALUE_IS_ARRAY con VALUE_REQUIRED o con VALUE_OPTIONAL nel seguente modo: Listing - $this ->addoption( 'ripetizioni', null, generated on August, 0 Chapter : Il componente Console

44 ); InputOption::VALUE_REQUIRED InputOption::VALUE_IS_ARRAY, 'Quante volte dovrà essere stampato il messaggio?', Aiutanti di console Il componente Console contiene anche una serie di "aiutanti", vari piccoli strumenti in grado di aiutare con diversi compiti: Aiutante Dialog: chiede informazioni interattive all'utente Aiutante Formatter: personalizza i colori dei testi Aiutante Progress: mostra una barra di progressione Aiutante Table: mostra dati in una tabella Testare i comandi Symfony mette a disposizione diversi strumenti a supporto del test dei comandi. Il più utile di questi è la classe CommandTester. Questa utilizza particolari classi per la gestione dell'input/output che semplificano lo svolgimento di test senza una reale interazione da terminale: Listing use Acme\Command\SalutaCommand; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; class ListCommandTest extends \PHPUnit_Framework_TestCase public function testexecute() $application = new Application(); $application->add(new SalutaCommand()); $comando = $application->find('demo:saluta'); $testdelcomando = new CommandTester($comando); $testdelcomando->execute(array('command' => $comando->getname())); $this->assertregexp('/.../', $testdelcomando->getdisplay()); Il metodo getdisplay() 0 restituisce ciò che sarebbe stato mostrato durante una normale chiamata dal terminale. Si può testare l'invio di argomenti e opzioni al comando, passandoli come array al metodo execute() : Listing - use Acme\Command\SalutaCommand; use Symfony\Component\Console\Application; generated on August, 0 Chapter : Il componente Console

45 0 0 use Symfony\Component\Console\Tester\CommandTester; class ListCommandTest extends \PHPUnit_Framework_TestCase public function testnameisoutput() $application = new Application(); $application->add(new SalutaCommand()); $comando = $application->find('demo:saluta'); $testdelcomando = new CommandTester($command); $testdelcomando->execute(array( 'command' => $comando->getname(), 'nome' => 'Fabien', '--ripetizioni' =>, )); $this->assertregexp('/fabien/', $testdelcomando->getdisplay()); È possibile testare un'intera applicazione da terminale utilizzando ApplicationTester. Richiamare un comando esistente Se un comando dipende da un altro, da eseguire prima, invece di chiedere all'utente di ricordare l'ordine di esecuzione, lo si può richiamare direttamente. Questo è utile anche quando si vuole creare un "meta" comando, che esegue solo una serie di altri comandi (per esempio, tutti i comandi necessari quando il codice del progetto è cambiato sui server di produzione: pulire la cache, generare i proxy di Doctrine, esportare le risorse di Assetic,...). Richiamare un comando da un altro è molto semplice: Listing - 0 protected function execute(inputinterface $input, OutputInterface $output) $comando = $this->getapplication()->find('demo:saluta'); $parametri = array( 'command' => 'demo:saluta', 'nome' => 'Fabien', '--urla' => true, ); $input = new ArrayInput($parametri); $codicediritorno = $comando->run($input, $output);. generated on August, 0 Chapter : Il componente Console

46 Innanzitutto si dovrà trovare (find() ) il comando da eseguire usandone il nome come parametro. Quindi si dovrà creare un nuovo ArrayInput che contenga i parametri e le opzioni da passare al comando. Infine, la chiamata al metodo run() manderà effettivamente in esecuzione il comando e restituirà il codice di ritorno del comando (0 se tutto è andato a buon fine, un qualsiasi altro intero negli altri altri casi). Nella maggior parte dei casi, non è una buona idea quella di eseguire un comando al di fuori del terminale. Innanzitutto perché l'output del comando è ottimizzato per il terminale. Ma, anche più importante, un comando è come un controllore: dovrebbe usare un modello per fare qualsiasi cosa e restituire informazioni all'utente. Perciò, invece di eseguire un comando dal Web, sarebbe meglio provare a rifattorizzare il codice e spostare la logica all'interno di una nuova classe. Saperne di più Uso di Console Come costruire un'applicazione in un singolo comando Cambiare comando predefinito Usare gli eventi Capire come sono gestiti i parametri della console. generated on August, 0 Chapter : Il componente Console

47 Chapter Uso di Console Oltre alle opzioni specificate per i comandi, ci sono anche alcune opzioni predefinite, oltre che alcuni comandi predefiniti per il componente Console. Questi esempi ipotizzano che sia stato aggiunto un file application.php, da eseguire dalla linea di comando: Listing - #!/usr/bin/env php <?php // application.php use Symfony\Component\Console\Application; $application = new Application(); $application->run(); Comandi predefiniti C'è un comando list, che mostra tutti i comandi registrati e le opzioni disponibili: Listing - $ php application.php list Si può ottenere lo stesso risultato, non eseguendo alcun comando Listing - $ php application.php Il comando help elenca le informazioni di aiuto per il comando specificato. Per esempio, per ottenere aiuto sul comando list: Listing - $ php application.php help list generated on August, 0 Chapter : Uso di Console

48 Eseguendo help senza specificare alcun comando mostrerà le opzioni globali: Listing - $ php application.php help Opzioni globali Si possono ottenere informazioni per ogni comando, con l'opzione --help. Per ottenere aiuto per il comando list: Listing - $ php application.php list --help $ php application.php list -h Si può avere in elenco meno verboso con: Listing - $ php application.php list --quiet $ php application.php list -q Si possono ottenere messaggi più verbosi (se supportato dal comando) con: Listing - $ php application.php list --verbose $ php application.php list -v Le opzioni sulla verbosità hanno un parametro opzionale, tra (predefinito) e, per mostrare messaggi ancora più verbosi: Listing - $ php application.php list --verbose= $ php application.php list -vv $ php application.php list --verbose= $ php application.php list -vvv Se si impostano, in modo facoltativo, nome e versione dell'applicazione: Listing -0 $application = new Application('Applicazione Acme Console', '.'); si può usare: Listing - $ php application.php list --version $ php application.php list -V per ottenere queste informazioni: Listing - Applicazione Acme Console version. Se non si forniscono entrambi i parametri, si otterrà solamente: Listing - console tool Si può forzare la colorazione ANSI con: Listing - $ php application.php list --ansi generated on August, 0 Chapter : Uso di Console

49 o disattivarla con: Listing - $ php application.php list --no-ansi Si possono aggirare le domande interattive del comando in esecuzione con: Listing - $ php application.php list --no-interaction $ php application.php list -n Sintassi breve Non occorre scrivere i nomi interi dei comandi. Basta scrivere la più breve parte non ambigua di un comando, per eseguirlo. Quindi, se non ci sono comandi con un nome simile, si può richiamare help in questo modo: Listing - $ php application.php h Se si hanno comandi che usano : per gli spazi dei nomi, occorre scrivere un pezzo di testo non ambiguo per ogni parte. Se è stato creato il comando demo:saluta, come mostrato in Il componente Console, lo si può eseguire con: Listing - $ php application.php d:s Fabien Se si sceglie un comando troppo breve e quindi ambiguo (cioè più di un comando corrisponde), non verrà eseguito alcun comando, ma verranno mostrati dei suggerimenti sui possibili comandi da eseguire. generated on August, 0 Chapter : Uso di Console

50 Chapter Cambiare comando predefinito New in version.: Il metodo setdefaultcommand() è stato introdotto in Symfony.. eseguirà sempre ListCommand se non si passa un nome di comando. Per cambiare il comando predefinito, basta passare il nome del comando che si vuole eseguire al metodo setdefaultcommand: Listing - 0 namespace Acme\Console\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class HelloWorldCommand extends Command protected function configure() $this->setname('ciao:mondo') ->setdescription('mostra \'Ciao mondo\''); protected function execute(inputinterface $input, OutputInterface $output) $output->writeln('ciao mondo'); Eseguire l'applicazione e cambiare il comando predefinito: Listing - // application.php use Acme\Console\Command\HelloWorldCommand; use Symfony\Component\Console\Application; $command = new HelloWorldCommand(); $application = new Application();. generated on August, 0 Chapter : Cambiare comando predefinito 0

51 0 $application->add($command); $application->setdefaultcommand($command->getname()); $application->run(); Verificare il nuovo comando predefinito, eseguendo: Listing - $ php application.php Mostrerà il seguente: Listing - Ciao Fabien Questa caratteristica ha una limitazione: non la si può usare con i parametri dei comandi. Saperne di più Come costruire un'applicazione in un singolo comando generated on August, 0 Chapter : Cambiare comando predefinito

52 Chapter Come costruire un'applicazione in un singolo comando Quando si costruisce uno strumento a linea di comando, potrebbe non essere necessario fornire molti comandi. In questo caso, dover passare ogni volta il nome del comando potrebbe essere noioso. Per fortuna, è possibile rimuovere questo obbligo, estendendo l'applicazione: Listing namespace Acme\Tool; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\InputInterface; class MiaApplicazione extends Application /** * Restituisce il nome del comando, in base all'input * InputInterface $input L'interfaccia input * string Il nome del comando */ protected function getcommandname(inputinterface $input) // Deve restituire il nome del comando return 'mio_comando'; /** * Restituice i comandi predefiniti, che sono sempre disponibili * array Un array di comandi predefiniti */ protected function getdefaultcommands() // Mantenere i comandi del nucleo, per avere HelpCommand, // usato quando si passa l'opzione --help generated on August, 0 Chapter : Come costruire un'applicazione in un singolo comando

53 0 0 $defaultcommands = parent::getdefaultcommands() $defaultcommands[] = new MioComando(); return $defaultcommands; /** * Sovrascritto in modo che l'applicazione non si aspetti il nome del * comando come primo parametro. */ public function getdefinition() $inputdefinition = parent::getdefinition(); // pulisce il primo parametro normale, che è il nome del comando $inputdefinition->setarguments(); return $inputdefinition; Richiamando lo script da console, sarà sempre usato il comando MioComando, senza doverne passare il nome. Si può anche semplificare come eseguire l'applicazione: Listing - #!/usr/bin/env php <?php // command.php use Acme\Tool\MiaApplicazione; $applicazione = new MiaApplicazione(); $applicazione->run(); generated on August, 0 Chapter : Come costruire un'applicazione in un singolo comando

54 Chapter Capire come sono gestiti i parametri della console Potrebbe risultare difficile capire il modo in cui le applicazione di console gestiscono i parametri. Un'applicazione di console Symony, come molti altri strumenti di CLI, segue il comportamento descritto negli standard docopt. Diamo uno sguardo al seguente comando, che ha tre opzioni: Listing namespace Acme\Console\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class DemoArgsCommand extends Command protected function configure() $this ->setname('demo:args') ->setdescription('descrive i comportamenti dei parametri') ->setdefinition( new InputDefinition(array( new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED), new InputOption('cat', 'c', InputOption::VALUE_OPTIONAL), )) ); protected function execute(inputinterface $input, OutputInterface $output). generated on August, 0 Chapter : Capire come sono gestiti i parametri della console

55 Poiché l'opzione foo non accetta valori, sarà o false (se non passata al comando) o true (se l'utente passa l'opzione --foo). Il valore dell'opzione bar (o della scorciatoia b) è obbligatorio. Può essere separato dal nome dell'opzione da spazi o dal carattere =. L'opzione cat (e la sua scorciatoia c) si comporta in modo simile, ma non ha un valore obbligatorio. Uno sguardo alla seguente tabella offre una panoramica dei modi possibili di passare queste opzioni: Input foo bar cat --bar=hello false "Hello" null --bar Hello false "Hello" null -b=hello false "Hello" null -b Hello false "Hello" null -bhello false "Hello" null -fcworld -b Hello true "Hello" "World" -cfworld -b Hello false "Hello" "fworld" -cbworld false null "bworld" Le cose si complicano un po' se il comando accetta anche un parametro facoltativo: Listing - new InputDefinition(array( new InputArgument('arg', InputArgument::OPTIONAL), )); Si potrebbe dover usare il separatore -- per separare le opzioni dai parametri. Diamo uno sguardo al quinto esempio nella tabella seguente, in cui è usato per dire al comando che World è il valore di arg e non il valore dell'opzione facoltativa cat: Input bar cat arg --bar Hello "Hello" null null --bar Hello World "Hello" null "World" --bar "Hello World" "Hello World" null null --bar Hello --cat World "Hello" "World" null --bar Hello --cat -- World "Hello" null "World" -b Hello -c World "Hello" "World" null generated on August, 0 Chapter : Capire come sono gestiti i parametri della console

56 Chapter Usare gli eventi New in version.: Gli eventi della console sono stati aggiunti in Symfony.. La classe Application del componente Console consente di agganciarsi al ciclo di vita di un'applicazione console, tramite gli eventi. Invece di reinventare la ruota, usa il componente EventDispatcher di Symfony: Listing - use Symfony\Component\Console\Application; use Symfony\Component\EventDispatcher\EventDispatcher; $dispatcher = new EventDispatcher(); $application = new Application(); $application->setdispatcher($dispatcher); $application->run(); Gli eventi della Console scattano solo quando viene eseguito il comando principale. I comandi richiamati dal comando principale non faranno scattare eventi. L'evento ConsoleEvents::COMMAND Scopi tipici: fare qualcosa prima che i comandi siano eseguiti (come scrivere nel log quale comando sta per essere eseguito) o mostrare qualcosa sull'evento che sta per essere eseguito. L'evento ConsoleEvents::COMMAND è distribuito prima dell'esecuzione dei comandi. Gli ascoltatori ricevono un evento ConsoleCommandEvent : Listing - use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\ConsoleEvents; $dispatcher->addlistener(consoleevents::command, function (ConsoleCommandEvent $event). generated on August, 0 Chapter : Usare gli eventi

57 0 // prende l'istanza di input $input = $event->getinput(); // prende l'istanza di output $output = $event->getoutput(); // prende il comando da eseguire $command = $event->getcommand(); // scrive qualcosa sul comando $output->writeln(sprintf('prima di eseguire il comando <info>%s</info>', $command->getname())); ); // prende l'applicazione $application = $command->getapplication(); L'evento ConsoleEvents::TERMINATE Scopi tipici: eseguire delle azioni di pulizia dopo l'esecuzione di un comando. L'evento ConsoleEvents::TERMINATE è distribuito dopo l'esecuzione di un comando. Può essere usato per qualsiasi azione che debba essere eseguita per tutti i comandi o per pulire qualcosa iniziato in un ascoltatore ConsoleEvents::COMMAND (come inviare log, chiudere connessioni a basi dati, inviare ,...). Un ascoltatore potrebbe anche cambiare il codice di uscita. Gli ascoltatori ricevono un evento ConsoleTerminateEvent : Listing - 0 use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Symfony\Component\Console\ConsoleEvents; $dispatcher->addlistener(consoleevents::terminate, function (ConsoleTerminateEvent $event) // prende l'output $output = $event->getoutput(); // prende il comando che è stato eseguito $command = $event->getcommand(); // mostra qualcosa $output->writeln(sprintf('dopo l\'esecuzione del comando <info>%s</info>', $command->getname())); ); // cambia il codice di uscita $event->setexitcode(); Questo evento viene distribuito anche se il comando lancia un'eccezione. Viene distribuito appena prima dell'evento ConsoleEvents::EXCEPTION. In questo caso, il codice di uscita ricevuto è il codice dell'eccezione.. generated on August, 0 Chapter : Usare gli eventi

58 L'evento ConsoleEvents::EXCEPTION Scopi tipici: gestire le eccezioni sollevate durante l'esecuzione di un comando. Ogni volta che un comando solleva un'eccezione, viene distribuito l'evento ConsoleEvents::EXCEPTION. Un ascoltatore può avvolgere o modificare l'eccezione o fare qualcosa di utile che l'applicazione lanci l'eccezione. Gli ascoltatori ricevono un evento ConsoleExceptionEvent : Listing - 0 use Symfony\Component\Console\Event\ConsoleExceptionEvent; use Symfony\Component\Console\ConsoleEvents; $dispatcher->addlistener(consoleevents::exception, function (ConsoleExceptionEvent $event) $output = $event->getoutput(); $command = $event->getcommand(); $output->writeln(sprintf('oops, eccezione lanciat durante l'\esecuzione del comando <info>%s</info>', $command->getname())); // prende il codice di uscita (il codice dell'eccezione o il codice di uscita impostato da un evento ConsoleEvents::TERMINATE) $exitcode = $event->getexitcode(); // cambia l'eccezione con un'altra $event->setexception(new \LogicException('Eccezione', $exitcode, $event->getexception())); );. generated on August, 0 Chapter : Usare gli eventi

59 Chapter Uso del Logger New in version.: ConsoleLogger è stato introdotto in Symfony.. Il componente Console dispone di un logger, che aderisce allo standard PSR-. A seconda dell'impostazione sulla verbosità, i messaggi di log saranno inviati all'istanza di OutputInterface passata come parametro al costruttore. Il logger non ha dipendenze esterno, tranne php-fig/log. Questo è utile per applicazioni e comandi di console che vogliono un logger PSR- leggero: Listing - 0 namespace Acme; use Psr\Log\LoggerInterface; class MyDependency private $logger; public function construct(loggerinterface $logger) $this->logger = $logger; public function dostuff() $this->logger->info('mi piacciono i tagli di Tony Vairelles.'); Ci si può appoggiare al logger per usare questa dipendenza da dentro un comando: Listing - namespace Acme\Console\Command;. generated on August, 0 Chapter : Uso del Logger

60 0 0 use Acme\MyDependency; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Logger\ConsoleLogger; class MyCommand extends Command protected function configure() $this ->setname('mio:comando') ->setdescription( 'Usa una dipendenza esterna che richiede un logger PSR-' ) ; protected function execute(inputinterface $input, OutputInterface $output) $logger = new ConsoleLogger($output); $mydependency = new MyDependency($logger); $mydependency->dostuff(); La dipendenza userà l'istanza di ConsoleLogger come logger. I messaggi di log emessi saranno mostrati nell'output della console. Verbosità A seconda del livello di verbosità con cui è eseguito il comando, i messaggi potrebbero o meno essere inviati all'istanza di OutputInterface. I logger della console si comporta come il gestore di console di Monolog. L'associazione tra livello di log e verbosità può essere configurata tramite il secondo parametro del costruttore di ConsoleLogger : Listing - $verbositylevelmap = array( LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, ); $logger = new ConsoleLogger($output, $verbositylevelmap); Colori Il logger mostra messaggi di log formattati con un colore che ne riflette il livello, Questo comportamento è configurabile tramite il terzo parametro del costruttore: Listing generated on August, 0 Chapter : Uso del Logger 0

61 $formatlevelmap = array( LogLevel::CRITICAL => self::info, LogLevel::DEBUG => self::error, ); $logger = new ConsoleLogger($output, array(), $formatlevelmap); generated on August, 0 Chapter : Uso del Logger

62 Chapter 0 Aiutante Dialog L'aiutante Dialog è stato deprecato in Symfony. e sarà rimosso in Symfony.0. Usare invece l'aiutante Question, che è più facile da usare. DialogHelper fornisce funzioni per chiedere informazioni all'utente. È incluso nell'insieme predefinito degli aiutanti, ottenibile richiamando gethelperset() : Listing 0- $dialog = $this->gethelper('dialog'); Tutti i metodi all'interno dell'aiutante Dialog hanno OutputInterface come primo parametro, la domanda come secondo parametro e il valore predefinito come ultimo parametro. Chiedere conferma all'utente Si supponga di voler confermare un'azione prima di eseguirla effettivamente. Aggiungere al comando il codice seguente: Listing 0- if (!$dialog->askconfirmation( $output, '<question>continuare con questa azione?</question>', false )) return; In questo caso, all'utente sarà chiesto "Continuare con questa azione", e si otterrà true se l'utente risponde y o false se l'utente risponde n. Il terzo parametro di askconfirmation() è il valore. generated on August, 0 Chapter 0: Aiutante Dialog

63 predefinito da restituire se l'utente non inserisce alcun input. Qualsiasi altro input farà ripetere la domanda. Chiedere informazioni all'utente Si possono anche porre domande con risposte più complesse di sì/no. Per esempio, se si vuole sapere il nome di un bundle, si può aggiungere al comando: Listing 0- $bundle = $dialog->ask( $output, 'Prego inserire il nome del bundle', 'AcmeDemoBundle' ); All'utente sarà chiesto "Prego inserire il nome del bundle". L'utente potrà inserire un nome, che sarà restituito dal metodo ask(). Se lasciato vuoto, sarà restituito il valore predefinito (qui AcmeDemoBundle). Autcompletamento Si possono anche specificare delle risposte possibili alla domanda data. Saranno completate man mano che l'utente scrive: Listing 0- $dialog = $this->gethelper('dialog'); $bundlenames = array('acmedemobundle', 'AcmeBlogBundle', 'AcmeStoreBundle'); $name = $dialog->ask( $output, 'Prego inserire il nome del bundle', 'PippoBundle', $bundlenames ); Nascondere la risposta dell'utente Si può anche fare una domanda e nascodere la risposta. Ciò risulta utile in particolare per le password: Listing 0- $dialog = $this->gethelper('dialog'); $password = $dialog->askhiddenresponse( $output, 'Inserire la password della base dati', false ); Quando si richiede una risposta nascosta, Symfony userà un binario, cambierà la modalità stty oppure userà un altro trucco per nascondere la risposta. Se nessuna opzione è disponibile, si arrenderà e mostrerà la risposta, a meno che non si passi false come terzo parametro, come nell'esempio appena visto. In questo caso, sarà sollevata una RuntimeException.. generated on August, 0 Chapter 0: Aiutante Dialog

64 Validare la risposta Si può anche validare la risposta. Per esempio, nell'ultimo esempio è stato chiesto il nome di un bundle. Seguendo le convenzioni di Symfony, il nome dovrebbe avere il suffisso Bundle. Lo si può validare, usando il metodo askandvalidate() : Listing 0-0 $bundle = $dialog->askandvalidate( $output, 'Prego inserire il nome del bundle', function ($answer) if ('Bundle'!== substr($answer, -)) throw new \RuntimeException( 'Il nome del bundle deve avere \'Bundle\' come suffisso' ); ); return $answer;, false, 'AcmeDemoBundle' Il metodo ha due nuovi parametri. La sua firma completa è: Listing 0- askandvalidate( OutputInterface $output, string array $question, callback $validator, integer $attempts = false, string $default = null, array $autocomplete = null ) Il parametro $validator è un callback, che gestisce la validazione. Dovrebbe lanciare un'eccezione se qualcosa va storto. Il messaggio dell'eccezione è mostrato nella console, quindi è una buona pratica inserirvi delle informazioni rilevanti. Si può impostare il numero massimo di volte in cui fare la domanda, nel parametro $attempts. Una volta raggiunto tale numero, sarà usato il valore predefinito, fornito nell'ultimo parametro. Usando false si indica che il numero di tentativi è infinito. L'utente vedrà la domanda finché inserisce una risposta non valida e potrà procedere solo in caso di risposta valida. Nascondere la risposta dell'utente Si può anche fare una domanda e validare una risposta nascosta: Listing 0- $dialog = $this->gethelper('dialog'); $validator = function ($value) if ('' === trim($value)) throw new \Exception('La password non può essere vuota'); return $value;. generated on August, 0 Chapter 0: Aiutante Dialog

65 0 ; $password = $dialog->askhiddenresponseandvalidate( $output, 'Si prega di inserire la password', $validator, 0, false ); Se si vuole consentire che la risposta sia visibile, in caso non possa essere nascosta per qualche ragione, passare true come quinto parametro. Consentire una scelta da una lista di risposte Se si ha un insieme predefinito di risposte tra cui l'utente può scegliere, si può usare il metodo ask descritto in precedenza oppure, per assicurarsi che l'utente fornisca una risposta corretta, il metodo askandvalidate. Entrambi hanno lo svantaggio di costringere lo sviluppatore a gestire i valori non corretti da solo. Si può invece usare il metodo select(), che assicura che l'utente possa inserire solamente una stringa valida, da una lista predefinita: Listing 0-0 $dialog = $this->gethelper('dialog'); $colors = array('rosso', 'blu', 'giallo'); $color = $dialog->select( $output, 'Scegli il tuo colore preferito (predefinito: rosso)', $colors, 0 ); $output->writeln('hai scelto: '. $colors[$color]); fare qualcosa con il colore L'opzione selezionata come predefinita va fornita come quarto parametro. Il valore predefinito è null, che significa che nessuna opzione è predefinita. Se l'utente inserisce una stringa non valida, viene mostrato un errore e chiesto all'utente di fornire una nuova risposta, finché non ne inserisce una valida o raggiunge il numero massimo di tentativi (definibile nel quinto parametro). Il valore predefinito per i tentativi è false, che equivale a infiniti tentativi. Si può definire un messaggio di errore personalizzato nel sesto parametro. New in version.: Il supporto alla selezione multipla è stato aggiunto in Symfony.. Scelte multiple A volte si possono dare più risposte. DialogHelper lo supporta tramite l'uso di valori separati da virgole. Per abilitare questa possibilità, occorre impostare il settimo parametro a true: Listing 0-0 $selected = $dialog->select( $output,. generated on August, 0 Chapter 0: Aiutante Dialog

66 0 ); 'Scegli il tuo colore preferito (predefinito: rosso)', $colors, 0, false, 'Il valore "%s" non è valido', true // abilita la selezione multipla $selectedcolors = array_map(function($c) use ($colors) return $colors[$c];, $selected); $output->writeln( 'Hai scelto: '. implode(', ', $selectedcolors) ); Se ora l'utente inserisce,, il risultato sarà: Hai scelto: blu, giallo. Testare un comando con un input atteso Se si vuole scrivere un test per un comando che si aspetta un qualche tipo di input da linea di comando, occorre sovrascrivere HelperSet usato dal comando: Listing use Symfony\Component\Console\Helper\DialogHelper; use Symfony\Component\Console\Helper\HelperSet; public function testexecute() $commandtester = new CommandTester($command); $dialog = $command->gethelper('dialog'); $dialog->setinputstream($this->getinputstream("test\n")); // Equivale all'inserimento di "Test" e pressione di ENTER // Se occorre una conferma, va bene anche "yes\n" $commandtester->execute(array('command' => $command->getname())); // $this->assertregexp('/.../', $commandtester->getdisplay()); protected function getinputstream($input) $stream = fopen('php://memory', 'r+', false); fputs($stream, $input); rewind($stream); return $stream; Impostando il flusso di input di DialogHelper, si imita ciò che la console farebbe internamente con l'input dell'utente tramite cli. In questo modo, si può testare ogni interazione, anche complessa, passando un appropriato flusso di input. generated on August, 0 Chapter 0: Aiutante Dialog

67 Chapter Aiutante Formatter L'aiutante Formatter fornisce funzioni per formattare l'output con colori. Si possono fare cose più avanzate con questo aiutante rispetto a Colorare l'output. L'aiutante FormatterHelper è incluso nell'insieme predefinito di aiutanti, ottenibile richiamando gethelperset() : Listing - $formatter = $this->gethelperset()->get('formatter'); I metodi restituiscono una stringa, che solitamente sarà resa nella console, passandola al metodo OutputInterface::writeln. Scrivere messaggi in una sezione Symfony offre uno stile definito per mostrare un messaggio che appartenga a una "sezione". Mostra la sezione con un colore e contornata da parentesi e il messaggio a destra di essa. A meno del colore, appare in questo modo: Listing - [UnaSezione] Qui appare un messaggio correlato alla sezione Per riprodurre questo stile, si può usare il metodo formatsection() : Listing - $formattedline = $formatter->formatsection( 'UnaSezione', 'Qui appare un messaggio correlato alla sezione' ); $output->writeln($formattedline);. generated on August, 0 Chapter : Aiutante Formatter

68 Scrivere messaggi in un blocco A volte si vuole poter mostrare un intero blocco di testo con un colore di sfondo. Symfony lo usa per mostrare messaggi di errore. Se si mostrano messaggi di errore su più linee in modo manuale, si noterà che lo sfondo ha lunghezza pari solo a ciascuna singola linea. Usare il metodo formatblock() per generare un blocco: Listing - $errormessages = array('errore!', 'Qualcosa è andato storto'); $formattedblock = $formatter->formatblock($errormessages, 'error'); $output->writeln($formattedblock); Come si può vedere, passando un array di messaggi al metodo formatblock(), si crea l'output desiderato. Se si passa true come terzo parametro, il blocco sarà formattato con più padding (una linea vuota sopra e una sotto ai messaggi e due spazi a sinistra e a destra). Lo "stile" esatto usato nel blocco dipende dallo sviluppatore. In tal caso, è stato usato lo stile predefinito error, ma ci sono altri stili e se ne possono creare di propri. Vedere Colorare l'output.. generated on August, 0 Chapter : Aiutante Formatter

69 generated on August, 0 Chapter : Aiutante Formatter

70 Chapter Barra di progressione New in version.: La barra di progressione è stata introdotta in Symfony., come sostituto dell'aiutante Progress. Durante l'esecuzione di comandi molto lunghi, può essere d'aiuto mostrare informazioni sull'avanzamento, man mano che il comando gira: Per mostrare dettagli sull'avanzamento, usare ProgressBar, passandogli un numero totale di unità, e avanzando la progressione durante i passi: Listing - 0 use Symfony\Component\Console\Helper\ProgressBar; // crea una nuova barra di progressione (0 unità) $progress = new ProgressBar($output, 0); // inizia la barra $progress->start(); $i = 0; while ($i++ < 0) fare qualcosa // avanzare la progressione di una unità $progress->advance(); // si può anche avanzare la progressione di più unità // $progress->advance();. generated on August, 0 Chapter : Barra di progressione 0

71 0 // assicura che la barra di progressione sia al 00% $progress->finish(); Invece di far avanzare la barra di un numero di passi (con il metodo advance() ), si può anche impostare la progressione corrente, richiamando il metodo setcurrent(). La barra di progressione funziona solo su piattaforme con supporto per i codici ANSI. Su altre piattaforme non verrà generato alcun output. Se non si conosce in anticipo il numero di passi, si può omettere il relativo parametro durante la creazione dell'istanza di ProgressBar : Listing - $progress = new ProgressBar($output); La progressione sarà quindi mostrata come un throbber: Listing - # passi non definiti (mostra un throbber) 0 [> ] [-----> ] [============================] # passi definiti 0/ [> ] 0% / [=========> ] % / [============================] 00% Non appena il comando finisce, non dimenticare di richiamare finish(), per assicurarsi che la barra di progressione venga aggiornata con il completamento al 00%. Se si vuole mostrare qualcosa durante l'esecuzione della barra di progressione, richiamare prima clear(). Successivamente, richiamare display() per mostrare nuovamente la barra di progressione. Personalizzazione della barra di progressione Formati predefiniti Le informazioni predefinite della barra di progressione dipendono dal livello di verbosità dell'instanza di OutputInterface: Listing generated on August, 0 Chapter : Barra di progressione

72 0 # OutputInterface::VERBOSITY_NORMAL (CLI senza opzioni di verbosità) 0/ [> ] 0% / [=========> ] % / [============================] 00% # OutputInterface::VERBOSITY_VERBOSE (-v) 0/ [> ] 0% sec / [=========> ] % sec / [============================] 00% sec # OutputInterface::VERBOSITY_VERY_VERBOSE (-vv) 0/ [> ] 0% sec / [=========> ] % sec / [============================] 00% sec # OutputInterface::VERBOSITY_DEBUG (-vvv) 0/ [> ] 0% sec/ sec.0 MB / [=========> ] % sec/ sec.0 MB / [============================] 00% sec/ sec.0 MB Se si richiama un comando con l'opzione -q, la barra di progressione non sarà mostrata. Invece di appoggioarsi al livello di verbosità del comando, si può anche forzare il formato tramite setformat(): Listing - $bar->setformat('verbose'); I formati predefiniti sono i seguenti: normal verbose very_verbose debug Se non si imposta il numero di passi della barra di progressione, usare le varianti _nomax: normal_nomax verbose_nomax very_verbose_nomax debug_nomax Formati personalizzati Invece di usare i formati predefiniti, se ne possono definire di propri: Listing - $bar->setformat('%bar%'); In questo modo si imposta il formato per mostrare solo la barra stessa: Listing - > =========> ============================ generated on August, 0 Chapter : Barra di progressione

73 Un formato per la barra di progressione è una stringa con determinati segnaposto (un nome racchiuso da simboli %). I segnaposto vengono sostituiti in base alla progressione corrente della barra. Ecco una lista di segnaposto predefiniti: current: il passo corrente; max: il numero massimo di passi (o 0, se non definito); bar: la barra stessa; percent: la percentuale di completamento (non disponibile se max non è definito); elapsed: il tempo trascorso dall'inizio della barra di progressione; remaining: il tempo restante al completamento (non disponibile se max non è definito);; estimated: il tempo stimato per il completamento (non disponibile se max non è definito);; memory: l'uso della memoria; message: il messaggio allegato alla barra di progressione. Per esempi, ecco come si può impostare il formato per essere uguale a quello debug: Listing - $bar->setformat(' %current%/%max% [%bar%] %percent:s%% %elapsed:s%/%estimated:-s% %memory:s%'); Notare la parte :s aggiunta ad alcuni segnaposto. Questo è il modo in cui si può alterare il modo in cui appare la barra (formattazione e allineamento). La parte dopo il simbolo : è usata per impostare il formato sprintf della stringa. Il segnaposto message è un po' speciale, perché lo si deve impostare manualmente: Listing - 0 $bar->setmessage('inizio'); $bar->start(); $bar->setmessage('in corso...'); $bar->advance(); $bar->setmessage('finito'); $bar->finish(); Invece di impostare il formato per una data istanza della barra di progressione, si possono anche definire formati globali: Listing -0 ProgressBar::setFormatDefinition('minimal', 'Progress: %percent%%'); $bar = new ProgressBar($output, ); $bar->setformat('minimal'); Questo codice definisce un nuovo formato, chiamato minimal, che si può usare per le barre di progressione: Listing - Progress: 0% Progress: % Progress: 00% Spesso è meglio ridefinire i formati predefiniti, invece di crearn di nuovi, perché questo consente la variazione automatica in base alla verbosità del comando. generated on August, 0 Chapter : Barra di progressione

74 Quando si definisce un nuovo stile, che contiene segnaposto disponibili solo quando il numero massimo di passi sia noto, si dovrebbe creare una variante _nomax: Listing - ProgressBar::setFormatDefinition('minimal', '%percent%% %remaining%'); ProgressBar::setFormatDefinition('minimal_nomax', '%percent%%'); $bar = new ProgressBar($output); $bar->setformat('minimal'); Quando si mostra la barra di progressione, il formato sarà impostato automaticamente a minimal_nomax se la barra non dispone del numero massimo di passi, come nell'esempio precedente. Un formato può contenere qualsiasi codice ANSI valido e può anche usare i modi specifici di Symfony per impostare i colori: Listing - ProgressBar::setFormatDefinition( 'minimal', '<info>%percent%</info>\0[m%\0[0m <fg=white;bg=blue>%remaining%</>' ); Un formato può estendersi per più righe. Questo torna molto utile qando si vogliono mostrare più informazioni contestuali, insieme alla barra di progressione (vedere l'esempio all'inizio di questo articolo). Impostazioni della barra Tra i segnaposto, bar è un po' speciale, perché tutti i caratteri usati per mostrarlo possono essere personalizzati: Listing - 0 // la parte finita della barra $progress->setbarcharacter('<comment>=</comment>'); // la parte non finita della barra $progress->setemptybarcharacter(' '); // il carattere di progressione $progress->setprogresscharacter(' '); // la larghezza della barra $progress->setbarwidth(0); generated on August, 0 Chapter : Barra di progressione

75 Per questioni prestazionali, fare attenzione a non impostare un numero totale di passi troppo elevato. Per esempio, se si itera un gran numero di elementi, considerare l'impostazione di una frequenza maggiore, richiamando setredrawfrequency(), in modo da aggiornare solamente ogni tot iterazioni: Listing - 0 $progress->start($output, 0000); // aggiorna ogni 00 iterazioni $progress->setredrawfrequency(00); $i = 0; while ($i++ < 0000) fare qualcosa $progress->advance(); Segnaposto personalizzati Se si vogliono mostrare alcune informazioni che dipendono dalla barra di progressione e non sono diponibili tra i segnaposto predefiniti, se ne possono creare di propri. Vediamo come creare un segnaposto remaining_steps, che mostri il numero di passi rimanenti: Listing - ProgressBar::setPlaceholderFormatter( '%remaining_steps%', function (ProgressBar $bar, OutputInterface $output) return $bar->getmaxsteps() - $bar->getstep(); ); Messaggi personalizzati Il segnaposto %message% consente di specificare un messaggio perrsonalizzato, da mostrare insieme alla barra di progressione. Se però ne servono più di uno, basta definire il proprio: Listing - 0 $bar->setmessage('inizio'); $bar->setmessage('', 'filename'); $bar->start(); $bar->setmessage('in corso...'); while ($file = array_pop($files)) $bar->setmessage($filename, 'filename'); $bar->advance(); $bar->setmessage('finito'); $bar->setmessage('', 'filename'); $bar->finish(); Per la parte filename della barra di progressione, basta aggiungere il segnaposto %filename% al proprio formato: Listing -. generated on August, 0 Chapter : Barra di progressione

76 $bar->setformat(" %message%\n %step%/%max%\n Working on %filename%"); generated on August, 0 Chapter : Barra di progressione

77 Chapter Aiutante Progress New in version.: Il metodo setcurrent è stato introdotto in Symfony.. New in version.: Il metodo clear è stato introdotto in Symfony.. L'aiutante Progress è stato deprecato in Symfony. e sarà rimosso in Symfony.0. Si dovrebbe invece usare la barra di progressione, che è più potente. Quando si eseguono comandi che devono girare a lungo, può essere utile mostrare una barra di progressione, che si aggiorna durante l'esecuzione: Per mostrare dettagli sulla progressione, usare la classe ProgressHelper, passando il numero totale di unità e avanzando la progressione man mano che il comando gira: Listing - 0 $progress = $this->gethelperset()->get('progress'); $progress->start($output, 0); $i = 0; while ($i++ < 0) fare qualcosa // avanzare la barra di progressione di unità $progress->advance(); $progress->finish();. generated on August, 0 Chapter : Aiutante Progress

78 Si può anche impostare la progressione attaule, richiamando il metodo setcurrent(). Se si vuole mostrare qualcosa mentre la barra di progressione avanza, richiamare prima clear(). Dopo aver finito, richiamre display() per mostrare di nuovo la barra di progressione. Si può anche personalizzare il modo in cui la progressione viene mostrata, con vari livelli di verbosità. Ciascuno di questi mostra diversi possibili elementi, come la percentuale di completamento, una barra mobile, o informazioni su attuale/totale (p.e. 0/0): Listing - $progress->setformat(progresshelper::format_quiet); $progress->setformat(progresshelper::format_normal); $progress->setformat(progresshelper::format_verbose); $progress->setformat(progresshelper::format_quiet_nomax); // valore predefinito $progress->setformat(progresshelper::format_normal_nomax); $progress->setformat(progresshelper::format_verbose_nomax); Si possono anche controllare i vari caratteri e la larghezza usata per la barra: Listing - // la parte finale della barra $progress->setbarcharacter('<comment>=</comment>'); // la parte in corso della barra $progress->setemptybarcharacter(' '); $progress->setprogresscharacter(' '); $progress->setbarwidth(0); Per tutte le opzioni disponibili, consultare la documentazione delle API di ProgressHelper. Per questioni prestazionali, fare attenzione a non impostare un numero totale di passi troppo elevato. Per esempio, se si itera un gran numero di elementi, considerare l'impostazione di una frequenza maggiore, richiamando setredrawfrequency(), in modo da aggiornare solamente ogni tot iterazioni: Listing - 0 $progress->start($output, 0000); // avanzare ogni 00 iterazioni $progress->setredrawfrequency(00); $i = 0; while ($i++ < 0000) fare qualcosa $progress->advance(); generated on August, 0 Chapter : Aiutante Progress

79 Chapter Aiutante Question New in version.: L'aiutante Question è stato introdotto in Symfony.. QuestionHelper fornisce funzioni per chiedere all'utente maggiori informazioni. Fa parte degli aiutanti predefiniti, il cui elenco si può ottenere richiamando gethelperset() : Listing - $helper = $this->gethelper('question'); L'aiutante Question ha un solo metodo, ask(), che ha bisogno di un'istanza di InputInterface come primo parametro, una istanza di OutputInterface come secondo parametro e una Question come ultimo parametro. Chiedere conferma all'utente Si supponga di voler confermare un'azione, prima di esegurila. Aggiungere a un comando: Listing - use Symfony\Component\Console\Question\ConfirmationQuestion; $helper = $this->gethelper('question'); $question = new ConfirmationQuestion('Continuare con questa azione?', false); if (!$helper->ask($input, $output, $question)) return; In questo caso, all'utente sarà posta la domanda "Continuare con questa azione?". Se l'utente risponde con y, restituirà true, se invece risponderà n restituirà false. Il secondo parametro di construct() generated on August, 0 Chapter : Aiutante Question

80 è il valore predefinito da restituire se l'utente non risponde nulla. Qualsiasi risposta diversa farà sì che la domanda venga posta nuovamente. Chiedere informazioni all'utente Si possono anche porre domande con risposte più elaborate. Per esempio, se si vuole sapere il nome di un bundle, si può aggiungere a un comando: Listing - use Symfony\Component\Console\Question\Question; $question = new Question('Si prega di inserire il nome del bundle', 'AcmeDemoBundle'); $bundle = $helper->ask($input, $output, $question); All'utente sarà chiesto "Si prega di inserire il nome del bundle". L'utente può inserire un nome, che sarà restituito dal metodo ask(). Se la risposta sarà vuota, sarà restituito il valore predefinito (AcmeDemoBundle, in questo caso). Far scegliere da una lista di risposte Se si ha un insieme predefinito di risposte, tra cui l'utente può scegliere, si può usare ChoiceQuestion, che si assicura che l'utente inserisca una stringa valida, cioè compresa nella lista: Listing - 0 use Symfony\Component\Console\Question\ChoiceQuestion; $helper = $this->gethelper('question'); $question = new ChoiceQuestion( 'Si prega di scegliere un colore (predefinito: rosso)', array('rosso', 'blu', 'giallo'), 0 ); $question->seterrormessage('il colore %s non è valido.'); $color = $helper->ask($input, $output, $question); $output->writeln('colore scelto: '.$color); fare qualcosa con il colore L'opzione predefinita viene fornità dal terzo parametro del costruttore. Il valore predefinito è null, che significa che nessuna opzione sarà quella predefinita. Se l'utente inserisce una stringa non valida, viene mostrato un messaggio di errore e chiesta nuovamente la domanda, fino a che l'utente non inserirà una stringa valida o raggiungerà il numero massimo di tentativi. Il valore predefinito per il numero massimo di tentativi è null, che significa un numero infinito di tentativi. Si può definire un messaggio di errore, usando seterrormessage() 0.. construct generated on August, 0 Chapter : Aiutante Question 0

81 Scelte multiple A volte, si possono dare più risposte. ChoiceQuestion fornisce questa caratteristica, usando valori separati da virgole. Per abilitarla, usare setmultiselect() : Listing - 0 use Symfony\Component\Console\Question\ChoiceQuestion; $helper = $this->gethelper('question'); $question = new ChoiceQuestion( 'Si prega di scegliere dei colori (predefiniti: rosso e blu)', array('rosso', 'blu', 'giallo'), '0,' ); $question->setmultiselect(true); $colors = $helper->ask($input, $output, $question); $output->writeln('colori scelti: '. implode(', ', $colors)); Se l'utente inserisce,, il risultato sarà: Colori scelti: blu, giallo. Se l'utente non inserisce niente, il risultato sarà: Colori scelti: rosso, blu. Completamento Si può anche specificare un array di potenziali risposte per una data domanda. Questi forniranno un completamento automatico, mentre l'utente scrive: Listing - use Symfony\Component\Console\Question\Question; $bundles = array('acmedemobundle', 'AcmeBlogBundle', 'AcmeStoreBundle'); $question = new Question('Si prega di inserire il nome di un bundle', 'PippoBundle'); $question->setautocompletervalues($bundles); $name = $helper->ask($input, $output, $question); Nascondere la risposta Si può anche fare una domanda e nascondere la risposta. Questo è particolarmente utile per le password: Listing - use Symfony\Component\Console\Question\Question; $question = new Question('Inserire una password'); $question->sethidden(true); $question->sethiddenfallback(false); $password = $helper->ask($input, $output, $question);. generated on August, 0 Chapter : Aiutante Question

82 Quando si usa una risposta nascosta, Symfony userà un binario, cambierà modalità stty o userà un altro trucco per nascodere la risposta. Se nessuno di questi è disponibile, si arrenderà e consentirà alla risposta di essere visibile, a meno che non si imposti questo comportamento a false, usando sethiddenfallback(), come nell'esempio precedente. In questo caso, sarà sollevata una``runtimeexception``. Validare la risposta Si può anche validare la risposta. Per esempio, nell'ultimo esempio è stato chiesto il nome di un bundle. Seguendo le convenzioni di Symfony, il nome dovrebbe avere il suffisso Bundle. Lo si può validare, usando il metodo setvalidator() : Listing - 0 use Symfony\Component\Console\Question\Question; $question = new Question('Si prega di inserire il nome del bundle', 'AcmeDemoBundle'); $question->setvalidator(function ($answer) if ('Bundle'!== substr($answer, -)) throw new \RuntimeException( 'Il nome del bundle deve terminare con \'Bundle\'' ); return $answer; ); $question->setmaxattempts(); $name = $helper->ask($input, $output, $question); Il parametro $validator è un callback, che gestisce la validazione. Dovrebbe lanciare un'eccezione se qualcosa va storto. Il messaggio dell'eccezione è mostrato nella console, quindi è una buona pratica inserirvi delle informazioni rilevanti. IL callback dovrebbe anche restituire il valore inserito dall'utente, in caso di successo. Si può impostare il numero massimo di volte in cui fare la domanda, con il metodo setmaxattempts(). Una volta raggiunto tale numero, sarà usato il valore predefinito. Usando null si indica che il numero di tentativi è infinito. L'utente vedrà la domanda finché inserisce una risposta non valida e potrà procedere solo in caso di risposta valida. Validare una risposta nascosta Si può anche usare un validatore con una risposta nascosta: Listing - use Symfony\Component\Console\Question\Question; $helper = $this->gethelper('question'); $question = new Question('Inserire la password'); $question->setvalidator(function ($value) if (trim($value) == ''). generated on August, 0 Chapter : Aiutante Question

83 0 throw new \Exception('La password non può essere vuota'); return $value; ); $question->sethidden(true); $question->setmaxattempts(0); $password = $helper->ask($input, $output, $question); Testare un comando con un input atteso Se si vuole scrivere un test per un comando che si aspetta un qualche tipo di input da linea di omando, occorre sovrascrivere HelperSet usato dal comando: Listing use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Tester\CommandTester; public function testexecute() $commandtester = new CommandTester($command); $helper = $command->gethelper('question'); $helper->setinputstream($this->getinputstream('test\\n')); // Equivale all'utente che inserisce "Test" e preme invio // Se serve una conferma, va bene "yes\n" $commandtester->execute(array('command' => $command->getname())); // $this->assertregexp('/.../', $commandtester->getdisplay()); protected function getinputstream($input) $stream = fopen('php://memory', 'r+', false); fputs($stream, $input); rewind($stream); return $stream; Impostando il flusso di input di QuestionHelper, si imita ciò che la console farebbe internamente con l'input dell'utente tramite cli. In questo modo, si può testare ogni interazione, anche complessa, passando un appropriato flusso di input. generated on August, 0 Chapter : Aiutante Question

84 Chapter Tabella New in version.: La classe Table è stata introdotta in Symfony. come rimpiazzo per l'aiutante Table. Può essere utile mostrare dati in tabella in un'applicazione di console: Listing ISBN Titolo Autore La divina commedia Dante Alighieri A Tale of Two Cities Charles Dickens The Lord of the Rings J. R. R. Tolkien And Then There Were None Agatha Christie Per mostrare una tabella, usare Table, impostare i titoli, impostare le righe e quindi rendere la tabella: Listing - 0 use Symfony\Component\Console\Helper\Table; $table = new Table($output); $table ->setheaders(array('isbn', 'Titolo', 'Autore')) ->setrows(array( array('--0-', 'La divina commedia', 'Dante Alighieri'), array('--00-0', 'A Tale of Two Cities', 'Charles Dickens'), array('0--0-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), array('0-0--', 'And Then There Were None', 'Agatha Christie'), )) ; $table->render(); Si può aggiumgere un separatore in un punto dell'output, passando un'istanza di TableSeparator come riga: Listing -. generated on August, 0 Chapter : Tabella

85 use Symfony\Component\Console\Helper\TableSeparator; $table->setrows(array( array('--0-', 'La divina commedia', 'Dante Alighieri'), array('--00-0', 'A Tale of Two Cities', 'Charles Dickens'), new TableSeparator(), array('0--0-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), array('0-0--', 'And Then There Were None', 'Agatha Christie'), )); Listing ISBN Titolo Autore La divina commedia Dante Alighieri A Tale of Two Cities Charles Dickens The Lord of the Rings J. R. R. Tolkien And Then There Were None Agatha Christie Si può cambiare stile, usando uno di quelli disponibili, tramite setstyle() : Listing - // lo stesso che non impostare niente $table->setstyle('default'); // cambia in stile compatto $table->setstyle('compact'); $table->render(); Il risultato è: Listing - ISBN Titolo Autore --0- La divina commedia Dante Alighieri A Tale of Two Cities Charles Dickens The Lord of the Rings J. R. R. Tolkien And Then There Were None Agatha Christie Si può anche impostare lo stile a borderless: Listing - $table->setstyle('borderless'); $table->render(); che mostra: Listing - =============== ========================== ================== ISBN Titolo Autore =============== ========================== ================== --0- La divina commedia Dante Alighieri A Tale of Two Cities Charles Dickens The Lord of the Rings J. R. R. Tolkien And Then There Were None Agatha Christie =============== ========================== ==================. generated on August, 0 Chapter : Tabella

86 Se nessuno degli stili predefiniti è adatto, se ne può definire uno nuovo: Listing - 0 use Symfony\Component\Console\Helper\TableStyle; // basato su "default", se non specificato diversamente $style = new TableStyle(); // personalizzare lo stile $style ->sethorizontalborderchar('<fg=magenta> </>') ->setverticalborderchar('<fg=magenta>-</>') ->setcrossingchar(' ') ; // usare lo stile $table->setstyle($style); Ecco una lista delle cose che si possono personalizzare: setpaddingchar() sethorizontalborderchar() setverticalborderchar() setcrossingchar() setcellheaderformat() setcellrowformat() setborderformat() 0 setpadtype() Si può anche registrare uno stile globalmente: Listing -0 // registra lo stile con nome "colorato" Table::setStyleDefinition('colorato', $style); // usa lo stile $table->setstyle('colorato'); Si può usare lo steso metodo per ridefinire uno degli stili predefiniti generated on August, 0 Chapter : Tabella

87 Chapter Aiutante Table New in version.: L'aiutante table è stato introdotto in Symfony.. L'aiutante Table è stato deprecato in Symfony. e sarà rimosso in Symfony.0. Si dovrebbe usare ora la classe Table, che è più potente. Quando si costruisce un'applicazione in console, può essere utile mostrare dati tabulari: Per mostrare una tabella, usare TableHelper, impostando testata e righe, e rendere: Listing - 0 $table = $this->gethelper('table'); $table ->setheaders(array('isbn', 'Titolo', 'Autore')) ->setrows(array( array('--0-', 'La divina commedia', 'Dante Alighieri'), array('--00-0', 'Racconto di due città', 'Charles Dickens'), array('0--0-0', 'Il signore degli anelli', 'J. R. R. Tolkien'), array('0-0--', 'Dieci piccoli indiani', 'Agatha Christie'), )) ; $table->render($output); Si può personalizzare anche il formato della tabella. Ci sono due modi per personalizzare la resa della tabella: con nomi di formato o personalizzando le opzioni di resa.. generated on August, 0 Chapter : Aiutante Table

88 Personalizzare il formato della tabella con i nomi di formati New in version.: Il formato TableHelper::LAYOUT_COMPACT è stato introdotto in Symfony.. L'aiutante Table dispone di due formati di tabella preconfigurati: TableHelper::LAYOUT_DEFAULT TableHelper::LAYOUT_BORDERLESS TableHelper::LAYOUT_COMPACT Si può impostare il formato con il metodo setlayout(). Personalizzare il formato della tabella con le opzioni di resa Si può anche controllare la resa della tabella impostando i valori delle opzioni di resa: setpaddingchar() sethorizontalborderchar() setverticalborderchar() setcrossingchar() setcellheaderformat() setcellrowformat() setborderformat() setpadtype() generated on August, 0 Chapter : Aiutante Table

89 generated on August, 0 Chapter : Aiutante Table

90 Chapter Il componente CssSelector Il componente CssSelector converte selettori CSS in espressioni XPath. Installazione Si può installare il componente in due modi: Installarlo via Composer (symfony/css-selector su Packagist ); Usare il repository ufficiale su Git (https://github.com/symfony/cssselector ). Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. Uso Perché usare selettori CSS? Quando si analizzano documenti HTML o XML, XPath è certamente il metodo più potente. Le espressioni XPath sono incredibilmente flessibili, quindi c'è quasi sempre un'espressione XPath che troverò l'elemento richiesto. Sfortunatamente, possono essere anche molto complicate e la curva di apprendimento è ripida. Anche operazioni comuni (come trovare un elemento con una data classe) possono richiedere espressioni lunghe e poco maneggevoli. Molti sviluppatori, in particolare gli sviluppatori web, si trovano più a loro agio nel cercare elementi tramite selettori CSS. Oltre a funzionare con i fogli di stile, i selettori CSS sono usati da Javascript con la funzione queryselectorall e da famose librerie Javascript, come jquery, Prototype e MooTools.. https://packagist.org/packages/symfony/css-selector. https://github.com/symfony/cssselector generated on August, 0 Chapter : Il componente CssSelector 0

91 I selettori CSS sono meno potenti di XPath, ma molto più facili da scrivere, leggere e capire. Essendo meno potenti, quasi tutti i selettori CSS possono essere convertiti in equivalenti XPath. Queste espressioni XPath possono quindi essere usate con altre funzioni e classi che usano XPath per trovare elementi in un documento. Il componente CssSelector L'unico fine del componente è la conversione di selettori CSS nei loro equivalenti XPath: Listing - use Symfony\Component\CssSelector\CssSelector; print CssSelector::toXPath('div.item > h > a'); Questo fornisce il seguente output: Listing - descendant-or-self::div[contains(concat(' ' '), ' item ')]/h/a Si può usare questa espressione, per esempio, con DOMXPath o SimpleXMLElement, per trovare elementi in un documento. Il metodo Crawler::filter() usa il componente CssSelector per trovare elementi in base a un selettore CSS. Si veda Il componente DomCrawler per ulteriori dettagli. Limiti del componente CssSelector Non tutti i selettori CSS possono essere convertiti in equivalenti XPath. Ci sono molti selettori CSS che hanno senso solo nel contesto di un browser. selettori dei collegamenti: :link, :visited, :target selettori basati su azioni dell'utente: :hover, :focus, :active selettori dello stato dell'interfaccia: :enabled, :disabled, :indeterminate (tuttavia, :checked e :unchecked sono disponibili) Gli pseudo-elementi (:before, :after, :first-line, :first-letter) non sono supportati, perché selezionano porzioni di testo, piuttosto che elementi. Diverse pseudo-classi non sono ancora supportate: *:first-of-type, *:last-of-type, *:nth-of-type, *:nth-last-of-type, *:only-oftype. (funzionano con il nome di un elemento (p.e. li:first-of-type) ma non con *.. generated on August, 0 Chapter : Il componente CssSelector

92 Chapter Il componente Debug Il componente Debug fornisce strumenti per facilitare il debug del codice PHP New in version.: Il componente Debug è nuovo in Symfony.. In precedenza, le classi si trovavano nel componente HttpKernel. Installazione Si può installare il componente in due modi: Usando il repository ufficiale su Git (https://github.com/symfony/debug ); Installandolo tramite Composer (symfony/debug su Packagist ). Uso Il componente Debug fornisce diversi strumenti che aiutano nel debug del codice PHP. Per abilitarlo, bastano poche istruzioni: Listing - use Symfony\Component\Debug\Debug; Debug::enable(); Il metodo enable() registra un gestore di errori e un gestore di eccezioni e uno speciale caricatore di classi. Leggere le sezioni seguenti per maggiori informazioni sui vari strumenti a disposizione.. https://github.com/symfony/debug. https://packagist.org/packages/symfony/debug. generated on August, 0 Chapter : Il componente Debug

93 Non si dovrebbero mai abilitare gli strumenti di debug in ambiente di produzione, perché potrebbero rivelare informazioni sensibili agli utenti. Abilitare il gestore di errori La classe ErrorHandler cattura gli errori PHP e li converte in eccezioni (di classe ErrorException o FatalErrorException per errori fatali PHP): Listing - use Symfony\Component\Debug\ErrorHandler; ErrorHandler::register(); Abilitare il gestore di eccezioni La classe ExceptionHandler cattura le eccezioni PHP non catturate e le converte in una risposta PHP. È utile in modalità di debug, per sostituire l'output predefinito di XDebug con qualcosa di più elegante e utile: Listing - use Symfony\Component\Debug\ExceptionHandler; ExceptionHandler::register(); Se è disponibile il componente HttpFoundation, il gestore usa un oggetto Response di Symfony. Altrimenti, si accontenta di una semplice risposta PHP generated on August, 0 Chapter : Il componente Debug

94 Chapter Debug di ClassLoader DebugClassLoader tenta di lanciare eccezioni più utili, quando gli autoloader registrati non trovano una classe. Tutti gli autoloader che implementano un metodo findfile() vengono sostituiti da un DebugClassLoader. L'uso di DebugClassLoader è facile, basta richiamare il metodo statico enable() : Listing - use Symfony\Component\Debug\DebugClassLoader; DebugClassLoader::enable();. generated on August, 0 Chapter : Debug di ClassLoader

95 Chapter 0 Il componente DependencyInjection Il componente DependencyInjection consente di standardizzare e centralizzare il modo in cui gli oggetti sono costruiti nelle applicazioni. Per un'introduzione alla dependency injection e ai contenitori di servizi, vedere Contenitore di servizi Installazione È possibile installare il componente in due modi: Installandolo via Composer (symfony/dependency-injection su Packagist ); Utilizzando il repository ufficiale su Git (https://github.com/symfony/dependencyinjection ). Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. Utilizzo Si potrebbe avere una semplice classe, come la seguente Mailer, che si vuole rendere disponibile come servizio: Listing 0- class Mailer private $transport; public function construct(). https://packagist.org/packages/symfony/dependency-injection. https://github.com/symfony/dependencyinjection generated on August, 0 Chapter 0: Il componente DependencyInjection

96 0 $this->transport = 'sendmail'; La si può registrare nel contenitore come servizio: Listing 0- use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container->register('mailer', 'Mailer'); Si potrebbe migliorare la classe, per renderla più flessibile, consentendo al contenitore di impostare il trasporto usato. Si può cambiare la classe, in modo che il trasporto sia passato al costruttore: Listing 0-0 class Mailer private $transport; public function construct($transport) $this->transport = $transport; Si può quindi impostare la scelta del trasporto nel contenitore: Listing 0- use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container ->register('mailer', 'Mailer') ->addargument('sendmail'); La classe ora è molto più flessibile, perché la scelta del trasporto è stata separata dall'implementazione e posta nel contenitore. La scelta del trasporto potrebbe interessare anche altri servizi. Si può evitare di doverlo cambiare in posti differenti, trasformandolo in un parametro nel contenitore e facendo riferimento a tale parametro per il costruttore del servizio Mailer: Listing 0- use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container->setparameter('mailer.transport', 'sendmail'); $container ->register('mailer', 'Mailer') ->addargument('%mailer.transport%'); Ora che il servizio mailer è nel contenitore, lo si può iniettare come dipendenza di altre classi. Se si ha una classe NewsletterManager come questa: Listing 0- generated on August, 0 Chapter 0: Il componente DependencyInjection

97 0 class NewsletterManager private $mailer; public function construct(\mailer $mailer) $this->mailer = $mailer; Allora la si può registrare come servizio e passarle il servizio mailer: Listing 0-0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; $container = new ContainerBuilder(); $container->setparameter('mailer.transport', 'sendmail'); $container ->register('mailer', 'Mailer') ->addargument('%mailer.transport%'); $container ->register('newsletter_manager', 'NewsletterManager') ->addargument(new Reference('mailer')); Se NewsletterManager non richiedesse Mailer e l'iniezione fosse quindi solamente opzionale, la si potrebbe passare usando un setter: Listing 0-0 class NewsletterManager private $mailer; public function setmailer(\mailer $mailer) $this->mailer = $mailer; Ora si può scegliere di non iniettare un Mailer dentro NewsletterManager. Se comunque lo si volesse fare, il contenitore può richiamare il metodo setter: Listing 0-0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; $container = new ContainerBuilder(); $container->setparameter('mailer.transport', 'sendmail'); $container ->register('mailer', 'Mailer') ->addargument('%mailer.transport%'); generated on August, 0 Chapter 0: Il componente DependencyInjection

98 $container ->register('newsletter_manager', 'NewsletterManager') ->addmethodcall('setmailer', new Reference('mailer')); Si può quindi ottenere il servizio newsletter_manager dal contenitore, in questo modo: Listing 0-0 use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $newslettermanager = $container->get('newsletter_manager'); Evitare che il codice dipenda dal contenitore Sebbene si possano recuperare servizi direttamente dal contenitore, sarebbe meglio minimizzarlo. Per esempio, in NewsletterManager abbiamo iniettato il servizio mailer, piuttosto che richiederlo al contenitore. Avremo potuto iniettare il contenitore e recuperare da esso il servizio mailer, ma allora sarebbe stato legato a questo particolare contenitore, rendendo difficile riusare la classe altrove. A un certo punto si avrà la necessità di ottenere un servizio dal contenitore, ma lo si dovrebbe fare il meno possibile e all'inizio dell'applicazione. Impostare il contenitore con file di configurazione Oltre a impostare servizi usando PHP, come sopra, si possono usare dei file di configurazione. Ciò consente di usare XML o Yaml per scrivere definizioni per i servizi, invece di usare PHP per definire i servizi, come visto negli esempi precedenti. In applicazioni che non siano piccole ha senso organizzare le definizioni dei servizi, spostandole in più file di configurazione. Per poterlo fare, occorre installare anche il componente Config: Caricare un file di configurazione xml: Listing 0- use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; $container = new ContainerBuilder(); $loader = new XmlFileLoader($container, new FileLocator( DIR )); $loader->load('services.xml'); Caricare un file di configurazione yaml: Listing 0- use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; $container = new ContainerBuilder(); $loader = new YamlFileLoader($container, new FileLocator( DIR )); $loader->load('services.yml'); generated on August, 0 Chapter 0: Il componente DependencyInjection

99 Se si vogliono caricare file di configurazione YAML, occorrerà installare anche il componente YAML. Se si vuole usare PHP per creare i servizi, si possono spostare in un file di configurazione separato e caricarlo in modo simile: Listing 0- use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; $container = new ContainerBuilder(); $loader = new PhpFileLoader($container, new FileLocator( DIR )); $loader->load('services.php'); I servizi newsletter_manager e mailer possono ora essere impostati da file di configurazione: Listing 0-0 parameters: #... mailer.transport: sendmail services: mailer: class: Mailer arguments: ["%mailer.transport%"] newsletter_manager: class: NewsletterManager calls: - [setmailer, generated on August, 0 Chapter 0: Il componente DependencyInjection

100 Chapter Tipi di iniezione Esplicitare le dipendenze di una classe e richiedere che vi siano iniettate è un buono modo per rendere una classe più usabile, testabile e disaccoppiata da altre. Ci sono molti modi per iniettare le dipendenze. Ogni tipo di iniezione ha vantaggi e svantaggi da considerare, così come diversi modi di funzionare nell'uso con il contenitore di servizi. Iniezione nel costruttore Il modo più comune per iniettare dipendenze è tramite il costruttore di una classe. Per poterlo fare, occorre aggiungere un parametro alla firma del costruttore, in modo che accetti la dipendenza: Listing - 0 class NewsletterManager protected $mailer; public function construct(\mailer $mailer) $this->mailer = $mailer; Si può specificare quale servizio si vuole iniettare, tramite la configurazione del contenitore di servizi: Listing - services: mio_mailer: #... newsletter_manager: class: NewsletterManager arguments: generated on August, 0 Chapter : Tipi di iniezione 00

101 Forzare il tipo di un oggetto iniettato implica la certezza che venga iniettata una dipendenza effettivamente utilizzabile. Forzando il tipo, si otterrà subito un errore chiaro, nel caso in cui venga iniettata una dipendenza inadatta. Forzando il tipo con un'interfaccia invece che con una classe, si può rendere più flessibile la scelta della dipendenza. Ipotizzando di usare solo i metodi definiti nell'interfaccia, si può guadagnare flessibilità e mantenere la sicurezza. Ci sono diversi vantaggi nell'uso dell'iniezione nel costruttore: Se la dipendenza è un requisito e la classe non funziona senza di essa, l'iniezione tramite il costruttore assicura che sia presente all'uso della classe, poiché la classe non può essere istanziata senza. Il costruttore è richiamato solo una volta, alla creazione dell'oggetto, quindi si può essere sicuri che la dipendenza non cambierà durante il ciclo di vita dell'oggetto. Tali vantaggi implicano che l'iniezione nel costruttore non è adatta per le dipendenze facoltative. Inoltre è più difficile da usare in combinazione con le gerarchie di classi: se una classe usa l'iniezione nel costruttore, estenderla e sovrascrivere il costruttore diventa problematico. Iniezione da setter Un altro possibile punto di iniezione in una classe è l'aggiunta di un metodo setter, che accetti la dipendenza: Listing - 0 class NewsletterManager protected $mailer; public function setmailer(\mailer $mailer) $this->mailer = $mailer; Listing - services: mio_mailer: #... newsletter_manager: class: NewsletterManager calls: - [setmailer, Questa volta i vantaggi sono: l'iniezione da setter funziona bene con le dipendenza facoltative. Se non si ha bisogno della dipendenza, basta non richiamare il setter. Si può richiamare il setter più volte. Questo è particolarmente utile se il metodo aggiunge la dipendenza a un insieme. Si può quindi avere un numero variabile di dipendenze. Gli svantaggi dell'iniezione da setter sono: Il setter può essere richiamato più volte, non solo all'istanza dell'oggetto, quindi non si può essere sicuri che la dipendenza non sia rimpiazzata durante il ciclo di vita dell'oggetto (tranne se si scrive esplicitamente il metodo setter per verificare se non sia stato già richiamato). generated on August, 0 Chapter : Tipi di iniezione 0

102 Non si può essere sicuri che il setter sia richiamato, quindi occorre verificare che ogni dipendenza obbligatoria sia iniettata. Iniezione di proprietà Un'altra possibilità consiste nell'impostare direttamente campi pubblici della classe: Listing - class NewsletterManager public $mailer; Listing - services: mio_mailer: #... newsletter_manager: class: NewsletterManager properties: mailer: Ci sono principalmente solo svantaggi nell'uso dell'iniezione di proprietà, che è simile a quella da setter, ma con importanti problemi ulteriori: Non si può in alcun modo controllare quando la dipendenza viene impostata, potrebbe essere modificata in qualsiasi punto del ciclo di vita dell'oggetto. Non si può forzare il tipo, quindi non si può essere sicuri di quale dipendenza sia iniettata, a meno di non scrivere nella classe esplicitamente di testare l'istanza della classe prima del suo uso. Tuttavia, è utile conoscere questa possibilità del contenitore di servizi, specialmente se si lavora con codice fuori dal proprio controllo, come in librerie di terze parti, che usino proprietà pubbliche per le proprie dipendenze. generated on August, 0 Chapter : Tipi di iniezione 0

103 Chapter Introduzione ai parametri Si possono definire, nel contenitore di servizi, parametri che possono essere usati direttamente o come parte di definizioni di servizi. Questo è di aiuto per separare valori che si vorranno cambiare più spesso. Recuperare e impostare parametri del contenitore È facile lavorare coi parametri del contenitore, usando i metodi di accesso del contenitore per i parametri. Si può verificare se un parametro sia stato definito nel contenitore con: Listing - $container->hasparameter('mailer.transport'); Si può recuperare un parametro impostato nel contenitore con: Listing - $container->getparameter('mailer.transport'); e impostare un parametro nel contenitore con: Listing - $container->setparameter('mailer.transport', 'sendmail'); La notazione. usata è solo una convenzione di Symfony per rendere più leggibili i parametri. I parametri sono solo elementi chiave-valore e non possono essere organizzati in array. Si può impostare un parametro solo prima che il contenitore sia compilato. Per saperne di più sulla compilazione del contenitore, vedere Compilazione del contenitore. generated on August, 0 Chapter : Introduzione ai parametri 0

104 Parametri nei file di configurazione Si può anche usare la sezione parameters di un file di configurazione per impostare parametri: Listing - parameters: mailer.transport: sendmail Come per il recupero diretto dei valori dei parametri dal contenitore, si può anche usarli nei file di configurazione. Si può fare riferimento ai parametri da altrove, inserendoli tra simboli di percentuale (%), p.e. %mailer.transport%. Un possibile uso è iniettare i valori nei servizi. Questo consente di configurare varie versioni dei servizi tra le applicazioni o vari servizi basati sulle stesse classi ma configurati diversamente, in una singola applicazione. Si può iniettare la scelta del trasporto dell' nella classe Mailer direttamente, ma rendendola un parametro. Questo rende più facile cambiarla, rispetto ad averla legata e nascosta nella definizione del servizio: Listing - parameters: mailer.transport: sendmail services: mailer: class: Mailer arguments: ['%mailer.transport%'] Gli spazi nei valori tra i tag parameter nei file di configurazione XML non sono eliminati. Questo vuol dire che il seguente pezzo di configurazione avrà come valore \n sendmail\n: Listing - <parameter key="mailer.transport"> sendmail </parameter> In alcuni casi (per costanti o nomi di classi), ciò potrebbe causare errori. Per evitarlo, usare sempre una singola riga per i parametri, come segue: Listing - <parameter key="mailer.transport">sendmail</parameter> In caso di uso altrove, occorre cambiare il parametro in un unico posto, se necessario. Si può anche usare i parametri nella definizione dei servizi, per esempio, rendendo un parametro la classe di un servizio: Listing - parameters: mailer.transport: sendmail mailer.class: Mailer services: mailer: class: "%mailer.class%" arguments: ["%mailer.transport%"] generated on August, 0 Chapter : Introduzione ai parametri 0

105 Il simbolo di percentuale dentro a un parametro o argomento, come parte della stringa, deve subire un escape con un ulteriore simbolo di percentuale: Listing - arguments: ["http://symfony.com/?foo=%%s&bar=%%d"] Parametri array I parametri non devono necessariamente essere semplici stringhe, possono anche essere array. Per il formato XML, occorre usare l'attributo type="collection" per tutti i parametri che sono array. Listing -0 0 parameters: my_mailer.gateways: - mail - mail - mail my_multilang.language_fallback: en: - en - fr fr: - fr - en Costanti come parametri Il contenitore supporta anche l'impostazione di costanti PHP come parametri. Per sfruttare questa caratteristica, mappare il nome della costante a un parametro e definire il tipo come constant. Listing - 0 <?xml version=".0" encoding="utf-"?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w.org/00/xmlschema-instance" xsi:schemalocation="http://symfony.com/schema/dic/services dic/services/services-.0.xsd"> <parameters> <parameter key="global.constant.value" type="constant">costante_globale</parameter> <parameter key="my_class.constant.value" type="constant">mia_classe::nome_costante</parameter> </parameters> </container> Questo non funziona per configurazioni YAML. Se si usa YAML, si può importare un file XML per sfruttare tale funzionalità: Listing - imports: - resource: parameters.xml generated on August, 0 Chapter : Introduzione ai parametri 0

106 Parole chiave di PHP in XML Per impostazione predefinita, true, false e null in XML sono convertiti in parole chiave di PHP (rispettivamente true, false e null): Listing - <parameters> <parameter key="mailer.send_all_in_once">false</parameter> </parameters> <!-- dopo l'analisi $container->getparameter('mailer.send_all_in_once'); // restituisce false --> Per evitare questo comportamento, usare il tipo string: Listing - <parameters> <parameter key="mailer.some_parameter" type="string">true</parameter> </parameters> <!-- dopo l'analisi $container->getparameter('mailer.some_parameter'); // restituisce "true" --> Non disponibile per Yaml e PHP, che hanno già un supporto nativo per le parole chiave di PHP. Sintassi nel riferimento a servizi Si può fare riferimento a servizi, in modo leggermente diverso nei vari formati. Si può configurare il comportamento in caso di non esistenza del servizio a cui si fa riferimento. Il comportamento predefinito è lanciare un eccezione. YAML Aggiungere a inizio stringa per fare riferimento a un servizio in fa riferimento al servizio mailer. Se il servizio non esiste, lancia fa riferimento al servizio mailer. Se il servizio non esiste, sarà ignorato; Listing - parameters: # se il servizio 'my_mailer' non è definito, sarà sollevata un'eccezione # se il servizio 'my_logger' non è definito, 'bar' sarà nullo Usare per l'escape del in Yaml. sarà convertito nella stringa invece di fare riferimento al servizio mailer. generated on August, 0 Chapter : Introduzione ai parametri 0

107 XML In XML, usare il tipo service. Il comportamento in caso di servizio non esistente può essere definito usando il parametro on-invalid. Il comportamento predefinito è lanciare un'eccezione. Valori accettabili per on-invalid sono null (usa null al posto del servizio mancante) o ignored (molto simile, tranne che, se usato su una chiamata a metodo, la chiamata viene rimossa). Listing - <parameters> <!-- se il servizio 'my_mailer' non è definito, sarà sollevata un'eccezione --> <parameter key="foo" type="service" id="my_mailer" /> <!-- se il servizio 'my_logger' non è definito, 'bar' sarà nullo --> <parameter key="bar" type="service" id="my_logger" on-invalid="null" /> </parameters> PHP In PHP, si può usare la classe Reference per fare riferimento a un servizio. Il comportamento invalido si configura usando il secondo parametro del costruttore e le costenati di ContainerInterface. Listing - use Symfony\Component\DependencyInjection\Reference; // se il servizio 'my_mailer' non è definito, sarà sollevata un'eccezione $container->setparameter('foo', new Reference('my_mailer')); // se il servizio 'my_logger' non è definito, 'bar' sarà nullo $container->setparameter('bar', new Reference('my_logger', ContainerInterface::NULL_ON_INVALID_REFERENCE ));. generated on August, 0 Chapter : Introduzione ai parametri 0

108 Chapter Lavorare con parametri e definizioni del contenitore Ottenere e impostare parametri del contenitore Ci sono alcuni metodi utili per lavorare con le definizioni dei servizi. Per trovare se ci sia una definizione per un id di un servizio: Listing - $container->hasdefinition($idservizio); Questo è utile se si vuole far qualcosa solo se una particolare definizione esiste. Si possono recuperare una definizione con: Listing - $container->getdefinition($idservizio); oppure: Listing - $container->finddefinition($idservizio); che, diversamente da getdefinition(), risolve anche gli alias, in modo che, se $idservizio è un alias, si otterrà la definizione sottostante. Le definizioni stesse dei servizi sono oggetti, quindi se si recupera una definizione con tali metodi e li si modifica, tali modifiche si rifletteranno nel contenitore. Se, tuttavia, si crea una nuova definizione, la si può aggiungere al contenitore, usando: Listing - $container->setdefinition($id, $definizione); generated on August, 0 Chapter : Lavorare con parametri e definizioni del contenitore 0

109 Lavorare con una definizione Creare una nuova definizione Se occorre creare una nuova definizione, piuttosto che manipolarne una recuperata dal contenitore, alla la classe della definizione è Definition. Classe Quella vista sopra è la classe di una definizione, questa è la classe dell'oggetto restituito quando si richiede un servizio dal contenitore. Per trovare quale classe sia impostata per una definizione: Listing - $definition->getclass(); e per impostare una classe diversa: Listing - $definition->setclass($classe); // Il nome pienamente qualificato di una classe, come stringa Parametri del costruttore Per ottenere un array di parametri del costruttore per una definizione, si può usare: Listing - $definition->getarguments(); oppure si può ottenere un singolo parametro, in base alla sua posizione: Listing - $definition->getargument($indice); // p.e. $definition->getarguments(0) per il primo parametro Si può aggiungere un nuovo parametro alla fine dell'array dei parametri, usando: Listing - $definition->addargument($parametro); Il parametro può essere una stringa, un array, il parametro di un servizio, se si usa %paramater_name%, oppure l'id di un servizio, se si usa: Listing -0 use Symfony\Component\DependencyInjection\Reference; $definition->addargument(new Reference('id_servizio')); In modo simile, si può sostituire un parametro già impostato, usando: Listing - $definition->replaceargument($indice, $parametro); Si possono anche sostituire tutti i parametri (o impostarne alcuni, se non ce ne sono) con un array di parametri:. generated on August, 0 Chapter : Lavorare con parametri e definizioni del contenitore 0

110 Listing - $definition->replacearguments($parametri); Chiamate a metodi Se il servizio con cui si sta lavorando usa l'iniezione per setter, si può manipolare qualsiasi chiamata a metodi nelle definizioni. Si può ottenere un array di tutte le chiamate a metodi, con: Listing - $definition->getmethodcalls(); E la chiamata a un metodo, con: Listing - $definition->addmethodcall($metodo, $parametri); Dove $metodo è il nome del metodo e $parametri è un array dei parametri con cui richiamare il metodo. I parametri possono essere stringhe, array, parametri o id di servizi, come per i parametri del costruttore. Si possono anche sostituire le chiamate a metodi esistenti con un array di nuove, con: Listing - $definition->setmethodcalls($chiamate); Ci sono ulteriori esempi di modi specifici di lavorare con le definizioni nei blocchi di codice PHP degli esempi di configurazione nelle pagine come Usare un factory per creare servizi e Come gestire le dipendenze comuni con servizi genitori. I metodi visti qui che cambiano definizione dei servizi possono essere usati solo prima che il contenitore sia compilato: una volta che il contenitore è compilato, non si possono manipolare ultetiormente le definizioni dei servizi. Per saperne di più sulla compilazione del contenitore, vedere Compilazione del contenitore. generated on August, 0 Chapter : Lavorare con parametri e definizioni del contenitore 0

111 Chapter Compilazione del contenitore Ci sono diverse ragioni per compilare il contenitore di servizi. Tra queste, poter verificare potenziali problemi, come i riferimenti circolari, e rendere il contenitore più efficiente, risolvendo i parametri e rimuovendo i servizi inutilizzati. Inoltre, alcune caratteristiche, come l'uso di servizi genitori, necessitano di un contenitore compilato. La compilazione avviene eseguendo: Listing - $container->compile(); Il metodo compile usa dei passi di compilatore per la compilazione. Il componente DependencyInjection dispone di diversi passi, registrati automaticamente per la compilazione. Per esempio, CheckDefinitionValidityPass verifica diversi problemi potenziali con le definizioni impostate nel contenitore. Dopo questo e molti altri passi, che verificano la validità del contenitore, ulteriori passi sono usati per ottimizzare la configurazione, prima che sia messa in cache. Per esempio, vengono rimossi i servizi privati e i servizi astratti, e vengono risolti gli alias. Gestire la configurazione con le estensioni Oltre a caricare la configurazione direttamente nel contenitore, come mostrato in Il componente DependencyInjection, la si può gestire registrando estensioni con il contenitore. Il primo passo nel processo di compilazione consiste nel caricare la configurazione da un classe estensione, registrata con il contenitore. Diversamente dal caricamento diretto della configurazione, sono processate solo quando il contenitore viene compilato. Se l'applicazione è modulare, le estensioni consentono a ciascun modulo di registrare e gestire la propria configurazione dei servizi. Le estensioni devono implementare ExtensionInterface e possono essere registrare con il contenitore in questo modo: Listing - $container->registerextension($extension);. generated on August, 0 Chapter : Compilazione del contenitore

112 Il lavoro principale dell'estensione viene eseguito nel metodo load. In questo metodo si possono caricare configurazioni da uno o più file, oltre che manipolare le definizioni del contenitore, usando i metodi mostrati in Lavorare con parametri e definizioni del contenitore. Al metodo load viene passato un nuovo contenitore da configurare, il quale viene poi fuso nel contenitore con cui è registrato. Questo consente di avere diverse estensioni, che gestiscono le definizioni in modo indipendente. Quando vengono aggiunte, le estensioni non aggiungono configurazioni al contenitore, ma sono processato quando viene richiamato il metodo compile del contenitore. Un'estensione molto semplice potrebbe solo caricare file di configurazione nel contenitore: Listing - 0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\Config\FileLocator; class AcmeDemoExtension implements ExtensionInterface public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader( $container, new FileLocator( DIR.'/../Resources/config') ); $loader->load('services.xml'); Questo non fornisce molti vantaggi, rispetto a caricare il file direttamente nel contenitore generale in via di costruzione. Consente solo ai file di essere suddivisi tra i moduli/bundle. La possibilità di influenzare la configurazione di un modulo dai file di configurazione esterni al modulo/bundle è necessaria per rendere configurabile un'applicazione complessa. Lo si può fare specificando che delle sezioni dei file di configurazione caricati direttamente nel contenitore appartengono a una particolare estensione. Tali sezioni non saranno processate direttamente dal contenitore, ma dall'estensione relativa. L'estensione deve specificare un metodo getalias, per implementare l'interfaccia: Listing - 0 class AcmeDemoExtension implements ExtensionInterface public function getalias() return 'acme_demo'; Per i file di configurazione YAML, specificare l'alias per l'estensione come chiave vorrà dire che tali valori sono passati al metodo load dell'estensione: Listing - #... acme_demo: pippo: valoredipippo pluto: valoredipluto generated on August, 0 Chapter : Compilazione del contenitore

113 Se questo file viene caricato nella configurazione, i valori in esso sono processati solo quando il contenitore viene compilato nel punto in cui viene caricata l'estensione: Listing - 0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; $container = new ContainerBuilder(); $container->registerextension(new AcmeDemoExtension); $loader = new YamlFileLoader($container, new FileLocator( DIR )); $loader->load('config.yml'); $container->compile(); Quando si carica un file di configurazione che usa un alias di estensione come chiave, l'estensione deve essere già stata registrata nel costruttore di contenitore o verrà sollevata un'eccezione. I valori di tali sezioni dei file di configurazione sono passati al primo parametro del metodo load dell'estensione: Listing - public function load(array $configs, ContainerBuilder $container) $foo = $configs[0]['pippo']; //valoredipippo $bar = $configs[0]['pluto']; //valoredipluto Il parametro $configs è un array contenente ogni diverso file di configurazione caricato nel contenitore. Nell'esempio precedente viene caricato solo un unico file di configurazione, ma sarà comunque dentro un array. L'array sarà simile a questo: Listing - array( array( 'pippo' => 'valoredipippo', 'pluto' => 'valoredipluto', ), ) Sebbene sia possibile gestire manualmente la fusione dei vari file, è molto meglio usare il componente Config per fondere e validare i valori di configurazione. Usando il processo di configurazione si può accedere ai valori di configurazione in questo modo: Listing - 0 use Symfony\Component\Config\Definition\Processor; public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $processor = new Processor(); $config = $processor->processconfiguration($configuration, $configs); $foo = $config['pippo']; //valoredipippo $bar = $config['pluto']; //valoredipluto generated on August, 0 Chapter : Compilazione del contenitore

114 Ci sono altri due metodi da implementare. Uno per restituire lo spazio dei nomi XML, in modo che le parti rilevanti di un file di configurazione XML siano passate all'estensione. L'altro per specificare il percorso di base ai file XSD per validare la configurazione XML: Listing -0 public function getxsdvalidationbasepath() return DIR.'/../Resources/config/'; public function getnamespace() return 'http://www.example.com/symfony/schema/'; La validazione XSD è facoltativa, restituendo false dal metodo getxsdvalidationbasepath sarà disabilitata. La versione XML della configurazione sarà dunque simile a questa: Listing - 0 <?xml version=".0"?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w.org/00/xmlschema-instance" xmlns:acme_demo="http://www.example.com/symfony/schema/" xsi:schemalocation="http://www.example.com/symfony/schema/ symfony/schema/hello-.0.xsd"> <acme_demo:config> <acme_demo:pippo>valoredipippo</acme_hello:foo> <acme_demo:pluto>valoredipluto</acme_demo:bar> </acme_demo:config> </container> Nel framework Symfony c'è una classe base Extension, che implementa questi metodi e un metodo scorciatoia per processare la configurazione. Vedere Caricare la configurazione di un servizio in un bundle per maggiori dettagli. Il valore di configurazione processato ora può essere aggiunto come parametro del contenitore, come se fosse elencato nella sezione parameters del config, ma con il beneficio aggiuntivo di fondere file diversi e della validazione della configurazione: Listing - public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $processor = new Processor(); $config = $processor->processconfiguration($configuration, $configs); $container->setparameter('acme_demo.pippo', $config['pippo']) generated on August, 0 Chapter : Compilazione del contenitore

115 0 Si possono stabilire requisiti di configurazione più complessi nelle classi estensione. Per esempio, si può scegliere di caricare un file di configurazione principale, ma anche di carne uno secondario solo se un certo parametro è impostato: Listing - 0 public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $processor = new Processor(); $config = $processor->processconfiguration($configuration, $configs); $loader = new XmlFileLoader( $container, new FileLocator( DIR.'/../Resources/config') ); $loader->load('services.xml'); if ($config['advanced']) $loader->load('advanced.xml'); La registrazione di un'estensione nel contenitore non è sufficiente per includerla tra le estensioni processate durante la compilazione del contenitore. Caricare la configurazione che usa l'alias dell'estensione come chiave, come mostrato in precedenza, assicurerà il suo caricamento. Si può anche dire al costruttore di contenitore di caricarla, usando il metodo loadfromextension() : Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $extension = new AcmeDemoExtension(); $container->registerextension($extension); $container->loadfromextension($extension->getalias()); $container->compile(); Se si deve manipolare la configurazione caricata da un'estensione, non lo si può fare da un'altra estensione, perché usa un contenitore nuovo. Invece, si deve usare un passo di compilatore, che funziona con il contenitore dopo che le estensioni sono state processate. Prependere la configurazione passata all'estensione Una Extension può prependere la configurazione di un altro bundle, prima della chiamata al metodo load(), implementando PrependExtensionInterface : Listing -. generated on August, 0 Chapter : Compilazione del contenitore

116 0 use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; class AcmeDemoExtension implements ExtensionInterface, PrependExtensionInterface public function prepend() $container->prependextensionconfig($name, $config); Per maggiori dettagli, si veda Semplificare la configurazione di più bundle, che è specifica del framework Symfony, ma contiene più informazioni su questa caratteristica. Creare un passo di compilatore Si possono anche creare e registrare i propri passi di compilatore con il contenitore. Per creare un passo di compilatore, si deve implementare l'interfaccia CompilerPassInterface. Il compilatore offre la possibilità di manipolare le definizioni del servizio che sono state compilate. Questo può essere molto potente, ma non necessario nell'uso quotidiano. Il passo di compilatore deve avere il metodo process, che viene passato al contenitore che si sta compilando: Listing - 0 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; class CustomCompilerPass implements CompilerPassInterface public function process(containerbuilder $container) Si possono manipolare parametri e definizioni del contenitore, usando i metodi descritti in Lavorare con parametri e definizioni del contenitore. Un cosa che si fa solitamente in un passo di compilatore è la ricerca di tutti i servizi con determinato tag, in modo da poterli processare in qualche modo o collegarli dinamicamente in qualche altro servizio. Registrare un passo di compilatore Occorre registrare il proprio passo di compilatore con il contenitore. Il suo metodo process sarà richiamato quando il contenitore viene compilato: Listing -. generated on August, 0 Chapter : Compilazione del contenitore

117 use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container->addcompilerpass(new CustomCompilerPass); I passi di compilatore sono registrati in modo diverso, se si usa il framework completo, si veda Lavorare con i passi di compilatore nei bundle per maggiori dettagli. Controllare l'ordine dei passi I passi di compilatore predefiniti sono raggruppati in passi di ottimizzazione e passi di rimozione. I passi di ottimizzazione girano prima e includono compiti come la risoluzione di riferimenti dentro le definizioni. I passi di rimozione eseguono compiti come la rimozione di alias privati e di servizi inutilizzati. Si può scegliere in quale ordine sia eseguito ogni passo aggiuntivo. Per impostazione predefinita, sono eseguiti prima dei passi di ottimizzazione. Si possono usare le seguenti costanti come secondo parametro quando si registra un passo con il contenitore, per controllare in quale posizione vada il passo: PassConfig::TYPE_BEFORE_OPTIMIZATION PassConfig::TYPE_OPTIMIZE PassConfig::TYPE_BEFORE_REMOVING PassConfig::TYPE_REMOVE PassConfig::TYPE_AFTER_REMOVING Per esempio, per eseguire il proprio passo dopo i passi di rimozione predefiniti: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\PassConfig; $container = new ContainerBuilder(); $container->addcompilerpass( new CustomCompilerPass, PassConfig::TYPE_AFTER_REMOVING ); Esportare la configurazione per le prestazioni L'uso di file di configurazione per gestire il contenitore di servizi può essere molto più facile da capire rispetto all'uso di PHP, appena ci sono molti servizi. Questa facilità ha un prezzo, quando si considerano le prestazioni, perché i file di configurazione necessitano di essere analizzati, in modo da costruire la configurazione in PHP. Si possono prendere due piccioni con una fava, usando i file di configurazione e poi esportando e mettendo in cache la configurazione risultante. PhpDumper rende facile l'esportazione del contenitore compilato: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; $file = DIR.'/cache/container.php'; generated on August, 0 Chapter : Compilazione del contenitore

118 0 if (file_exists($file)) require_once $file; $container = new ProjectServiceContainer(); else $container = new ContainerBuilder(); $container->compile(); $dumper = new PhpDumper($container); file_put_contents($file, $dumper->dump()); ProjectServiceContainer è il nome predefinito dato alla classe del contenitore esportata: lo si può cambiare tramite l'opzione class, al momento dell'esportazione: Listing -0 0 $file = DIR.'/cache/container.php'; if (file_exists($file)) require_once $file; $container = new MyCachedContainer(); else $container = new ContainerBuilder(); $container->compile(); $dumper = new PhpDumper($container); file_put_contents( $file, $dumper->dump(array('class' => 'MyCachedContainer')) ); Si otterrà la velocità del contenitore compilato in PHP con la facilità di usare file di configurazione. Inoltre, esportare il contenitore in questo modo ottimizza ulteriormente i servizi creati dal contenitore. Nell'esempio precedente, occorrerà pulire il contenitore in cache ogni volta che si fa una modifica. L'aggiunta di una variabile che determini se si è in modalità di debug consente di mantenere la velocità del contenitore in cache in produzione, mantenendo una configurazione aggiornata durante lo sviluppo dell'applicazione: Listing - 0 // impostare $isdebug in base a una logica del progetto $isdebug =...; $file = DIR.'/cache/container.php'; if (!$isdebug && file_exists($file)) require_once $file; $container = new MyCachedContainer(); else $container = new ContainerBuilder(); $container->compile(); if (!$isdebug) generated on August, 0 Chapter : Compilazione del contenitore

119 0 $dumper = new PhpDumper($container); file_put_contents( $file, $dumper->dump(array('class' => 'MyCachedContainer')) ); Si può fare un ulteriore miglioramento solo ricompilando il contenitore in modalità debug quando le modifiche sono state fatte alla sua configurazione, piuttosto che a ogni richiesta. Lo si può fare mettendo in cache i file risorse usati per configurare il contenitore, come descritto nella documentazione del componente config, "Cache basata sulle risorse". Non occorre calcolare quali file mettere in cache, perché il costruttore del contenitore tiene traccia di tutte le risorse usate per configurarlo, non solo dei file di configurazione, ma anche le classi estensione e i passi di compilatore. Ciò significa che qualsiasi modifica a uno di tali file invaliderà la cache e farà scattare la ricostruzione del contenitore. Basta chiedere al contenitore queste risorse e usarle come meta dati per la cache: Listing // impostare $isdebug in base a qualcosa nel progetto $isdebug =...; $file = DIR.'/cache/container.php'; $containerconfigcache = new ConfigCache($file, $isdebug); if (!$containerconfigcache->isfresh()) $containerbuilder = new ContainerBuilder(); $containerbuilder->compile(); $dumper = new PhpDumper($containerBuilder); $containerconfigcache->write( $dumper->dump(array('class' => 'MyCachedContainer')), $containerbuilder->getresources() ); require_once $file; $container = new MyCachedContainer(); Ora il contenitore in cache esportato viene usato indipendentemente dalla modalità di debug. La differenza è che ConfigCache è impostato a debug con il secondo parametro del suo costruttore. Quando la cache non è in debug, sarà sempre usato il contenitore in cache, se esiste. In debug, viene scritto un file aggiuntivo di meta dati, con i timestamp di tutti i file risorsa. Vengono poi verificate eventuali modifiche dei file, nel caso in cui la cache debba essere considerata vecchia. Nel framework completo, compilazione e messa in cache del contenitore sono eseguite automaticamente. generated on August, 0 Chapter : Compilazione del contenitore

120 Chapter Usare i tag nei servizi I tag sono generiche stinghe (con alcune opzioni) che si possono applicare a un servizio. Di per sé, i tag non alterano la funzionalità di un servizio in alcun modo. Ma, se lo si desidera, si può chiedere a un costruttore di contenitori una lista di tutti i servizi che hanno uno specifico tag. Questo può tornare utile nei passi di compilatore, in cui si possono trovare tali servizi e usarli per modificarli in qualche modo. Per esempio, se si usa SwiftMailer, si può immaginare di voler implementare una "catena di trasporto", che è un insieme di classi che implementano \Swift_Transport. Usando una catena, si vogliono offrire a SwiftMailer diversi modi di trasportare un messsaggio, finché uno non ha successo. Per iniziare, definire la classe TransportChain: Listing - 0 class TransportChain private $transports; public function construct() $this->transports = array(); public function addtransport(\swift_transport $transport) $this->transports[] = $transport; Quindi, definire la catena come servizio: Listing - services: acme_mailer.transport_chain: class: TransportChain generated on August, 0 Chapter : Usare i tag nei servizi 0

121 Definire servizi con un tag personalizzato Ora vogliamo che diverse classi \Swift_Transport siano istanziate e aggiunte alla catena automaticamente, usando il metodo addtransport(). Come esempio, aggiungere i seguenti trasporti come servizi: Listing - 0 services: acme_mailer.transport.smtp: class: \Swift_SmtpTransport arguments: - "%mailer_host%" tags: - name: acme_mailer.transport acme_mailer.transport.sendmail: class: \Swift_SendmailTransport tags: - name: acme_mailer.transport Si noti che a ognuno è stato assegnato il tag acme_mailer.transport. Questo è il tag personalizzato che useremo nel passo di compilatore. Il passo di compilatore è ciò che dà un significato a questo tag. Creare un CompilerPass Il passo di compilatore ora chiede al contenitore ogni servizio che abbia il tag personalizzato: Listing use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; class TransportCompilerPass implements CompilerPassInterface public function process(containerbuilder $container) if (!$container->hasdefinition('acme_mailer.transport_chain')) return; $definition = $container->getdefinition( 'acme_mailer.transport_chain' ); $taggedservices = $container->findtaggedserviceids( 'acme_mailer.transport' ); foreach ($taggedservices as $id => $attributes) $definition->addmethodcall( 'addtransport', array(new Reference($id)) ); Il metodo process() verifica l'esistenza del servizio acme_mailer.transport_chain, quindi cerca tutti i servizi con tag acme_mailer.transport. Aggiunge all definizione del servizio acme_mailer.transport_chain una chiamata a addtransport() per ogni servizio generated on August, 0 Chapter : Usare i tag nei servizi

122 "acme_mailer.transport" trovato. Il primo parametro di ognuna di queste chiamate sarà il servizio di trasporto stesso. Registrare il passo con il contenitore Occorrerà anche registrare il passo con il contenitore, sarà poi eseguito quando il contenitore viene compilato: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container->addcompilerpass(new TransportCompilerPass()); I passi di compilatore sono registrati in modo diverso, se si usa il framework completo. Vedere Lavorare con i passi di compilatore nei bundle per maggiori dettagli. Aggiungere altri attributi ai tag A volte occorrono informazioni aggiuntive su ogni servizio che ha un certo tag. Per esempio, si potrebbe voler aggiungere un alias a ogni TransportChain. Per iniziare, cambiare la classe TransportChain: Listing class TransportChain private $transports; public function construct() $this->transports = array(); public function addtransport(\swift_transport $transport, $alias) $this->transports[$alias] = $transport; public function gettransport($alias) if (array_key_exists($alias, $this->transports)) return $this->transports[$alias]; Come si può vedere, al richiamo di addtransport, non prende solo un oggetto Swift_Transport, ma anche una stringa alias per il trasporto. Quindi, come si può fare in modo che ogni servizio di trasporto fornisca anche un alias? Per rispondere, cambiare la dichiarazione del servizio: Listing - generated on August, 0 Chapter : Usare i tag nei servizi

123 0 services: acme_mailer.transport.smtp: class: \Swift_SmtpTransport arguments: - "%mailer_host%" tags: - name: acme_mailer.transport, alias: pippo acme_mailer.transport.sendmail: class: \Swift_SendmailTransport tags: - name: acme_mailer.transport, alias: pluto Si noti che è stata aggiunta una chiave generica alias al tag. Per usarla effettivamente, aggiornare il compilatore: Listing use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; class TransportCompilerPass implements CompilerPassInterface public function process(containerbuilder $container) if (!$container->hasdefinition('acme_mailer.transport_chain')) return; $definition = $container->getdefinition( 'acme_mailer.transport_chain' ); $taggedservices = $container->findtaggedserviceids( 'acme_mailer.transport' ); foreach ($taggedservices as $id => $tagattributes) foreach ($tagattributes as $attributes) $definition->addmethodcall( 'addtransport', array(new Reference($id), $attributes["alias"]) ); La parte più strana è la variabile $attributes. Poiché si può usare lo stesso tag più volte sullo stesso servizio (p.e. in teoria si potrebbe assegnare il tag acme_mailer.transport allo stesso servizio cinque volte, $attributes è un array di informazioni sul tag per ciascun tag su tale servizio. generated on August, 0 Chapter : Usare i tag nei servizi

124 Chapter Usare un factory per creare servizi New in version.: Il metodo setfactory() è stato introdotto in Symfony.. Fare riferimento alle versioni precedenti per la sintassi per i factory prima di.. Il contenitore di servizi di Symfony fornisce un modo potente per controllare la creazione di oggetti, consentendo di specificare parametri da passare al costruttore, così come chiamate a metodi e impostazioni di parametri. A volte, tuttavia, non fornisce tutto ciò che è necessario per costruire gli oggetti. Per tali situazioni, si può usare un factory per creare oggetti e dire al contenitore di servizi di richiamare un metodo del factory, invece che istanziare direttamente l'oggetto. Si supponga di avere un factory che configura e restituisce un nuovo oggetto NewsletterManager: Listing - 0 class NewsletterManagerFactory public static function createnewslettermanager() $newslettermanager = new NewsletterManager(); return $newslettermanager; Per rendere l'oggetto NewsletterManager disponibile come servizio, si può configurare il contenitore di servizi per usare il metodo factory NewsletterFactory::createNewsletterManager(): Listing - services: newsletter_manager: class: NewsletterManager factory: [NewsletterManagerFactory, createnewslettermanager] Ora il metodo sarà richiamato staticamente. Se il factory stesso va istanziato e il metodo dell'oggetto risultante richiamato, configurare il factory stesso come servizio: In questo caso, il metodo (p.e. get) va cambiato per non essere statico:. generated on August, 0 Chapter : Usare un factory per creare servizi

125 Listing - services: newsletter_manager.factory: class: NewsletterManagerFactory newsletter_manager: class: NewsletterManager factory: createnewslettermanager] Passare parametri al metodo del factory Se occorre passare parametri al metodo del factory, si può usare l'opzione arguments dentro al contenitore di servizi. Per esempio, si supponga che il metodo get dell'esempio precedente accetti un servizio templating come parametro: Listing - services: newsletter_manager.factory: class: NewsletterManagerFactory newsletter_manager: class: NewsletterManager factory: createnewslettermanager] arguments: - generated on August, 0 Chapter : Usare un factory per creare servizi

126 Chapter Configurare servizi con un configuratore di servizi Il configuratore di servizi è una caratteristica del contenitore, che consente di usare un callable per configurare un servizio appena istanziato. Si può specificare un metodo di un altro servizio, una funzione PHP o un metodo statico in una classe. L'istanza del servizio viene passata al callable, consentendo al configuratore di fare tutto ciò di cui ha bisogno per configurare il servizio creato. Per esempio, si può usare un configuratore di servizi quando si ha un servizio che richiede una preparazione complessa, in base a impostazioni di configurazione provenienti da diversi sorgenti/servizi. Usando un configuratore esterno, si può mantenere l'implementazione del servizio pulita e disaccoppiata da altri oggetti, che forniscono la configurazione necessaria. Un altro caso d'uso interessante è quando si hanno molti oggetti che condividono una configurazione comune o che vanno configurati in modo simile a runtime. Per esempio, si supponga di avere un'applicazione in cui si inviano diversi tipi di agli utenti. Le passano attraverso diversi formattatori, che possono essere abilitati o meno, a seconda di alcune impostazioni dinamiche dell'applicazione. Si inizia definendo una classe NewsletterManager, come questa: Listing - 0 class NewsletterManager implements FormatterAwareInterface protected $mailer; protected $enabledformatters; public function setmailer(mailer $mailer) $this->mailer = $mailer; public function setenabledformatters(array $enabledformatters) $this->enabledformatters = $enabledformatters; generated on August, 0 Chapter : Configurare servizi con un configuratore di servizi

127 e una classe GreetingCardManager: Listing - 0 class GreetingCardManager implements FormatterAwareInterface protected $mailer; protected $enabledformatters; public function setmailer(mailer $mailer) $this->mailer = $mailer; public function setenabledformatters(array $enabledformatters) $this->enabledformatters = $enabledformatters; Come menzionato in precedenza, lo scopo è quello di impostare i formattatori a runtime, a seconda delle configurazioni dell'applicazione. Per farlo, definiamo anche una classe FormatterManager, che si occupi di caricare e validatore i formattatori abilitati nell'applicazione: Listing class FormatterManager protected $enabledformatters; public function loadformatters() // codice per configurare quali formattatori usare $enabledformatters = array(...); $this->enabledformatters = $enabledformatters; public function getenabledformatters() return $this->enabledformatters; Se lo scopo è quello di evitare accoppiamenti tra NewsletterManager e GreetingCardManager con FormatterManager, si potrebbe voler creare una classe configuratore, per configurare tali istanze: Listing - class Configurator private $formattermanager; generated on August, 0 Chapter : Configurare servizi con un configuratore di servizi

128 0 public function construct( formattermanager $formattermanager) $this->formattermanager = $formattermanager; public function configure( formatterawareinterface $ manager) $ manager->setenabledformatters( $this->formattermanager->getenabledformatters() ); Il compito di Configurator è iniettare i filtri abilitati in NewsletterManager e GreetingCardManager, perché non sono consapevoli di dove i filtri abilitati arrivino. D'altro canto, FormatterManager sa dei formattatori abilitati e come caricarli, mantenendo il principio della singola responsabilità. Configurazione del configuratore di servizi La configurazione del servizio per le classi viste sopra assomiglia a questa: Listing services: my_mailer: #... _formatter_manager: class: FormatterManager #... _configurator: class: Configurator arguments: #... newsletter_manager: class: NewsletterManager calls: - [setmailer, configurator: configure] greeting_card_manager: class: GreetingCardManager calls: - [setmailer, configurator: configure] generated on August, 0 Chapter : Configurare servizi con un configuratore di servizi

129 Chapter Come gestire le dipendenze comuni con servizi genitori Quando si aggiungono molte funzionalità a un'applicazione, si potrebbe voler condividere delle dipendenze comuni tra classi correlate. Per esempio, si potrebbe avere un gestore di newsletter che usa un setter per impostare le sue dipendenze: Listing - 0 class NewsletterManager protected $mailer; protected $ formatter; public function setmailer(mailer $mailer) $this->mailer = $mailer; public function set formatter( formatter $ formatter) $this-> formatter = $ formatter; e poi dei biglietti di auguri, con una classe che condivide alcune dipendenze: Listing - class GreetingCardManager protected $mailer; protected $ formatter; public function setmailer(mailer $mailer) $this->mailer = $mailer; generated on August, 0 Chapter : Come gestire le dipendenze comuni con servizi genitori

130 0 public function set formatter( formatter $ formatter) $this-> formatter = $ formatter; La configurazione dei servizi di queste classe potrebbe essere qualcosa del genere: Listing - 0 services: my_mailer: #... my_ _formatter: #... newsletter_manager: class: NewsletterManager calls: - [setmailer, - [set formatter, greeting_card_manager: class: "GreetingCardManager" calls: - [setmailer, - [set formatter, Ci sono diverse ripetizioni, sia nelle classi che nella configurazione. Questo vuol dire che, se per esempio si cambiano le classi Mailer o Formatter per essere iniettate tramite costruttore, si avrà bisogno di aggiornare la configurazione in due punti. In modo simile, se fosse necessario cambiare i metodi setter, si avrebbe bisogno di farlo in entrambe le classi. Il tipico modo di trattare i metodi comuni di queste classi correlate sarebbe estrarli in una classe superiore: Listing - 0 abstract class MailManager protected $mailer; protected $ formatter; public function setmailer(mailer $mailer) $this->mailer = $mailer; public function set formatter( formatter $ formatter) $this-> formatter = $ formatter; Quindi NewsletterManager e GreetingCardManager possono estendere tale classe: Listing - generated on August, 0 Chapter : Come gestire le dipendenze comuni con servizi genitori 0

131 class NewsletterManager extends MailManager e: Listing - class GreetingCardManager extends MailManager In modo simile, il contenitore di servizi di Symfony supporta anche l'estensione di servizi nella configurazione, in modo da poter ridurre le ripetizioni, specificando un genitore per un servizio. Listing - 0 #... services: #... mail_manager: abstract: true calls: - [setmailer, - [set formatter, newsletter_manager: class: "NewsletterManager" parent: mail_manager greeting_card_manager: class: "GreetingCardManager" parent: mail_manager In questo contesto, avere un servizio parent implica che i parametri e le chiamate a metodi del servizio genitore andrebbero usati per i servizi figli. Specificatamente, i metodi setter definiti per il servizio genitore saranno richiamati all'istanza dei servizi figli. Se si rimuove la voce di configurazione parent, i servizi saranno ancora istanziati e estenderanno ancora la classe MailManager. La differenza è che, omettendo la voce di configurazione parent, si farà in modo che calls, definito nel servizio mail_manager, non sarà eseguito quando i servizi figli saranno istanziati. Gli attributi scope, abstract e tags sono sempre presi dal servizio figlio. La classe genitore è astratta, perché non andrebbe istanziata direttamente dal contenitore o passata in un altro servizio. Esiste puramente come "template" per altri servizi. Per questo può non avere class configurata, che provocherebbe un'eccezione per un servizio non astratto. Per poter risolvere dipendenze dei genitori, ContainerBuilder deve essere precedentemente compilato. Si veda Compilazione del contenitore per maggiori dettagli. generated on August, 0 Chapter : Come gestire le dipendenze comuni con servizi genitori

132 Negli esempi mostrati c'è una relazione simile tra servizi padre e figlio e classi padre e figlio sottostanti. Sebbene non sia detto che questo debba sempre essere il caso, si possono estrarre le parti comuni di definizioni simili di servizi in un servizio padre, senza ereditare anche una classe padre in PHP. Sovrascrivere le dipendenze del genitore A volte si potrebbe voler sovrascrivere la classe passata come dipendenza solo per un servizio figlio. Fortunatamente, aggiungendo la configurazione della chiamata al metodo per il servizio figlio, le dipendenze impostate dalla classe genitore saranno sovrascritte. Se quindi si ha bisogno di passare una dipendenza diversa, solo alla classe NewsletterManager, la configurazione potrebbe essere come la seguente: Listing #... services: #... my_alternative_mailer: #... mail_manager: abstract: true calls: - [setmailer, - [set formatter, newsletter_manager: class: "NewsletterManager" parent: mail_manager calls: - [setmailer, greeting_card_manager: class: "GreetingCardManager" parent: mail_manager La classe GreetingCardManager riceverà le stesse dipendenze di prima, ma a NewsletterManager sarà passato il servizio my_alternative_mailer, invece di my_mailer. Non si possono sovrascrivere le chiamate a metodi. Dopo aver definito nuove chiamate a metodi nel servizio figlio, queste sono aggiunte all'insieme attuale di chiamate a metodi. Questo vuol dire che funzionerà quando il setter sovrascrive la proprietà corrente, ma non funzionerà come ci si aspetta quando il setter appende a dati esistenti (p.e. un metodo addfilters()). In questi casi, l'unica soluzione è non estendere il servizio padre e configurare il servizio, come si farebbe senza questa caratteristica. generated on August, 0 Chapter : Come gestire le dipendenze comuni con servizi genitori

133 Chapter Configurazione avanzata del contenitore Segnare i servizi come pubblici / privati Quando si definisce un servizio, di solito si vuole potervi accedere dall'interno di un'applicazione. Tali servizi sono chiamati "pubblici". Per esempio, il servizio doctrine registrato con il contenitore durante l'uso di DoctrineBundle è un servizio pubblico, accessibile tramite: Listing - $doctrine = $container->get('doctrine'); Ci sono tuttavia dei casi in cui non si desidera che un servizio sia pubblico. Di solito avviene quando un servizio è definito solo per essere usato come parametro da un altro servizio. Se si usa un servizio privato come parametro di più di un altro servizio, ciò provocherà un'istanza in linea (p.e. new PippoPlutoPrivato()) all'interno di quest'altro servizio, rendendola non disponibile pubblicamente a runtime. In parole povere: un servizio sarà privato quanto non si vuole che sia accessibile direttamente dal codice. Ecco un esempio: Listing - services: pippo: class: Esempio\Pippo public: false Essendo il servizio privato, non si può richiamare: Listing - $container->get('pippo'); Tuttavia, se un servizio è stato segnato come privato, gli si può comunque assegnare un alias (vedere sotto) per accedervi (tramite alias). generated on August, 0 Chapter : Configurazione avanzata del contenitore

134 I servizi sono predefiniti come pubblici. Servizi sintetici I servizi sintetici sono servizi che vengono iniettati nel contenitore, invece di essere creati dal contenitore stesso. Per esempio, se si usa il componente HttpKernel con il componente DependencyInjection, il servizio request è iniettato nel metodo ContainerAwareHttpKernel::handle(), quando entra nello scope della richiesta. Se non c'è una richiesta, la classe non esiste, quindi non può essere inclusa nella configurazione del contenitore. Inoltre, il servizio deve essere diverso per ogni sotto-richiesta nell'applicazione. Per creare un servizio sintetico, impostare synthetic a true: Listing - services: request: synthetic: true Come si può vedere, viene impostata solo l'opzione synthetic. Tutte le altre opzioni vengono solo usate per configurare il modo in cui un servizio viene creato dal contenitore. Non essendo il servizio creato dal contenitore, tali opzioni sono omesse. Si può ora iniettare la classe, usando Container::set : Listing - $container->set('request', new MyRequest(...)); Alias A volte si ha bisogno di usare scorciatoie per accedere ad alcuni servizi. Si possono impostare degli alias e si può anche impostare un alias su un servizio non pubblico. Listing - services: pippo: class: Esempio\Pippo pluto: alias: pippo Ciò vuol dire che, quando si usa direttamente il contenitore, si può accedere al servizio pippo richiedendo il servizio pluto, in questo modo: Listing - $container->get('pluto'); // restituisce il servizio pippo. generated on August, 0 Chapter : Configurazione avanzata del contenitore

135 In YAML, si può anche usare una scorciatoia come alias di un servizio: Listing - services: pippo: class: Esempio\Pippo pluto: Richiesta di file Possono esserci dei casi in cui occorra includere altri file subito prima che il servizio stesso sia caricato. Per poterlo fare, si può usare la direttiva file. Listing - services: foo: class: Esempio\Pippo\Pluto file: "%kernel.root_dir%/src/percorso/del/file/pippo.php" Si noti che Symfony richiamerà internamente la funzione require_once di PHP, il che vuol dire che il file sarà incluso una sola volta per richiesta. Decorare i servizi New in version.: I servizi decorati sono stati introdotti in Symfony.. Quando si sovrascrive una definizione esistente, il vecchio servizio va perduto: Listing -0 $container->register('pippo', 'ServizioPippo'); // questo rimpiazzerà la vecchia definizione con quella nuova // la vecchia definizione va perduta $container->register('pippo', 'NuovoServizioPippo'); La maggior parte delle volte questo è esattamente quello che si desidera. A volte, però, si potrebbe invece voler decorare il vecchio servizio. In questo caso, il vecchio servizio viene mantenuto, per potervi fare riferimento all'interno del nuovo. Questa configurazione sostituisce pippo con un nuovo servizio, ma mantiene un riferimento al vecchio, come pluto.inner: Listing - bar: public: false class: stdclass decorates: pippo arguments: Ecco quello che succede: il metodo setdecoratedservice()` dice al contenitore che il servizio ``pluto sostituisce il servizio pippo, rinominando pippo in pluto.inner. Per convenzione, il vecchio servizio pippo è rinominato pluto.inner, in modo da poterlo iniettare nel nuovo servizio. generated on August, 0 Chapter : Configurazione avanzata del contenitore

136 L'identificativo interno generato è basato sull'id del servizio generato (pluto, in questo caso), non su quello del servizio decorato (pippo, in questo caso). Questo è necessario, per consentire più decoratori sullo stesso servizio (devono avere id generati diversi). La maggior parte delle volte, il decoratore deve essere dichiarato privato, perché non ci sarà bisogno di recuperarlo come pluto dal contenitore. La visibilità del servizio edcorato pippo (che è un alias per pluto) resterà quella originale di pippo. Si può cambiare il nome del servizio interno, se lo si desidera: Listing - bar: class: stdclass public: false decorates: pippo decoration_inner_name: pluto.wooz arguments: generated on August, 0 Chapter : Configurazione avanzata del contenitore

137 Chapter 0 Servizi pigri New in version.: I servizi pigri sono stati aggiunti in Symfony.. Perché i servizi pigri? A volte può essere necessario iniettare, all'interno del proprio oggetto, un servizio un po' pesante da istanziare e che non sempre viene utilizzato. Si supponga, ad esempio, di avere un GestoreDiNewsletter e che si voglia iniettare un servizio mailer al suo interno. Solo alcuni metodi del GestoreDiNewsletter usano effettivamente il mailer ma, anche senza farne uso, il servizio mailer verrebbe comunque istanziato in modo da poter costruire il GestoreDiNewsletter. Per risolvere questo problema è possibile usare un servizio pigro. Quando si usa un servizio pigro, in realtà viene iniettato un "proxy" del servizio mailer. Il proxy sembra e si comporta esattamente come se fosse il mailer, a parte il fatto che mailer non viene istanziato finché non si interagisce in qualche modo con il suo proxy. Installazione Per poter istanziare i servizi pigri è prima necessario installare il bridge ProxyManager : Listing 0- $ composer require symfony/proxy-manager-bridge:~. Se si usa il framework completo, questo pacchetto è già incluso, ma il vero gestore di proxy deve essere incluso. Eseguire quindi: Listing 0- $ php composer.phar require ocramius/proxy-manager:~0. Compilare quindi il contenitore e verificare di avere un proxy per i servizi pigri.. https://github.com/symfony/symfony/tree/master/src/symfony/bridge/proxymanager generated on August, 0 Chapter 0: Servizi pigri

138 Configurazione Si può definire un servizio come pigro, modificandone la definizione: Listing 0- services: pippo: class: Acme\Pippo lazy: true Ora è possibile richiedere il servizio dal contenitore: Listing 0- $servizio = $container->get('pippo'); A questo punto il $servizio recuperato dovrebbe essere un proxy virtuale con la stessa firma della classe che rappresenta il servizio. È anche possibile iniettare il servizio così come si farebbe con qualsiasi altro servizio. L'oggetto che verrà effettivamente iniettato sarà il proxy. Per verificare che il proxy funzioni, si può semplicemente verificare l'interfaccia dell'oggetto ricevuto. Listing 0- var_dump(class_implements($service)); Se la classe implementa ProxyManager\Proxy\LazyLoadingInterface, i servizi pigri stanno funzionando. Se non si è installato il bridge ProxyManager, il contenitore si limiterà a saltare il parametro lazy e a istanziare il servizio come farebbe normalmente. Il proxy viene inizializzato e il servizio vero e proprio viene istanziato non appena si dovesse interagire con l'oggetto. Risorse aggiuntive È possibile approfondire le modalità con cui i sostituti vengono istanziati, generati e inizializzati nella documentazione su ProxyManager.. https://github.com/symfony/symfony/tree/master/src/symfony/bridge/proxymanager. https://github.com/ocramius/proxymanager/blob/master/docs/lazy-loading-value-holder.md generated on August, 0 Chapter 0: Servizi pigri

139 Chapter Flusso di costruzione del contenitore Nelle pagine precedenti di questa sezioni, è stato detto poco sulle posizioni in cui i vari file e le classi dovrebbero trovarsi. Questo perché ciò dipende dall'applicazione, libreria o framework in cui si vuole usare il contenitore. Vedere come il contenitore è configurato e costruito nel framework Symfony aiuterà a capire come tutti questi file si incastrino insieme, sia che si voglia usare il framework, sia che si cerchi solo di usare il contenitore di servizi in un'altra applicazione. Il framework usa il componente HttpKernel per gestire il caricamento della configurazione del contenitore di servizi dall'applicazione e dai bundle, inoltre gestisce la compilazione e la cache. Anche se non si usa HttpKernel, dovrebbe dare un'idea del modo in cui organizzare la configurazione in un'applicazione modulare. Lavorare con il contenitore in cache Il kernel verifica se c'è una versione in cache del contenitore, prima di costruirlo. HttpKernel ha un'impostazione di debug, per cui la versione in cache viene usata se tale impostazione vale false. Se invece debug è true, il kernel verifica se la configurazione è fresca e, se lo è, la versione in cache è quella del contenitore. Se non lo è, il contenitore viene costruito a partire dalla configurazione a livello di applicazione e da quella dei bundle. Leggere esportare la configurazione per le prestazioni per maggiori dettagli. Configurazione a livello di applicazione La configurazione a livello di applicazione è caricata dalla cartella app/config. Vengono caricati più file e quindi fusi, quando le estensioni vengono processate. Ciò consente di avere configurazioni diverse per ambienti diversi, p.e. dev, prod, ecc. Questi file contengono parametri e servizi, che sono caricati direttamente nel contenitore, come in impostare il contenitore con file di configurazione. Contengono anche configurazioni che sono processate da estensioni, come in gestire la configurazione con le estensioni. Sono considerate configurazioni di bundle, perché ogni bundle contiene una classe an Extension. generated on August, 0 Chapter : Flusso di costruzione del contenitore

140 Configurazione a livello di bundle Per convenzione, ogni bundle contiene una classe Extension, nella cartella DependencyInjection del bundle stesso. Queste classi vengono registrare da ContainerBuilder, al boot del kernel. Quando ContainerBuilder viene compilato, la configurazione a livello di applicazione rilevante per l'estensione del bundle viene passata alla classe Extension, che solitamente carica anche i propri file di configurazione, tipicamente dalla cartella Resources/config del bundle. La configurazione a livello di applicazione è solitamente processata con un oggetto Configuration, anch'esso memorizzato nella cartella DependencyInjection del bundle. Passi di compilatore per consentire interazioni tra bundle I passi di compilatore sono usati per consentire interazioni tra diversi bundle, poiché non possono influire a vicenda sulla configurazione nelle classi estensione. Uno degli usi principali è il processamento dei servizi con tag, consentendo ai bundle di registrare servizi che possono essere presi da altri bundle, come i logger di Monolog, le estensioni di Twig e i collettori di dati del Web Profiler. I passi di compilatore sono solitamente posti nella cartella DependencyInjection/Compiler del bundle. Compilazione e cache Dopo che il processo di compilazione ha caricato i servizi dalla configurazione, dalle estensioni e dai passi di compilatore, viene esportato, in modo che possa essere usata la cache nella volta successiva. La versione esportata è usata nelle richieste successive, essendo più efficiente. generated on August, 0 Chapter : Flusso di costruzione del contenitore 0

141 Chapter Il componente DomCrawler Il componente DomCrawler semplifica la navigazione nel DOM dei documenti HTML e XML. Il componente DomCrawler non è progettato per manipolare il DOM o per ri-esportare HTML/ XML, anche se sarebbe tecnicamente possibile utilizzarlo in tal modo. Installazione È possibile installare il componente in due modi: Installandolo via Composer (symfony/dom-crawler su Packagist ). Utilizzando il repository ufficiale su Git (https://github.com/symfony/domcrawler ); Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. Utilizzo La classe Crawler mette a disposizione metodi per effettuare query e manipolare i documenti HTML e XML. Un'istanza di Crawler rappresenta un insieme (SplObjectStorage ) di oggetti DOMElement, che sono, in pratica, nodi facilmente visitabili:. https://packagist.org/packages/symfony/dom-crawler. https://github.com/symfony/domcrawler. generated on August, 0 Chapter : Il componente DomCrawler

142 Listing - 0 use Symfony\Component\DomCrawler\Crawler; $html = <<<'HTML' <!DOCTYPE html> <html> <body> <p class="messaggio">ciao Mondo!</p> <p>ciao Crawler!</p> </body> </html> HTML; $crawler = new Crawler($html); foreach ($crawler as $elementodom) print $elementodom->nodename; Le classi specializzate Link e Form sono utili per interagire con collegamenti html e i form durante la visita dell'albero HTML. DomCrawler proverà a sistemare automaticamente il codice HTML, per farlo corrispondere alle specifiche ufficiali. Per esempio, se si inserisce un tag <p> dentro a un altro tag <p>, sarà spostato come fratello del tag genitore. Questo è il comportamento atteso e fa parte delle specifiche di HTML. Se però si ottiene un comportamento inatteso, potrebbe esserne una causa. Pur non essendo DomCrawler pensato per esportare contenuti, si può vedere la versione "sistemata" del codice HTML con un'esportazione. Filtrare i nodi È possibile usare facilmente le espressioni di XPath: Listing - $crawler = $crawler->filterxpath('descendant-or-self::body/p'); internamente viene usato DOMXPath::query per eseguire le query XPath. La ricerca è anche più semplice se si è installato il componente CssSelector. In questo modo è possibile usare lo stile jquery per l'attraversamento: Listing - $crawler = $crawler->filter('body > p'); È possibile usare funzioni anonime per eseguire filtri complessi: Listing - use Symfony\Component\DomCrawler\Crawler; $crawler = $crawler. generated on August, 0 Chapter : Il componente DomCrawler

143 ->filter('body > p') ->reduce(function (Crawler $node, $i) // filtra i nodi pari return ($i % ) == 0; ); Per rimuovere i nodi, la funzione anonima dovrà restituire false. Tutti i metodi dei filtri restituiscono una nuova istanza di Crawler contenente gli elementi filtrati. Entrambio i metodi filterxpath() e filter() 0 funzionano con gli spazi di nomi XML, che possono essere scoperti autometicamente oppure registrati esplicitamente. New in version.: La scoperta automatica e la registrazione esplicita di spazi di nomi è stata introdotta in Symfony.. Si consideri il seguente XML: Listing - 0 <?xml version=".0" encoding="utf-"?> <entry xmlns="http://www.w.org/00/atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/00" > <id>tag:youtube.com,00:video:kgzrzmecj</id> <yt:accesscontrol action="comment" permission="allowed"/> <yt:accesscontrol action="videorespond" permission="moderated"/> <media:group> <media:title type="plain">chordates - CrashCourse Biology #</media:title> <yt:aspectratio>widescreen</yt:aspectratio> </media:group> </entry> Lo si può filtrare con Crawler, senza bisogno di registrare alias di spazi di nomi, con filterxpath() : Listing - $crawler = $crawler->filterxpath('//default:entry/media:group//yt:aspectratio'); e con filter() : Listing - use Symfony\Component\CssSelector\CssSelector; CssSelector::disableHtmlExtension(); $crawler = $crawler->filter('default entry media group yt aspectratio'); generated on August, 0 Chapter : Il componente DomCrawler

144 Lo spazio dei nomi predefinito è registrato con prefisso "default". Lo si può cambiare con il metodo setdefaultnamespaceprefix(). Lo spazio dei nomi predefinito viene rimosso durante il caricamento del contenuto, se è l'unico spazio di nomi nel documento. Questo per semplificare le query xpath. Si possono registrare esplicitamente spazi di nomi con il metodo registernamespace() : Listing - $crawler->registernamespace('m', 'http://search.yahoo.com/mrss/'); $crawler = $crawler->filterxpath('//m:group//yt:aspectratio'); Per una query su XML con selettore CSS, occorre disabilitare l'estensione HTML, tramite CssSelector::disableHtmlExtension, per evitare di convertire il selettore in minuscolo. Attraversamento dei nodi Accedere ai nodi tramite la loro posizione nella lista: Listing - $crawler->filter('body > p')->eq(0); Ottenere il primo o l'ultimo nodo della selezione: Listing -0 $crawler->filter('body > p')->first(); $crawler->filter('body > p')->last(); Ottenere i nodi allo stesso livello della selezione attuale: Listing - $crawler->filter('body > p')->siblings(); Ottenere i nodi, allo stesso livello, precedenti o successivi alla selezione attuale: Listing - $crawler->filter('body > p')->nextall(); $crawler->filter('body > p')->previousall(); Ottenere tutti i nodi figlio o padre: Listing - $crawler->filter('body')->children(); $crawler->filter('body > p')->parents(); Tutti i metodi di attraversamento restituiscono un nuova istanza di Crawler generated on August, 0 Chapter : Il componente DomCrawler

145 Accedere ai nodi tramite il loro valore New in version.: Il metodo nodename() è stato introdotto in Symfony.. Accedere al nome del nodo (nome del tag HTML) del primo nodo della selezione attuale (es. "p" o "div"): Listing - // restituirà il nome del nodo (nome del tag HTML) del primo elemento figlio di <body> $tag = $crawler->filterxpath('//body/*')->nodename(); Accedere al valore del primo nodo della selezione attuale: Listing - $message = $crawler->filterxpath('//body/p')->text(); Accedere al valore dell'attributo del primo nodo della selezione attuale: Listing - $class = $crawler->filterxpath('//body/p')->attr('class'); Estrarre l'attributo e/o il valore di un nodo da una lista di nodi: Listing - $attributi = $crawler ->filterxpath('//body/p') ->extract(array('_text', 'class')) ; L'attributo speciale _text rappresenta il valore di un nodo. Chiamare una funzione anonima su ogni nodo della lista: Listing - use Symfony\Component\DomCrawler\Crawler; $valorinodi = $crawler->filter('p')->each(function (Crawler $nodo, $i) return $nodo->text(); ); New in version In: Symfony., alle funzioni Closure each e reduce viene passato un Crawler come primo parametro. In precedenza, tale parametro era un DOMNode. La funzione anonima riceve la posizione e il nodo (come Crawler) come parametri. Il risultato è un array contenente i valori restituiti dalle chiamate alla funzione anonima. Aggiungere contenuti Il crawler supporta diversi modi per aggiungere contenuti: Listing - $crawler = new Crawler('<html><body /></html>'); $crawler->addhtmlcontent('<html><body /></html>'); $crawler->addxmlcontent('<root><node /></root>');. generated on August, 0 Chapter : Il componente DomCrawler

146 0 $crawler->addcontent('<html><body /></html>'); $crawler->addcontent('<root><node /></root>', 'text/xml'); $crawler->add('<html><body /></html>'); $crawler->add('<root><node /></root>'); Quando si trattano set di caratteri diversi da ISO--, aggiungere sempre il content HTML, usando il metodo addhtmlcontent(), in cui si può specificare come secondo parametro il set di caratteri desiderato. Essendo l'implementazione del Crawler basata sull'estensione di DOM, è anche possibile interagire con le classi native DOMDocument 0, DOMNodeList e DOMNode : Listing -0 0 $documento = new \DOMDocument(); $documento->loadxml('<root><node /><node /></root>'); $listanodi = $documento->getelementsbytagname('node'); $nodo = $documento->getelementsbytagname('node')->item(0); $crawler->adddocument($documento); $crawler->addnodelist($listanodi); $crawler->addnodes(array($nodo)); $crawler->addnode($nodo); $crawler->add($documento); Manipolare ed esportare un Crawler Questi metodi di Crawler servono per popolare inizialmente il proprio Crawler e non per essere usati per manipolare ulteriormente un DOM (sebbene sia possibile). Tuttavia, poiché il Crawler è un insieme di oggetti DOMElement, si può usare qualsiasi metodo o proprietà disponibile in DOMElement, DOMNode o DOMDocument. Per esempio, si può ottenere l'html di un Crawler con qualcosa del genere: Listing - $html = ''; foreach ($crawler as $domelement) $html.= $domelement->ownerdocument->savehtml($domelement); Oppure si può ottenere l'html del primo nodo con html() : Listing - $html = $crawler->html(); Il metodo html è nuovo in Symfony generated on August, 0 Chapter : Il componente DomCrawler

147 Collegamenti Per trovare un collegamento tramite il suo nome (o un'immagine cliccabile tramite il suo attributo alt) si usa il metodo selectlink in un crawler esistente. La chiamata restituisce un'istanza di Crawler contenente solo i collegamenti selezionati. La chiamata link() restituisce l'oggetto speciale Link : Listing - $linkscrawler = $crawler->selectlink('vai altrove...'); $link = $linkscrawler->link(); // oppure, in una sola riga $link = $crawler->selectlink('vai altrove...')->link(); L'oggetto Link selezionato: ha diversi metodi utili per avere ulteriori informazioni relative al collegamento Listing - // restituisce l'uri che può essere usato per eseguire nuove richieste $uri = $link->geturi(); Il metodo geturi() è specialmente utile, perché pulisce il valore di href e lo trasforma nel modo in cui dovrebbe realmente essere processato. Per esempio, un collegamento del tipo href="#foo" restituirà l'uri completo della pagina corrente con il suffisso #foo. Il valore restituito da geturi() è sempre un URI completo, sul quale è possibile lavorare. Form Un trattamento speciale è riservato anche ai form. È disponibile, in Crawler, un metodo selectbutton() che restituisce un altro Crawler relativo al pulsante (input[type=submit], input[type=image], o button) con il testo dato. Questo metodo è specialmente utile perché può essere usato per restituire un oggetto Form 0, che rappresenta il form all'interno del quale il pulsante è definito: Listing - $form = $crawler->selectbutton('valida')->form(); // oppure "riempire" i campi del form con dati $form = $crawler->selectbutton('valida')->form(array( 'nome' => 'Ryan', )); L'oggetto Form ha molti utilissimi metodi che permettono di lavorare con i form: $uri = $form->geturi(); $metodo = $form->getmethod(); Il metodo geturi() fa più che restituire il mero attributo action del form. Se il metodo del form è GET, allora, imitando il comportamento del browser, restituirà l'attributo dell'azione seguito da una stringa di tutti i valori del form. È possibile impostare e leggere virtualmente i valori nel form: generated on August, 0 Chapter : Il componente DomCrawler

148 Listing - 0 // imposta, internamente, i valori del form $form->setvalues(array( 'registrazione[nomeutente]' => 'fandisymfony', 'registrazione[termini]' =>, )); // restituisce un array di valori in un array "semplice", come in precedenza $values = $form->getvalues(); // restituisce i valori come li vedrebbe PHP // con "registrazione" come array $values = $form->getphpvalues(); Per lavorare con i campi multi-dimensionali: Listing - <form> <input name="multi[]" /> <input name="multi[]" /> <input name="multi[dimensionale]" /> </form> È necessario specificare il nome pienamente qualificato del campo: Listing - // Imposta un singolo campo $form->setvalue('multi[0]', 'valore'); // Imposta molteplici campi in una sola volta $form->setvalues(array('multi' => array( => 'valore', 'dimensionale' => 'un altro valore' ))); Se questo è fantastico, il resto è anche meglio! L'oggetto Form permette di interagire con il form come se si usasse il browser, selezionando i valori dei radio, spuntando i checkbox e caricando file: Listing - 0 $form['registrazione[nomeutente]']->setvalue('fandisymfony'); // cambia segno di spunta a un checkbox $form['registrazione[termini]']->tick(); $form['registrazione[termini]']->untick(); // seleziona un'opzione $form['registrazione[data_nascita][anno]']->select(); // seleziona diverse opzioni da una lista di opzioni o da una serie di checkbox $form['registrazione[interessi]']->select(array('symfony', 'biscotti')); // può anche imitare l'upload di un file $form['registrazione[foto]']->upload('/percorso/al/file/lucas.jpg'); Usare i dati del form A cosa serve tutto questo? Se si stanno eseguendo i test interni, è possibile recuperare informazioni da tutti i form esattamente come se fossero stati inviati utilizzando i valori PHP: Listing -0 generated on August, 0 Chapter : Il componente DomCrawler

149 $valori = $form->getphpvalues(); $files = $form->getphpfiles(); Se si utilizza un client HTTP esterno, è possibile usare il form per recuperare tutte le informazioni necessarie per create una richiesta POST dal form: Listing - $uri = $form->geturi(); $metodo = $form->getmethod(); $valori = $form->getvalues(); $files = $form->getfiles(); // a questo punto si usa un qualche client HTTP e si inviano le informazioni Un ottimo esempio di sistema integrato che utilizza tutte queste funzioni è Goutte. Goutte usa a pieno gli oggetti del Crawler di Symfony e, con essi, può inviare i form direttamente: Listing - 0 use Goutte\Client; // crea una richiesta a un sito esterno $client = new Client(); $crawler = $client->request('get', 'https://github.com/login'); // seleziona il form e riempie alcuni valori $form = $crawler->selectbutton('entra')->form(); $form['login'] = 'fandisymfony'; $form['password'] = 'unapassword'; // invia il form $crawler = $client->submit($form); Scegliere valori non validi New in version.: Il metodo disablevalidation() è stato aggiunto in Symfony.. Per impostazione predefinita, i campi di scelta (select, radio) hanno una validazione interna, che previene l'impostazione di valori non validi. Se si vuole poter impostare valori non validi, si può usare il metodo disablevalidation(), sia sull'intero form, sia su campi specifici: Listing - // Disabilita la validazione per un campo specifico $form['country']->disablevalidation()->select('valore non valido'); // Disabilita la validazione per l'intero form $form->disablevalidation(); $form['country']->select('valore non valido');. https://github.com/fabpot/goutte. generated on August, 0 Chapter : Il componente DomCrawler

150 Chapter Il componente EventDispatcher Il componente EventDispatcher fornisce strumenti che consentono ai componenti di un'applicazione di comunicare tra di loro, distribuendo eventi e ascoltandoli. Introduzione L'approccio orientato agli oggetti da tempo assicura estensibilità del codice. Creando classi con responsabilità ben definite, il codice diventa più flessibile, consentendo allo sviluppatore di estenderlo con sotto-classi, che ne modifichino il comportamento. Se tuttavia si vogliono condividere le proprie modifiche con altri sviluppatori, che a loro volta abbiano costruito le proprie sotto-classi, l'ereditarietà non è più la risposta giusta. Consideriamo un esempio concreato, in cui si voglia fornire un sistema di plugin per un progetto. Un plugin dovrebbe essere in grado di aggiungere metodi o di fare qualcosa prima o dopo che un metodo sia eseguito, senza interferire con altri plugin. Questo problema non si risolve facilmente con l'ereditarietà singola, mentre l'ereditarietà multipla (dove sia possibile con PHP) ha i suoi difetti. Il componente Event Dispatcher di Symfony implementa il pattern Mediator in modo semplice ed efficace, per rendere possibile tutto questo e per rendere un progetto veramente estensibile. Si prenda un semplice esempio da Il componente HttpKernel. Una volta creato un oggetto Response, può essere utile consentirne la modifica ad altri elementi del sistema (p.e. aggiungere header di cache) prima del suo utilizzo effettivo. Per poterlo fare, il kernel di Symfony lancia un evento, kernel.response. Ecco come funziona: Un ascoltatore (oggetto PHP) dice a un oggetto distributore centrale che vuole ascoltare l'evento kernel.response; A un certo punto, il kernel di Symfony dice all'oggetto distributore di distribuire l'evento kernel.response, passando un oggetto Event, che ha accesso all'oggetto Response; Il distributore notifica a (ovvero chiama un metodo su) tutti gli ascoltatori dell'evento kernel.response, consentendo a ciascuno di essi di modificare l'oggetto Response.. generated on August, 0 Chapter : Il componente EventDispatcher 0

151 Installazione Si può installare il componente in due modi: Installarlo via Composer (symfony/event-dispatcher su Packagist ); Usare il repository ufficiale su Git (https://github.com/symfony/eventdispatcher ). Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. Uso Eventi Quando un evento viene distribuito, è identificato da un nome univoco (p.e. kernel.response), che può essere ascoltato da un numero qualsiasi di ascoltatori. Inoltre, un'istanza di Event viene creata e passata a tutti gli ascoltatori. Come si vedrà più avanti, l'oggetto Event stesso spesso contiene dei dati sull'evento distribuito. Convenzioni sui nomi Il nome univoco dell'evento può essere una stringa qualsiasi, ma segue facoltativamente alcune semplici convenzioni di nomenclatura: usa solo lettere minuscole, numeri, punti (.) e trattini bassi (_); ha un prefisso con uno spazio dei nomi, seguito da un punto (p.e. kernel.); termina con un verbo, che indica l'azione intrapresa (p.e. request). Ecco alcuni buoni esempi di nomi di eventi: kernel.response form.pre_set_data Nomi di eventi e oggetti Event Quando il distributore notifica gli ascoltatori, passa loro un oggetto Event. La classe base Event è molto semplice: contiene un metodo per fermare la propagazione degli eventi, non molto di più. Spesso, i dati su uno specifico evento devono essere passati insieme all'oggetto Event, in modo che gli ascoltatori ottengano le informazioni necessarie. Nel caso dell'evento kernel.response, l'oggetto Event creato e passato a ciascun ascoltatore è in effetti di tipo FilterResponseEvent, una sottoclasse dell'oggetto base Event. Questa classe contiene metodi come getresponse e setresponse, che consentono agli ascoltatori di ottenere, o anche sostituire, l'oggetto Response. La morale della favola è questa: quando si crea un ascoltatore per un evento, l'oggetto Event passato all'ascoltatore può essere una speciale sotto-classe, con metodi aggiuntivi per recuperare informazioni dall'evento e per rispondere all'evento.. https://packagist.org/packages/symfony/event-dispatcher. https://github.com/symfony/eventdispatcher. generated on August, 0 Chapter : Il componente EventDispatcher

152 Il distributore Il distributore è l'oggetto centrale del sistema di distribuzione degli eventi. In generale, viene creato un solo distributore, che mantiene un registro di ascoltatori. Quando un evento viene distribuito dal distributore, esso notifica a tutti gli ascoltatori registrati a tale evento: Listing - use Symfony\Component\EventDispatcher\EventDispatcher; $dispatcher = new EventDispatcher(); Connettere gli ascoltatori Per sfruttare un evento esistente, occorre connettere un ascoltatore al distributore, in modo che riceva una notifica quando l'evento viene distribuito. Una chiamata al metodo addlistener() del distributore associa un qualsiasi callable PHP a un evento: Listing - $listener = new AcmeListener(); $dispatcher->addlistener('pippo.action', array($listener, 'onpippoaction')); Il metodo addlistener() accetta fino a tre parametri: Il nome dell'evento (stringa) che questo ascoltatore vuole ascoltare; Un callable PHP, che sarà notificato quando viene lanciato un evento che sta ascoltando; Un intero opzionale di priorità (più alto equivale a più importante, quindi l'ascoltatore scatterà prima), che determina quando far scattare un ascoltatore, rispetto ad altri (predefinito a 0). Se due ascoltatori hanno la medesima priorità, sono eseguiti nell'ordine in cui sono stati aggiunti al distributore. Un callable PHP è una variabile PHP che possa essere usata dalla funzione call_user_func() e che restituisca true se passata alla funzione is_callable(). Può essere un'istanza di \Closure, un oggetto che implementi un metodo invoke (che è ciò che in effetti sono le closure), una stringa che rappresenti una funzione, o infine un array che rappresenti il metodo di un oggetto o di una classe. Finora, abbiamo visto che oggetti PHP possano essere registrati come ascoltatori. Si possono anche registrare Closure PHP come ascoltatori di eventi: Listing - use Symfony\Component\EventDispatcher\Event; $dispatcher->addlistener('pippo.action', function (Event $event) // sarà eseguito quando l'evento pippo.action sarà distribuito ); Una volta registrato un evento sul distributore, esso aspetterà finché l'evento non sarà notificato. Nell'esempio precedente, quando l'evento pippo.action viene distribuito, il distributore richiama il metodo AcmeListener::onPippoAction e passa l'oggetto Event come singolo parametro: Listing - use Symfony\Component\EventDispatcher\Event; class AcmeListener. generated on August, 0 Chapter : Il componente EventDispatcher

153 0 public function onpippoaction(event $event) fare qualcosa In molti casi, viene passata all'ascoltatore una speciale sotto-classe Event, che è specifica dell'evento dato. Questo dà accesso all'ascoltatore a informazioni speciali sull'evento. Leggere la documentazione o l'implementazione di ciascun evento, per determinare l'esatta istanza Symfony\Component\EventDispatcher\Event passata. Per esempio, l'evento kernel.response passa un'istanza di Symfony\Component\HttpKernel\Event\FilterResponseEvent: Listing - use Symfony\Component\HttpKernel\Event\FilterResponseEvent; public function onkernelresponse(filterresponseevent $event) $response = $event->getresponse(); $request = $event->getrequest(); generated on August, 0 Chapter : Il componente EventDispatcher

154 Registrare ascoltatori di eventi nel contenitore di servizi Quando si usa ContainerAwareEventDispatcher e il componente DependencyInjection, si può usare RegisterListenersPass del componente HttpKernel per assegnare il tag di ascoltatore di eventi ai servizi: Listing use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass; $containerbuilder = new ContainerBuilder(new ParameterBag()); $containerbuilder->addcompilerpass(new RegisterListenersPass()); // registra il servizio come sottoscrittore di eventi $containerbuilder->setdefinition('event_dispatcher', new Definition( 'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher', array(new Reference('service_container')) )); // registra il servizio come ascoltatore di eventi $listener = new Definition('AcmeListener'); $listener->addtag('kernel.event_listener', array( 'event' => 'foo.action', 'method' => 'onfooaction', )); $containerbuilder->setdefinition('listener_service_id', $listener); // registra un sottoscrittore di eventi $subscriber = new Definition('AcmeSubscriber'); $subscriber->addtag('kernel.event_subscriber'); $containerbuilder->setdefinition('subscriber_service_id', $subscriber); Per impostazione predefinita, RegisterListenersPass presume che l'id del servizio del distributore di eventi sia event_dispatcher, che gli ascoltatori di eventi abbiano il tag kernel.event_listener e che i sottoscrittori di eventi abbiano il tag kernel.event_subscriber. Si possono cambiare tali valori predefiniti passando valori personalizzati al costruttore di RegisterListenersPass. Creare e distribuire un evento Oltre a registrare ascoltatori con eventi esistenti, si possono creare e distribuire i propri eventi. Questo è utile quando si creano librerie di terze parti e anche quando si vogliono mantenere i vari componenti dei propri sistemi flessibili e disaccoppiati. La classe statica Events Si supponga di voler creare un nuovo evento, chiamato negozio.ordine, distribuito ogni volta che un ordine viene creato dentro l'applicazione. Per mantenere le cose organizzate, iniziamo a creare una classe StoreEvents all'interno dell'applicazione, che serve a definire e documentare il proprio evento: Listing - namespace Acme\StoreBundle;. generated on August, 0 Chapter : Il componente EventDispatcher

155 0 final class StoreEvents /** * L'evento negozio.ordine è lanciato ogni volta che un ordine viene creato * nel sistema. * * L'ascoltatore dell'evento riceve un'istanza di * Acme\StoreBundle\Event\FilterOrderEvent. * string */ const STORE_ORDER = 'negozio.ordine'; Si noti che la class in realtà non fa nulla. Lo scopo della classe StoreEvents è solo quello di essere un posto in cui le informazioni sugli eventi comuni possano essere centralizzate. Si noti che anche che una classe speciale FilterOrderEvent sarà passata a ogni ascoltatore di questo evento. Creare un oggetto evento Più avanti, quando si distribuirà questo nuovo evento, si creerà un'istanza di Event e la si passerà al distributore. Il distributore quindi passa questa stessa istanza a ciascuno degli ascoltatori dell'evento. Se non si ha bisogno di passare informazioni agli ascoltatori, si può usare la classe predefinita Symfony\Component\EventDispatcher\Event. Tuttavia, la maggior parte delle volte, si avrà bisogno di passare informazioni sull'evento a ogni ascoltatore. Per poterlo fare, si creerà una nuova classe, che estende Symfony\Component\EventDispatcher\Event. In questo esempio, ogni ascoltatore avrà bisogno di accedere a un qualche oggetto Order. Creare una classe Event che lo renda possibile: Listing - 0 namespace Acme\StoreBundle\Event; use Symfony\Component\EventDispatcher\Event; use Acme\StoreBundle\Order; class FilterOrderEvent extends Event protected $order; public function construct(order $order) $this->order = $order; public function getorder() return $this->order; Ogni ascoltatore ora ha accesso all'oggetto Order, tramite il metodo getorder. Distribuire l'evento Il metodo dispatch() 0 notifica a tutti gli ascoltatori l'evento dato. Accetta due parametri: il nome dell'evento da distribuire e l'istanza di Event da passare a ogni ascoltatore di tale evento: 0. generated on August, 0 Chapter : Il componente EventDispatcher

156 Listing - 0 use Acme\StoreBundle\StoreEvents; use Acme\StoreBundle\Order; use Acme\StoreBundle\Event\FilterOrderEvent; // l'ordine viene in qualche modo creato o recuperato $order = new Order(); // creare FilterOrderEvent e distribuirlo $event = new FilterOrderEvent($order); $dispatcher->dispatch(storeevents::store_order, $event); Si noti che l'oggetto speciale FilterOrderEvent è creato e passato al metodo dispatch. Ora ogni ascoltatore dell'evento negozio.ordino riceverà FilterOrderEvent e avrà accesso all'oggetto Order, tramite il metodo getorder: Listing -0 // una qualche classe ascoltatore che è stata registrata per onstoreorder use Acme\StoreBundle\Event\FilterOrderEvent; public function onstoreorder(filterorderevent $event) $order = $event->getorder(); // fare qualcosa con l'ordine Usare i sottoscrittori Il modo più comune per ascoltare un evento è registrare un ascoltatore con il distributore. Questo ascoltatore può ascoltare uno o più eventi e viene notificato ogni volta che tali eventi sono distribuiti. Un altro modo per ascoltare gli eventi è tramite un sottoscrittore. Un sottoscrittore di eventi è una classe PHP che è in grado di dire al distributore esattamente quale evento dovrebbe sottoscrivere. Implementa l'interfaccia EventSubscriberInterface, che richiede un unico metodo statico, chiamato getsubscribedevents. Si consideri il seguente esempio di un sottoscrittore, che sottoscrive gli eventi kernel.response e negozio.ordine: Listing - 0 namespace Acme\StoreBundle\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; class StoreSubscriber implements EventSubscriberInterface public static function getsubscribedevents() return array( 'kernel.response' => array( array('onkernelresponsepre', 0), array('onkernelresponsemid', ), array('onkernelresponsepost', 0), ), 'negozio.ordine' => array('onstoreorder', 0), );. generated on August, 0 Chapter : Il componente EventDispatcher

157 0 0 public function onkernelresponsepre(filterresponseevent $event) public function onkernelresponsemid(filterresponseevent $event) public function onkernelresponsepost(filterresponseevent $event) public function onstoreorder(filterorderevent $event) È molto simile a una classe ascoltatore, tranne che la classe stessa può dire al distributore quali eventi dovrebbe ascoltare. Per registrare un sottoscrittore con il distributore, usare il metodo addsubscriber() Listing - use Acme\StoreBundle\Event\StoreSubscriber; $subscriber = new StoreSubscriber(); $dispatcher->addsubscriber($subscriber); Il distributore registrerà automaticamente il sottoscrittore per ciascun evento restituito dal metodo getsubscribedevents. Questo metodo restituisce un array indicizzata per nomi di eventi e i cui valori sono o i nomi dei metodi da chiamare o array composti dal nome del metodo e da una priorità. L'esempio precedente mostra come registrare diversi metodi ascoltatori per lo stesso evento in un sottoscrittore e mostra anche come passare una priorità a ciascun metodo ascoltatore. Più è alta la priorità, prima sarà chiamato il metodo. Nell'esempio precedente, quando viene lanciato l'evento kernel.response, i metodi onkernelresponsepre, onkernelresponsemid e onkernelresponsepost sono richiamati in questo ordine. Bloccare il flusso e la propagazione degli eventi In alcuni casi, potrebbe aver senso che un ascoltatore prevenga il richiamo di qualsiasi altro ascoltatore. In altre parole, l'ascoltatore deve poter essere in grado di dire al distributore di bloccare ogni propagazione dell'evento a futuri ascoltatori (cioè di non notificare più altri ascoltatori). Lo si può fare da dentro un ascoltatore, tramite il metodo stoppropagation() : Listing - use Acme\StoreBundle\Event\FilterOrderEvent; public function onstoreorder(filterorderevent $event). generated on August, 0 Chapter : Il componente EventDispatcher

158 $event->stoppropagation(); Ora, tutti gli ascoltatori di negozio.ordine che non sono ancora stati richiamati non saranno richiamati. Si può individuare se un evento è stato fermato, usando il metodo ispropagationstopped(), che restituisce un booleano: Listing - $dispatcher->dispatch('foo.event', $event); if ($event->ispropagationstopped()) Eventi e ascolatori consapevoli del distributore New in version.: Da Symfony. il nome dell'evento corrente ed EventDispatcher stesso sono passati agli ascoltatori come parametri aggiuntivi. EventDispatcher inietta sempre l'evento distribuito, il nome dell'evento e un riferimento a sé stesso agli ascoltatori. Questo può portare ad applicazioni avanzate per EventDispatcher, incluse la possibilità per gli ascoltatori di distribuire altri eventi, il concatenamento degli eventi o anche il caricamento pigro di più ascoltatori nell'oggetto distributore. Ecco degli esempi: Caricamento pigro degli ascoltatori: Listing use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Acme\StoreBundle\Event\StoreSubscriber; class Foo private $started = false; public function mylazylistener(event $event, $eventname, EventDispatcherInterface $dispatcher) if (false === $this->started) $subscriber = new StoreSubscriber(); $dispatcher->addsubscriber($subscriber); $this->started = true; eccetera Distribuzione di altri eventi da dentro un ascoltatore: Listing - use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class Foo public function myfoolistener(event $event, $eventname, EventDispatcherInterface. generated on August, 0 Chapter : Il componente EventDispatcher

159 0 $dispatcher) $dispatcher->dispatch('log', $event); eccetera Questo è sufficiente per la maggior parte dei casi, ma, se si ha un'applicazione con istanze multiple di EventDispatcher, potrebbe essere necessario iniettare specificatamente un'istanza nota di EventDispatcher nei propri ascoltatori. Questo è possibile tramite l'utilizzo dell'iniezione per costruttore o per setter, come segue: Iniezione per costruttore: Listing - 0 use Symfony\Component\EventDispatcher\EventDispatcherInterface; class Foo protected $dispatcher = null; public function construct(eventdispatcherinterface $dispatcher) $this->dispatcher = $dispatcher; Iniezione tramite setter: Listing - 0 use Symfony\Component\EventDispatcher\EventDispatcherInterface; class Foo protected $dispatcher = null; public function seteventdispatcher(eventdispatcherinterface $dispatcher) $this->dispatcher = $dispatcher; La scelta tra i due è una questione di gusti. Molti preferiscono l'iniezione per costruttore, perché l'oggetto in questo modo viene inizializzato durante la costruzione. Ma quando si ha una lunga lista di dipendenze, l'utilizzo dell'iniezione per setter può essere l'unico modo, specialmente per dipendenze opzionali. Scorciatoie del distributore Il metodo EventDispatcher::dispatch restituisce sempre un oggetto Event. Questo consente diverse scorciatoie. Per esempio, se non si ha bisogno di un oggetto evento personalizzato, ci si può appoggiare semplicemente su un oggetto Event. Non occorre nemmeno passarlo al distributore, perché ne sarà creato uno per impostazione predefinita, a meno che non venga passato specificatamente: Listing generated on August, 0 Chapter : Il componente EventDispatcher

160 $dispatcher->dispatch('foo.event'); Inoltre, EventDispatcher restituisce sempre quale oggetto evento è stato distribuito, cioè o l'evento passato o l'evento creato internamente dal distributore. Questo consente utili scorciatoie: Listing -0 if (!$dispatcher->dispatch('foo.event')->ispropagationstopped()) Oppure: Listing - $barevent = new BarEvent(); $bar = $dispatcher->dispatch('bar.event', $barevent)->getbar(); Oppure: Listing - $bar = $dispatcher->dispatch('bar.event', new BarEvent())->getBar(); e così via... Introspezione del nome dell'evento Poiché EventDispatcher conosce già il nome dell'evento al momento della distribuzione, il nome dell'evento è iniettato anche negli oggetti Event, quindi è disponibile agli ascoltatori dell'evento, tramite il metodo getname(). Il nome dell'evento (come ogni altro dato in un oggetto evento personalizzato) può essere usato come parte della logica di processamento dell'ascoltatore: Listing - use Symfony\Component\EventDispatcher\Event; class Foo public function myeventlistener(event $event) echo $event->getname(); Altri distributori Oltre a EventDispatcher, usato comunemente, il componente dispone di altri due distributori: Il distributore consapevole del contenitore L'Event Dispatcher Immutable. generated on August, 0 Chapter : Il componente EventDispatcher 0

161 Chapter Il distributore consapevole del contenitore Introduzione La classe ContainerAwareEventDispatcher è una speciale implementazione di distributore di eventi, accoppiata con il contenitore di servizi, che fa parte del component DependencyInjection. Questo consente di specificare i servizi come ascoltatori di eventi, rendendo EventDispatcher molto potente. Si servizi sono caricati in modo pigro, il che vuol dire che i servizi allegati come ascoltatori saranno creato solo se viene distribuito un evento che richieda tali ascoltatori. Preparazione La preparazione è molto semplice, basta iniettare un ContainerInterface in ContainerAwareEventDispatcher : Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; $container = new ContainerBuilder(); $dispatcher = new ContainerAwareEventDispatcher($container); Aggiungere ascoltatori Il distributore di eventi consapevole del contenitore può caricare direttamente servizi specifici, oppure servizi che implementino EventSubscriberInterface generated on August, 0 Chapter : Il distributore consapevole del contenitore

162 Gli esempi seguenti presumo che il DIC sia stato caricato con i servizi che vengono menzionati. I servizi devono essere segnati come pubblici nel DIC. Aggiungere servizi Per collegare definizioni di servizi esistenti, usare il metodo addlistenerservice(), dove $callback è un array array($idservizio, $nomemetodo): Listing - $dispatcher->addlistenerservice($eventname, array('pippo', 'loglistener')); Aggiungere servizi sottoscrittori Si possono aggiungere degli EventSubscribers, usando il metodo addsubscriberservice(), dove il primo parametro è l'id del servizio sottoscrittore e il secondo parametro è il nome della classe del servizio (che deve implementare EventSubscriberInterface ), come segue: Listing - $dispatcher->addsubscriberservice( 'kernel.store_subscriber', 'StoreSubscriber' ); EventSubscriberInterface sarà esattamente come ci si può aspettare: Listing use Symfony\Component\EventDispatcher\EventSubscriberInterface; class StoreSubscriber implements EventSubscriberInterface public static function getsubscribedevents() return array( 'kernel.response' => array( array('onkernelresponsepre', 0), array('onkernelresponsepost', 0), ), 'store.order' => array('onstoreorder', 0), ); public function onkernelresponsepre(filterresponseevent $event) public function onkernelresponsepost(filterresponseevent $event). generated on August, 0 Chapter : Il distributore consapevole del contenitore

163 0 public function onstoreorder(filterorderevent $event) generated on August, 0 Chapter : Il distributore consapevole del contenitore

164 Chapter Oggetto evento generico La classe base Event fornita dal componente Event Dispatcher è deliberatamente breve, per consentire la creazione di oggetti evento con API specifiche, usando l'ereditarietà. Questo consente un codice elegante e leggibile, anche in applicazioni complesse. La classe GenericEvent è disponibile per comodità per chi volesse usare un solo oggetto evento in tutta la propria applicazione. È adatta alla maggior parte degli scopi, senza modifiche, perché segue il pattern observer standard, in cui gli oggetti evento incapsulano il soggetto ("subject") di un evento, ma anche alcuni parametri in più. GenericEvent ha una semplice API, in aggiunta alla classe base Event construct() : il costruttore accetta il soggetto dell'evento e qualsiasi parametro; getsubject() : restituisce il soggetto; setargument() : imposta un parametro per chiave; setarguments() : imposta un array di parametri; getargument() : restituisce un parametro per chiave; getarguments() 0 : restituisce un array di parametri; hasargument() : restituisce true se il parametro esiste; GenericEvent implementa anche ArrayAccess sui parametri dell'evento, il che lo rende molto utile per passare parametri ulteriori, che riguardino il soggetto dell'evento. Gli esempi seguenti mostrano dei casi d'uso, che danno un'idea generale della flessibilità. Gli esempi presumono che gli ascoltatori siano stati aggiunti al distributore di eventi. Passare semplicemente un soggetto: construct() generated on August, 0 Chapter : Oggetto evento generico

165 Listing - 0 use Symfony\Component\EventDispatcher\GenericEvent; $event = GenericEvent($subject); $dispatcher->dispatch('pippo', $event); class PippoListener public function handler(genericevent $event) if ($event->getsubject() instanceof Pippo) Passare e processare parametri usando l'api ArrayAccess per accedere ai parametri dell'evento: Listing use Symfony\Component\EventDispatcher\GenericEvent; $event = new GenericEvent( $subject, array('type' => 'pippo', 'counter' => 0) ); $dispatcher->dispatch('pippo', $event); echo $event['counter']; class PippoListener public function handler(genericevent $event) if (isset($event['type']) && $event['type'] === 'pippo') fare qualcosa $event['counter']++; Filtrare i dati: Listing - 0 use Symfony\Component\EventDispatcher\GenericEvent; $evento = new GenericEvent($subject, array('data' => 'pippo')); $dispatcher->dispatch('pippo', $evento); echo $event['data']; class PippoListener public function filter(genericevent $evento) $event['data'] = strtolower($evento['data']);. generated on August, 0 Chapter : Oggetto evento generico

166 Chapter L'Event Dispatcher Immutable ImmutableEventDispatcher è un distributore di eventi bloccato o congelato. Il distributore non può registrare nuovi ascoltatori o sottoscrittori. ImmutableEventDispatcher accetta un altro distributore di eventi, con tutti gli ascoltatori e i sottoscrittore. Il distributore immutabile è solo un proxy di tale distributore originale. Per poterlo usare, creare dapprima un distributore normale (EventDispatcher o ContainerAwareEventDispatcher) e registrare degli ascoltatori o dei sottoscrittori: Listing - use Symfony\Component\EventDispatcher\EventDispatcher; $dispatcher = new EventDispatcher(); $dispatcher->addlistener('pippo.azione', function ($event) ); Quindi, iniettarlo in un ImmutableEventDispatcher: Listing - use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; $immutabledispatcher = new ImmutableEventDispatcher($dispatcher); Si dovrà usare tale nuovo distributore nel proprio progetto. Se si prova a eseguire uno dei metodi che modificano il distributore (p.e. addlistener), verrà lanciata una BadMethodCallException.. generated on August, 0 Chapter : L'Event Dispatcher Immutable

167 Chapter Distributore di eventi tracciabile New in version.: La classe TraceableEventDispatcher è stata spostata nel componente EventDispatcher in Symfony.. In precedenza, si trovava nel componente HttpKernel. TraceableEventDispatcher è un distributore di eventi che avvolge ogni altro distributore di eventi e può quindi essere usato per determinare quale ascoltatori di eventi siano stati richiamati dal distributore. Passare il distributore di eventi da avvolgere e un'istanza di Stopwatch al suo costruttore: Listing - 0 use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; use Symfony\Component\Stopwatch\Stopwatch; // distributore di eventi su cui fare debug $eventdispatcher =...; $traceableeventdispatcher = new TraceableEventDispatcher( $eventdispatcher, new Stopwatch() ); Si può quindi usare TraceableEventDispatcher come qualsiasi altro distributore di eventi, per registrare ascoltatori di eventi e distribuire eventi: Listing - 0 // registrare un ascoltatore di eventi $eventlistener =...; $priority =...; $traceableeventdispatcher->addlistener('nome-evento, $eventlistener, $priority); // distribuire un evento $event =...; $traceableeventdispatcher->dispatch('nome-evento', $event);. generated on August, 0 Chapter : Distributore di eventi tracciabile

168 Dopo che un'applicazione è stata processata, si può usare il metodo getcalledlisteners() per recuperare un array di ascoltatori di eventi che sono stati richiamati nell'applicazione. In modo simile il metodo getnotcalledlisteners() restituisce un array di ascoltatori di eventi che non sono stati chiamati: Listing - $calledlisteners = $traceableeventdispatcher->getcalledlisteners(); $notcalledlisteners = $traceableeventdispatcher->getnotcalledlisteners();. TraceableEventDispatcherInterface.html#method_getCalledListeners. TraceableEventDispatcherInterface.html#method_getNotCalledListeners generated on August, 0 Chapter : Distributore di eventi tracciabile

169 Chapter Il componente ExpressionLanguage Il componente ExpressionLanguage fornisce un motore che compila e valuta espressioni. Un'espressione è una singola riga che restituisce un valore (soprattutto, ma non sempre, un booleano). New in version.: Il componente ExpressionLanguage è stato introdotto in Symfony.. Installazione Si può installare il componente in due modi: Installarlo tramite Composer (symfony/expression-language su Packagist ); Usare il repository ufficiale Git (https://github.com/symfony/expression-language ). In che modo il linguaggio delle espressioni può essere utile? Lo scopo del componente è consentire agli utenti di usare espressioni all'interno della configurazione, per logiche più complesse. Per esempio, il framework Symfony usa espressioni nella sicurezza, per le regole di validazione e per la corrispondenza di rotte. Oltre a usare il componente nel framework, il componente ExpressionLanguage è un perfetto candidato per la fondazione di un motore di regole di business. L'idea è quella di lasciare al webmaster di un sito la possibilità di configurare le cose in modo dinamico, usando PHP e senza introdurre problemi di sicurezza: Listing - # Ottieni il prezzo speciale se user.getgroup() in ['good_customers', 'collaborator']. https://packagist.org/packages/symfony/expression-language. https://github.com/symfony/expression-language generated on August, 0 Chapter : Il componente ExpressionLanguage

170 # Promuovi l'articolo in homepage quando article.commentcount > 00 and article.category not in ["misc"] # Invia un avviso quando product.stock < Si possono vedere le espressioni come una sanbox PHP molto ristretta e immune a intrusioni esterne, dovendo dichiarare esplicitamente quali variabili sono disponibili in un'espressione. Uso Il componente ExpressionLanguage può compilare e valutare espressioni. Le espressioni sono righe che spesso restituiscono un booleano, che può essere usato nel codice che esegue l'espressione in un costrutto if. Un semplice esempio di espressione è +. Si possono usare espressioni più complesse, come unarray[].unmetodo('pluto'). Il componente fornisce due modi di lavorare con le espressioni: valuazione: l'espressione viene valutata senza essere compilata in PHP; compilazone: l'espressione viene compilata in PHP, in modo da poter essere messa in cache e valutata. La classe principale del componente è ExpressionLanguage : Listing - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; $language = new ExpressionLanguage(); echo $language->evaluate(' + '); // mostra echo $language->compile(' + '); // mostra ( + ) Sintassi delle espressioni Vedere Sintassi di Expression per conoscere la sintassi del componente ExpressionLanguage. Passare variabili Si possono anche passare variabili dentro a un'espressione, le quali possono essere di qualsiasi tipo valido in PHP (inclusi oggetti): Listing - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; $language = new ExpressionLanguage(); class Apple public $variety;. generated on August, 0 Chapter : Il componente ExpressionLanguage 0

171 0 $apple = new Apple(); $apple->variety = 'Honeycrisp'; echo $language->evaluate( 'fruit.variety', array( 'fruit' => $apple, ) ); Questo codice mostrerà "Honeycrisp". Per maggiori informazioni, vedere Sintassi di Expression, in particolare Lavorare con gli oggetti e Lavorare con gli array. Cache Il componente fornisce varie strategie di cache, si può approfondire in Cache di espressioni analizzate. generated on August, 0 Chapter : Il componente ExpressionLanguage

172 Chapter Sintassi di Expression Il componente ExpressionLanguage usa una sintassi specifica, basata sulla sintassi delle espressioni di Twig. In questo documento si potranno trovare tutte le sintassi supportate. Letterali supportati Il componente supporta: stringhe - con virgolette singole e doppie (p.e. 'ciao') numeri - p.e. 0 array - con notazione tipo JSON (p.e. [, ]) hash - con notazione tipo JSON (p.e. pippo: 'pluto' ) booleani - true e false nullo - null Lavorare con gli oggetti Quando si passano oggetti in un'espressione, si possono usare varie sintassi per accedere a proprietà e richiamare metodi. Accedere a proprietà pubbliche Si può accedere a proprietà pubbliche degli oggetti usando la sintassi., similmente a JavaScript: Listing - class Apple public $variety; $apple = new Apple(); $apple->variety = 'Honeycrisp'; generated on August, 0 Chapter : Sintassi di Expression

173 0 echo $language->evaluate( 'fruit.variety', array( 'fruit' => $apple, ) ); Mostrerà Honeycrisp. Richiamare metodi Si può usare la sintassi. anche per richiamare metodi dell'oggetto, similmente a JavaScript: Listing class Robot public function sayhi($times) $greetings = array(); for ($i = 0; $i < $times; $i++) $greetings[] = 'Ciao'; return implode(' ', $greetings).'!'; $robot = new Robot(); echo $language->evaluate( 'robot.sayhi()', array( 'robot' => $robot, ) ); Mostrerà Ciao Ciao Ciao!. Lavorare con le funzioni Si possono anche usare funzioni registrate nell'espressione, usando la stessa sintassi di PHP e JavaScript. Il componente ExpressionLanguage dispone già di una funzione: constant(), che restituisce il valore di una costante PHP: Listing - define('utente_db', 'root'); echo $language->evaluate( 'constant("utente_db")' ); Mostrerà root. generated on August, 0 Chapter : Sintassi di Expression

174 Le sapere come registrare funzioni da usare in un'espressione, vedere "Estendere ExpressionLanguage". Lavorare con gli array Se si passa un array in un'espressione, usare la sintassi [] per accedere all'array, similmente a JavaScript: Listing - $data = array('vita' => 0, 'universo' => 0, 'tutto_quanto' => ); echo $language->evaluate( 'data["vita"] + data["universo"] + data["tutto_quanto"]', array( 'data' => $data, ) ); Mostrerà. Operatori supportati Il componente disponde di vari operatori: Operatori aritmetici + (addizione) - (sottrazione) * (moltiplicazione) / (divisione) % (modulo) ** (potenza) Per esempio: Listing - echo $language->evaluate( 'vita + universo + tutto_quanto', array( 'vita' => 0, 'universe' => 0, 'tutto quanto' =>, ) ); Mostrerà. Operatori di bit & (and) (or) ^ (xor) generated on August, 0 Chapter : Sintassi di Expression

175 Operatori di confronto == (uguale) === (identico)!= (diverso)!== (non identico) < (minore) > (maggiore) <= (minore o uguale) >= (maggiore o uguale) matches (espressione regolare) Per verificare che una stringa non soddisfi un'espressione regolare, usare l'operatore logico not in combinazione con l'operatore matches: Listing - $language->evaluate('not ("pippo" matches "/pluto/")'); // restituisce true Si devono usare le parentesi, perché l'operatore unario not ha precedenza sull'operatore binario matches. Esempi: Listing - 0 $ret = $language->evaluate( 'vita == tutto_quanto', array( 'vita' => 0, 'universe' => 0, 'tutto quanto' =>, ) ); $ret = $language->evaluate( 'vita > tutto_quanto', array( 'vita' => 0, 'universe' => 0, 'tutto quanto' =>, ) ); Entrambe le variabili saranno impostate a false. Operatori logici not o! and o && or o Per esempio: Listing - $ret = $language->evaluate( 'vita < universo or vita < tutto_quanto', array( 'vita' => 0, generated on August, 0 Chapter : Sintassi di Expression

176 ); ) 'universe' => 0, 'tutto quanto' =>, La variabile $ret sarà impostata a true. Operatori di stringhe ~ (concatenazione) Per esempio: Listing - echo $language->evaluate( 'nome~" "~cognome', array( 'nome' => 'Arthur', 'cognome' => 'Dent', ) ); Mostrerà Arthur Dent. Operatori di array in (contiene) not in (non contiene) For example: Listing -0 0 class User public $group; $user = new User(); $user->group = 'risorse_umane'; $ingroup = $language->evaluate( 'user.group in ["risorse_umane", "marketing"]', array( 'user' => $user ) ); $ingroup sarà valutata a true. Operatori numerici.. (gamma) Per esempio: Listing - class User generated on August, 0 Chapter : Sintassi di Expression

177 0 public $age; $user = new User(); $user->age = ; $language->evaluate( 'user.age in..', array( 'user' => $user, ) ); Sarà valutata a true, perché user.age è compreso tra e. Operatori ternari pippo? 'sì' : 'no' pippo?: 'no' (uguale a pippo? pippo : 'no') pippo? 'sì' (uguale a pippo? 'sì' : '') generated on August, 0 Chapter : Sintassi di Expression

178 Chapter 0 Estendere ExpressionLanguage Si può estendere ExpressionLanguage, aggiungendo funzioni. Per esempio, nel framework Symfony, la sicurezza ha funzioni personalizzate per verificare il ruolo dell'utente. Se si vuole imparare come usare funzioni in un'espressione, leggere "Lavorare con le funzioni". Registrare funzioni Le funzioni sono registrate su una specifica istanza di ExpressionLanguage. Questo vuole dire che le funzioni possono essere usate in qualsiasi espressione eseguita da quella istanza. Per registrare una funzione, usare register(). Questo metodo ha tre parametri: name - il nome della funzione in un'espressione; compiler - una funzione eseguita quando si compila un'espressione usando la funzione; evaluator - una funzione eseguita quando l'espressione viene valutata. Listing 0-0 use Symfony\Component\ExpressionLanguage\ExpressionLanguage; $language = new ExpressionLanguage(); $language->register('lowercase', function ($str) return sprintf('(is_string(%$s)? strtolower(%$s) : %$s)', $str);, function ($arguments, $str) if (!is_string($str)) return $str; ); return strtolower($str);. generated on August, 0 Chapter 0: Estendere ExpressionLanguage

179 echo $language->evaluate('lowercase("hello")'); Questo mostrerà hello. Al compilatore e al valutatore viene passata come primo parametro una variabile arguments, che è uguale al secondo parametro per evaluate() o compile() (p.e. i "valori" quando si valuta o i "nomi" se si compila). Creare una nuova classe ExpressionLanguage Quando si usa la classe ExpressionLanguage in una libreria, si raccomanda di creare una nuova classe ExpressionLanguage e di registrarvi all'interno le funzioni. Sovrascrivere registerfunctions per aggiungere funzioni: Listing namespace Acme\AwesomeLib\ExpressionLanguage; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; class ExpressionLanguage extends BaseExpressionLanguage protected function registerfunctions() parent::registerfunctions(); // non dimenticare di registrare anche funzioni del nucleo $this->register('lowercase', function ($str) return sprintf('(is_string(%$s)? strtolower(%$s) : %$s)', $str);, function ($arguments, $str) if (!is_string($str)) return $str; ); return strtolower($str); generated on August, 0 Chapter 0: Estendere ExpressionLanguage

180 Chapter Cache di espressioni analizzate Il componente ExpressionLanguage fornisce già un metodo compile(), per consentire di mettere in cache le espressioni in puro PHP. Ma, internamente, il componente già mette in cache le espressioni analizzate, quindi le espressioni duplicate possono essere compilate e valutate più rapidamente. Il flusso di lavoro I metodi evaluate() e compile() necessitano entrambi di fare alcune cose, prima di poter restituire valori. Per evaluate(), questo overhead è più grande. Entrambi i metodi necessitano di spezzettare e analizzare l'espressione. Lo fanno tramite il metodo parse(). Questo metodo restituisce una ParsedExpression. Ora, il metodo compile() restituisce semplicemente la conversione in stringa di tale oggetto. Il metodo evaluate() deve ciclare tra i "nodi" (i pezzi di un'esoressione salvata in ParsedExpression) e valutarli al volo. Per risparmiare tempo, ExpressionLanguage mette in cache ParsedExpression, in modo da saltare i passi di spezzettamento e analisi con espressioni duplicate. La cache è eseguita da un'istanza di ParserCacheInterface (che usa un ArrayParserCache ). Si può personalizzare il comportamento, creando una ParserCache personalizzata e iniettandola nell'oggetto, tramite costruttore: Listing - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Acme\ExpressionLanguage\ParserCache\MyDatabaseParserCache; $cache = new MyDatabaseParserCache(...); $language = new ExpressionLanguage($cache); generated on August, 0 Chapter : Cache di espressioni analizzate 0

181 DoctrineBridge fornisce un'implementazione di ParserCache, che usa la libreria di cache di Doctrine, che fornisce cache per ogni sorta di strategia, come APC,filesystem e Memcached. Uso di espressioni analizzate e serializzate Sia evaluate() sia compile() possono gestire ParsedExpression e SerializedParsedExpression: Listing - // il metodo parse() restituisce una ParsedExpression $expression = $language->parse(' + ', array()); echo $language->evaluate($expression); // mostra Listing - use Symfony\Component\ExpressionLanguage\SerializedParsedExpression; $expression = new SerializedParsedExpression( serialize($language->parse(' + ', array())) ); echo $language->evaluate($expression); // mostra. https://github.com/symfony/doctrinebridge. generated on August, 0 Chapter : Cache di espressioni analizzate

182 Chapter Il componente Filesystem Il componente Filesystem fornisce utilità di base per il filesystem. New in version.: Il componente Filesystem è nuovo in Symfony.. In precedenza, la classe Filesystem si trovava nel componente HttpKernel. Installazione Si può installare il componente in due modi: Installarlo tramite Composer (symfony/filesystem su Packagist ). Usare il repository ufficiale su Git (https://github.com/symfony/filesystem ); Utilizzo La classe Filesystem è l'unico punto finale per le operazioni su filesystem: Listing - 0 use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Exception\IOException; $fs = new Filesystem(); try $fs->mkdir('/tmp/random/dir/'. mt_rand()); catch (IOException $e) echo "Errore durante la creazione della cartella";. https://packagist.org/packages/symfony/filesystem. https://github.com/symfony/filesystem. generated on August, 0 Chapter : Il componente Filesystem

183 New in version.: IOExceptionInterface e il suo metodo getpath sono nuovi in Symfony.. Prima della., occorreva catturare la classe IOException. I metodi mkdir(), exists(), touch(), remove(), chmod(), chown() e chgrp() 0 possono ricevere una stringa, un array o un oggetto che implementi Traversable come parametro. Mkdir mkdir() crea una cartella. Su filesystem di tipo posix, le cartelle sono create in modalità predefinita 0. Si può usare il secondo parametro per impostare la modalità: Listing - $fs->mkdir('/tmp/photos', 000); Si può passare un array o un oggetto Traversable come primo parametro. Exists exists() verifica la presenza di tutti i file o cartelle e restituisce false se un file manca: Listing - // questa cartella esiste, restituisce true $fs->exists('/tmp/photos'); // rabbit.jpg esiste, bottle.png non esiste, restituisce false $fs->exists(array('rabbit.jpg', 'bottle.png')); Si può passare un array o un oggetto Traversable come primo parametro. Copy copy() copia file. Se la destinazione esiste già, file file è copiato solo se la data di modifica del sorgente è precedente a quella della destinazione. Questo comportamento è modificabile tramite un terzo parametro booleano: Listing generated on August, 0 Chapter : Il componente Filesystem

184 // funziona solo se image-icc è stato modificato dopo image.jpg $fs->copy('image-icc.jpg', 'image.jpg'); // image.jpg sarà sovrascritto $fs->copy('image-icc.jpg', 'image.jpg', true); Touch touch() imposta l'ora di accesso e modifica di un file. Per impostazione predefinita, usa l'ora attuale. Si può impostare un'ora diversa con il secondo parametro. Il terzo parametro è l'ora di accesso: Listing - // imposta l'ora di accesso al timestamp attuale $fs->touch('file.txt'); // imposta l'ora di modifica a 0 secondi nel futuro $fs->touch('file.txt', time() + 0); // imposta l'ora di accessoa 0 secondi nel passato $fs->touch('file.txt', time(), time() - 0); Si può passare un array o un oggetto Traversable come primo parametro. Chown chown() è usato per cambiare il proprietario di un file. Il terzo parametro è un booleano per un'opzione ricorsiva: Listing - // imposta il proprietario del video lolcat a www-data $fs->chown('lolcat.mp', 'www-data'); // cambia il proprietario della cartella video ricorsivamente $fs->chown('/video', 'www-data', true); Si può passare un array o un oggetto Traversable 0 come primo parametro. Chgrp chgrp() è usato per cambiare il gruppo di un file. Il terzo parametro è un booleano per un'opzione ricorsiva: Listing - // imposta il gruppo del video lolcat a nginx $fs->chgrp('lolcat.mp', 'nginx'); generated on August, 0 Chapter : Il componente Filesystem

185 // cambia il gruppo della cartella video ricorsivamente $fs->chgrp('/video', 'nginx', true); Si può passare un array o un oggetto Traversable come primo parametro. Chmod chmod() è usato per modificare la modalità di un file. Il terzo parametro è un'opzione ricorsiva booleana: Listing - // imposta la modalità di video.ogg a 000 $fs->chmod('video.ogg', 000); // imposta ricorsivamente la modalità della cartella src $fs->chmod('src', 000, true); Si può passare un array o un oggetto Traversable come primo parametro. Remove remove() rimuove file, collegamenti simbolici, cartelle: Listing - $fs->remove(array('symlink', '/path/to/directory', 'activity.log')); Si può passare un array o un oggetto Traversable come primo parametro. Rename rename() rinomina file e cartelle: Listing -0 // rinomina un file $fs->rename('/tmp/processed_video.ogg', '/path/to/store/video_.ogg'); // rinomina una cartella $fs->rename('/tmp/files', '/path/to/store/files'); generated on August, 0 Chapter : Il componente Filesystem

186 symlink symlink() crea un collegamento simbolico dal sorgente alla destinazione. Se il filesystem non supporta i collegamenti simbolici, c'è un terzo parametro booleano: Listing - // crea un collegamento simbolico $fs->symlink('/percorso/della/sorgente', '/percorso/della/destinazione'); // duplica la cartella sorgente, se il filesystem // non supporta i collegamenti simbolici $fs->symlink('/percorso/della/sorgente', '/percorso/della/destinazione', true); makepathrelative makepathrelative() restituisce il percorso relativo di una cartella, data un'altra: Listing - // restituisce '../' $fs->makepathrelative( '/var/lib/symfony/src/symfony/', '/var/lib/symfony/src/symfony/component' ); // restituisce 'videos' $fs->makepathrelative('/tmp/videos', '/tmp') mirror mirror() 0 esegute il mirror di una cartella: Listing - $fs->mirror('/percorso/della/sorgente', '/percorso/della/destinazione'); isabsolutepath isabsolutepath() restiuisce true se il percorso dato è assoluto, false altrimenti: Listing - // restituisce true $fs->isabsolutepath('/tmp'); // restituisce true $fs->isabsolutepath('c:\\windows'); // restituisce false $fs->isabsolutepath('tmp'); // restituisce false $fs->isabsolutepath('../dir'); dumpfile New in version.: dumpfile è nuovo in Symfony generated on August, 0 Chapter : Il componente Filesystem

187 dumpfile() consente di esportare contenuti in un file. Lo fa in maniera atomica: scrive prima un file temporaneo e quindi lo sposta nella nuova posizione, in cui viene finalizzato. Questo vuol dire che l'utente vedrà sempre o il vecchio file completo o il nuovo file completo (ma mai un file parziale): Listing - $fs->dumpfile('file.txt', 'Ciao mondo'); Il file file.txt ora contiene Ciao mondo. Si può passare come terzo parametro una modalità di file. Gestione degli errori Quando si verifica un problema, viene sollevata un'eccezione che implementa ExceptionInterface o IOExceptionInterface. Viene sollevata una IOException se la creazione della cartella fallisce generated on August, 0 Chapter : Il componente Filesystem

188 Chapter LockHandler (TODO da tradurre...) generated on August, 0 Chapter : LockHandler

189 Chapter Il componente Finder Il componente Finder cerca file e cartelle tramite un'interfaccia intuitiva e "fluida". Installazione È possibile installare il componente in due modi: Installandolo tramite Composer (symfony/finder su Packagist ); Usando il repository ufficiale su Git (https://github.com/symfony/finder ). Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. Uso La classe Finder trova i file e/o le cartelle: Listing - use Symfony\Component\Finder\Finder; $finder = new Finder(); $finder->files()->in( DIR ); foreach ($finder as $file) // Stampa il percorso assoluto print $file->getrealpath()."\n";. https://packagist.org/packages/symfony/finder. https://github.com/symfony/finder. generated on August, 0 Chapter : Il componente Finder

190 0 // Stampa il percorso relativo del file, omettendo il nome del file stesso print $file->getrelativepath()."\n"; // Stampa il percorso relativo del file print $file->getrelativepathname()."\n"; $file è un'istanza di SplFileInfo la quale estende SplFileInfo che mette a disposizione i metodi per poter lavorare con i percorsi relativi. Il precedente codice stampa, ricorsivamente, i nomi di tutti i file della cartella corrente. La classe Finder implementa il concetto di interfaccia fluida, perciò tutti i metodi restituiscono un'istanza di Finder. Un Finder è un'istanza di un Iterator PHP. Perciò, invece di dover iterare attraverso Finder con un ciclo foreach, è possibile convertirlo in un array, tramite il metodo iterator_to_array, oppure ottenere il numero di oggetti in esso contenuti, con iterator_count. Quando si cerca in posizioni diverse, passate al metodo in(), internamente viene creato un iteratore separato per ogni posizione. Questo vuol dire che si ottengono vari insiemi di risultati, aggregati in uno solo. Poiché iterator_to_array 0 usa le chiavi degli insiemi di risultati, durante la conversione in array, alcune chiavi potrebbero essere duplicate e i loro valori sovrascritti. Lo si può evitare, passando false come secondo parametro di iterator_to_array. Criteri Ci sono molti modi per filtrare e ordinare i risultati. Posizione La posizione è l'unico parametro obbligatorio. Indica al finder la cartella da utilizzare come base per la ricerca: Listing - $finder->in( DIR ); Per cercare in diverse posizioni, è possibile concatenare diverse chiamate a in() : Listing - $finder->files()->in( DIR )->in('/altraparte'); Si possono usare caratteri jolly nelle cartelle, per cercare uno schema: Listing - $finder->in('src/symfony/*/*/resources'); generated on August, 0 Chapter : Il componente Finder 0

191 Ogni schema deve risolvere almeno un percorso di cartella. È possibile escludere cartelle dalla ricerca tramite il metodo exclude() : Listing - $finder->in( DIR )->exclude('ruby'); New in version.: Il metodo ignoreunreadabledirs() è stato aggiunto in Symfony.. È anche possibile ignorare le cartelle che non si ha il permesso di leggere: Listing - $finder->ignoreunreadabledirs()->in( DIR ); Visto che Finder utilizza gli iteratori di PHP, è possibile passargli qualsiasi URL con protocollo supportato: Listing - $finder->in('ftp://example.com/pub/'); Funziona anche con flussi definiti dall'utente: Listing - 0 use Symfony\Component\Finder\Finder; $s = new \Zend_Service_Amazon_S($chiave, $segreto); $s->registerstreamwrapper("s"); $finder = new Finder(); $finder->name('photos*')->size('< 00K')->date('since hour ago'); foreach ($finder->in('s://bucket-name') as $file) fare qualcosa print $file->getfilename()."\n"; Per approfondire l'argomento su come creare flussi personalizzati, si legga la documentazione degli stream. File o cartelle Il comportamento predefinito di Finder è quello di restituire file e cartelle, ma grazie ai metodi files() e directories(), è possibile raffinare i risultati: Listing - $finder->files(); $finder->directories(); Per seguire i collegamenti, è possibile utilizzare il metodo followlinks(): Listing generated on August, 0 Chapter : Il componente Finder

192 $finder->files()->followlinks(); Normalmente l'iteratore ignorerà i file dei VCS più diffusi. È possibile modificare questo comportamento, grazie al metodo ignorevcs(): Listing - $finder->ignorevcs(false); Ordinamento È possibile ordinare i risultati per nome o per tipo (prima le cartelle e poi i file): Listing - $finder->sortbyname(); $finder->sortbytype(); Si noti che i metodi sort*, per poter funzionare, richiedono tutti gli elementi ricercati. In caso di iteratori molto grandi, l'ordinamento potrebbe risultare lento. È anche possibile definire algoritmi di ordinamento personalizzati, grazie al metodo sort(): Listing - $sort = function (\SplFileInfo $a, \SplFileInfo $b) return strcmp($a->getrealpath(), $b->getrealpath()); ; $finder->sort($sort); Nomi dei file È possibile eseguire filtri sui nomi dei file, utilizzando il metodo name() : Listing - $finder->files()->name('*.php'); Il metodo name() accetta, come parametri, glob, stringhe o espressioni regolari: Listing - $finder->files()->name('/\.php$/'); Il metodo notnames() viene invece usato per escludere i file che corrispondono allo schema: Listing - $finder->files()->notname('*.rb'); Contenuti dei file Si possono filtrare file per contenuto, con il metodo contains() 0 : Listing generated on August, 0 Chapter : Il componente Finder

193 $finder->files()->contains('lorem ipsum'); Il metodo contains() accetta stringhe o espressioni regolari: Listing - $finder->files()->contains('/lorem\s+ipsum$/i'); Il metodo notcontains() esclude file che contengono lo schema dato: Listing - $finder->files()->notcontains('dolor sit amet'); Percorso Si possono filtrare file e cartelle per percorso, con il metodo path() : Listing -0 $finder->path('una/cartella/particolare'); Su tutte le piattaforme, bisogna usare la barra (cioè /) come separatore di cartelle. Il metodo path() accetta stringhe o espressioni regolari: Listing - $finder->path('pippo/pluto'); $finder->path('/^pippo\/pluto/'); Internamente, le stringhe sono convertite in espressioni regolari, tramite escape delle barre e aggiunta di delimitatori: Listing - nomecartella ===> /nomecartella/ a/b/c ===> /a\/b\/c/ Il metodo notpath() esclude i file per percorso: Listing - $finder->notpath('altra/cartella'); Dimensione dei file Per filtrare i file in base alla dimensione, si usa il metodo size() : Listing - $finder->files()->size('<.k'); Si possono filtrare i file di dimensione compresa tra due valori, concatenando le chiamate: Listing - $finder->files()->size('>= K')->size('<= K'); È possibile utilizzare uno qualsiasi dei seguenti operatori di confronto: >, >=, <, <=, ==,!=. La dimensione può essere indicata usando l'indicazione in kilobyte (k, ki), megabyte (m, mi) o in gigabyte (g, gi). Gli indicatori che terminano con i utilizzano l'appropriata versione **n, in accordo allo standard IEC. generated on August, 0 Chapter : Il componente Finder

194 Data dei file È possibile filtrare i file in base alla data dell'ultima modifica, con il metodo date() : Listing - $finder->date('since yesterday'); È possibile utilizzare uno qualsiasi dei seguenti operatori di confronto: >, >=, <, '<=', '=='. È anche possibile usare i sostantivi since o after come degli alias di > e until o before come alias di <. Il valore usato può essere una data qualsiasi tra quelle supportate dalla funzione strtotime. Profondità della ricerca Normalmente, Finder attraversa ricorsivamente tutte le cartelle. Per restringere la profondità dell'attraversamento, si usa il metodo depth() : Listing - $finder->depth('== 0'); $finder->depth('< '); Filtri personalizzati È possibile definire filtri personalizzati, grazie al metodo filter() : Listing - $filtro = function (\SplFileInfo $file) if (strlen($file) > 0) return false; ; $finder->files()->filter($filtro); Il metodo filter() prende una Closure come argomento. Per ogni file che corrisponde ai criteri, la Closure viene chiamata passandogli il file come un'istanza di SplFileInfo. Il file sarà escluso dal risultato della ricerca nel caso in cui la Closure restituisca false. Leggere il contenuto dei file restituiti Il contenuto dei file restituiti può essere letto con getcontents() 0 : Listing - use Symfony\Component\Finder\Finder; $finder = new Finder(); $finder->files()->in( DIR ); foreach ($finder as $file) $contents = $file->getcontents(); generated on August, 0 Chapter : Il componente Finder

195 0 generated on August, 0 Chapter : Il componente Finder

196 Chapter Il componente Form Il componente Form consente di creare, processare e riusare facilmente form HTML. Il componente Form è uno strumento che aiuta a risolvere il problema di consentire agli utenti finali di interagire con i dati e modificare i dati in un'applicazione. Sebbene, tradizionalmente, ciò viene fatto tramite form HTML, il componente si focalizza sul processamento dei dati da e verso il client e l'applicazione, sia che i dati vengano da un classico form, sia che vengano da un'api. Installazione Si può installare il componente in due modi: Installarlo tramite Composer (symfony/form su Packagist ). Usare il repository ufficiale Git (https://github.com/symfony/form ); Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l'applicazione non sarà in grado di trovare le classi di questo componente di Symfony. Configurazione Se si lavora con il framework Symfony, il componente Form è già configurato. In questo caso, passare a Creazione di un semplice form. In Symfony, i form sono rappresentati da oggetti e tali oggetti sono costruiti usando un factory di form. Costruire un factory di form è semplice:. https://packagist.org/packages/symfony/form. https://github.com/symfony/form generated on August, 0 Chapter : Il componente Form

197 Listing - use Symfony\Component\Form\Forms; $formfactory = Forms::createFormFactory(); Questo factory può già essere usato per creare form di base, ma manca di supporto per alcune cose importanti: Gestione della richiesta: Supporto per gestione delle richieste e caricamento di file; Protezione da CSRF: Supporto per protezione contro attacchi di tipo Cross-Site-Request- Forgery (CSRF); Template: Integrazione con uno strato di template, che consenta di riutilizzare frammenti HTML usando un form; Traduzione: Supporto per la traduzione di messaggi di errore, label dei campi e altre stringhe; Validazione: Integrazione con una libreria di validazione, per generare messaggi di errore per i dati inseriti. Il componente Form si appoggia su altre librerie per risolvere questi problemi. La maggior parte delle volte si useranno i componenti Twig e HttpFoundation, Translation e Validator, ma si possono sostituire tutte queste librerie con altre a scelta. Le sezioni seguenti spiegano come usare queste librerie insieme al factory di form. Per un esempio funzionante, si veda https://github.com/bschussek/standalone-forms Gestione della richiesta New in version.: Il metodo handlerequest() è stato introdotto in Symfony.. Per processare i dati di un form, occorre richiamare il metodo handlerequest() : Listing - $form->handlerequest(); Dietro le quinte, viene usato un oggetto NativeRequestHandler per leggere i dati dalle opportune variabili di PHP ($_POST o $_GET), in base al metodo HTTP configurato nel form (quello predefinito è POST). Se occorre maggiore controllo su come esattamente il form viene inviato o su quali dati vi siano passati, si può usare submit(). Per saperne di più, vedere il ricettario.. https://github.com/bschussek/standalone-forms. generated on August, 0 Chapter : Il componente Form

198 Integrazione con il componente HttpFoundation Per l'integrazione con HttpFoundation, aggiungere HttpFoundationExtension al factory di form: Listing - use Symfony\Component\Form\Forms; use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension; $formfactory = Forms::createFormFactoryBuilder() ->addextension(new HttpFoundationExtension()) ->getformfactory(); Ora, quando si processa un form, si può passare l'oggetto Request a handlerequest() : Listing - $form->handlerequest($request); Per maggiori informazioni sul componente HttpFoundation e su come installarlo, vedere Il componente HttpFoundation. Protezione da CSRF La protezione da attacchi CSRF è compresa nel componente Form, ma occorre abilitarla esplicitamente o rimpiazzarla con una soluzione personalizzata. Il codice seguente aggiunge la protezione da CSRF al factory di form: Listing - 0 use Symfony\Component\Form\Forms; use Symfony\Component\Form\Extension\Csrf\CsrfExtension; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; use Symfony\Component\HttpFoundation\Session\Session; // generare in qualche modo una parola segreta $csrfsecret = '<generated token>'; // creare un oggetto sessione da HttpFoundation $session = new Session(); $csrfprovider = new SessionCsrfProvider($session, $csrfsecret); $formfactory = Forms::createFormFactoryBuilder() ->addextension(new CsrfExtension($csrfProvider)) ->getformfactory(); Per proteggere un'applicazione da attacchi CSRF, occorre definire una parola segreta. Generare una stringa casuale con almeno caratteri, inserirla nel codice appena visto e assicurarsi che nessuno, tranne il server web, possa accedervi. Internamente, l'estensione aggiungerà automaticamente a ogni form un campo nascosto (chiamato token), il cui valore è automaticamente generato e validato.. generated on August, 0 Chapter : Il componente Form

199 Se non si usa il componente HttpFoundation, usare DefaultCsrfProvider 0, che si basa sulla gestione nativa di PHP delle sessioni: Listing - use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; $csrfprovider = new DefaultCsrfProvider($csrfSecret); Template Twig Se si usa il componente Form per processare form HTML, occorrerà un modo per rendere facilmente i form come campi HTML (completi con valori, errori e label). Se si usa Twig come motore di template, il componente Form offre una ricca integrazione. Per usare tale integrazione, occorre TwigBridge, che integra Twig con vari componenti di Symfony. Usando Composer, si può installare la versione. più recente. aggiungendo la seguente riga al file composer.json: Listing - "require": "symfony/twig-bridge": "..*" L'integrazione TwigBridge fornisce varie funzioni Twig, che aiutano a rendere ciascun widget, label ed errore per ogni campo (insieme ad alcune altre cose). Per configurare l'integrazione, occorrerà accedere a Twig e aggiungere FormExtension : Listing use Symfony\Component\Form\Forms; use Symfony\Bridge\Twig\Extension\FormExtension; use Symfony\Bridge\Twig\Form\TwigRenderer; use Symfony\Bridge\Twig\Form\TwigRendererEngine; // il file Twig con tutti i tag per i form // questo file fa parte di TwigBridge $defaultformtheme = 'form_div_layout.html.twig'; $vendordir = realpath( DIR.'/../vendor'); // percorso di TwigBridge, che consente a Twig di trovare il file // form_div_layout.html.twig $vendortwigbridgedir = $vendordir. '/symfony/twig-bridge/symfony/bridge/twig'; // percorso degli altri template $viewsdir = realpath( DIR.'/../views'); $twig = new Twig_Environment(new Twig_Loader_Filesystem(array( $viewsdir, $vendortwigbridgedir.'/resources/views/form', ))); $formengine = new TwigRendererEngine(array($defaultFormTheme)); $formengine->setenvironment($twig); // aggiunge FormExtension a Twig generated on August, 0 Chapter : Il componente Form

200 0 $twig->addextension( new FormExtension(new TwigRenderer($formEngine, $csrfprovider)) ); // creare il factory, come al solito $formfactory = Forms::createFormFactoryBuilder() ->getformfactory(); I dettagli esatti della configurazione di Twig possono variare, ma lo scopo è sempre quello di aggiungere FormExtension a Twig, che dà accesso alle funzioni Twig functions per rendere i form. Per poterlo fare, occorre prima creare un TwigRendererEngine, in cui definire i propri form themes (cioè file o risorse che definiscono i tag HTML per i form). Per dettagli sulla resa dei form, vedere Personalizzare la resa dei form. Se si usa l'integrazione con Twig, leggere "Traduzione" più avanti, per i dettagli sui necessari filtri di traduzione. Traduzione Se si usa l'integrazione con Twig con uno dei file di temi di form predefiniti (come form_div_layout.html.twig), ci sono due filtri Twig (trans e transchoice), usati per tradurre label, errori, opzioni e altre stringhe. Per aggiungere questi filtri, si può usare TranslationExtension, che si integra con il componente Translation, oppure aggiungere i due filtri a mano, tramite un'estensione Twig. Per usare l'integrazione predefinita, assicurarsi che il progetto abbia i componenti Translation e Config installati. Se si usa Composer, si possono ottenere le versioni. più recenti di entrambi aggiungendo le seguenti righe al file composer.json: Listing - "require": "symfony/translation": "..*", "symfony/config": "..*" Aggiungere quindi TranslationExtension all'istanza di Twig_Environment: Listing -0 use Symfony\Component\Form\Forms; use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Loader\XliffFileLoader; use Symfony\Bridge\Twig\Extension\TranslationExtension; // creare il Translator $translator = new Translator('en'); // caricare traduzioni in qualche modo generated on August, 0 Chapter : Il componente Form 00

201 0 0 $translator->addloader('xlf', new XliffFileLoader()); $translator->addresource( 'xlf', DIR.'/percorso/delle/traduzioni/messages.en.xlf', 'en' ); // aggiungere TranslationExtension (fornisce i filtri trans e transchoice) $twig->addextension(new TranslationExtension($translator)); $formfactory = Forms::createFormFactoryBuilder() ->getformfactory(); A seconda di come sono state caricate le traduzioni, si possono ora aggiungere chiavi stringa, come label di campi, e le loro traduzioni nei file di traduzione. Per maggiori dettagli sulle traduzioni, vedere Traduzioni. Validazione Il componente Form dispone di un'integrazione stretta (ma facoltativa) con il componente Validator di Symfony. Si può anche usare una soluzione diversa per la validazione. Basta prendere i dati inseriti nel form (che sono un array o un oggetto) e passarli al proprio sistema di validazione. Per usare l'integrazione con il componente Validator, assicurarsi innanzitutto di installarlo nell'applicazione. Se si usa Composer e si vogliono installare le versioni. più recenti, aggiungere a composer.json: Listing - "require": "symfony/validator": "..*" Chi non avesse familiarità con il componente Validator può approfondire su Validazione. Il componente Form dispone di una classe ValidatorExtension, che applica automaticamente la validazione ai dati. Gli errori sono quindi mappati sui rispettivi campi e resi. L'integrazione con il componente Validation sarà simile a questa: Listing - 0 use Symfony\Component\Form\Forms; use Symfony\Component\Form\Extension\Validator\ValidatorExtension; use Symfony\Component\Validator\Validation; $vendordir = realpath( DIR.'/../vendor'); $vendorformdir = $vendordir.'/symfony/form/symfony/component/form'; $vendorvalidatordir = $vendordir.'/symfony/validator/symfony/component/validator'; // creare il validatore (i dettagli possono variare) $validator = Validation::createValidator(); // ci sono traduzioni predefinite per i messaggi di errore principali $translator->addresource( 'xlf',. generated on August, 0 Chapter : Il componente Form 0

202 0 0 $vendorformdir.'/resources/translations/validators.en.xlf', 'en', 'validators' ); $translator->addresource( 'xlf', $vendorvalidatordir.'/resources/translations/validators.en.xlf', 'en', 'validators' ); $formfactory = Forms::createFormFactoryBuilder() ->addextension(new ValidatorExtension($validator)) ->getformfactory(); Per approfondire, vedere la sezione Validazione di form. Accesso al factory dei form L'applicazione ha bisogno di un unico factory di form, quello che andrebbe usato per creare tutti gli oggetti form nell'applicazione. Questo vuol dire che andrebbe creato in una parte centralizzata iniziale dell'applicazione e quindi acceduto ovunque ci sia bisogno di costruire un form. In questo documento, il factory di form è sempre una variabile locale, chiamata $formfactory. Il punto è che probabilmente si avrà la necessità di creare questo oggetto in un qualche modo "globale", per potervi accedere ovunque. Il modo esatto in cui si accede al factory di form dipende dallo sviluppatore. Se si usa un Contenitore di servizi, si dovrebbe aggiungere il factory di form al contenitore e recuperarlo all'occorrenza. Se l'applicazione usa variabili globali o statiche (di solito una cattiva idea), si può memorizzare l'oggetto in una classe statica o qualcosa del genere. Indipendentemente dall'architettura dell'applicazione, si ricordi che si dovrebbe avere solo un factory di form e che occorrerà accedervi in ogni parte dell'applicazione. Creazione di un semplice form Se si usa il framework Symfony, il factory di form è disponibile automaticamente come servizio, chiamato form.factory. Inoltre, la classe controller base ha un metodo createformbuilder(), che è una scorciatoia per recuperare il factory di form e richiamare createbuilder su di esso. La creazione di un form si esegue tramite un oggetto FormBuilder 0, in cui si costruiscono e configurano i vari campi. Il costruttore di form è creato dal factory di form. Listing - $form = $formfactory->createbuilder() ->add('task', 'text') ->add('duedate', 'date') ->getform(); generated on August, 0 Chapter : Il componente Form 0

203 echo $twig->render('new.html.twig', array( 'form' => $form->createview(), )); Come si può vedere, creare un form è come scrivere una ricettta: si richiama add per ogni nuovo campo da creare. Il primo parametro di add è il nome del campo, il secondo il "tipo" di campo. Il componente Form dispone di molti tipi già pronti. Una volta costruito il form, si può capire come renderlo e come processarne l'invio. Impostazione di valori predefiniti Se il form deve caricare alcuni valori predefiniti (o se si sta costruendo un form di modifica), basta passare i dati predefiniti durante la creazione del costruttore di form: Listing - $defaults = array( 'duedate' => new \DateTime('tomorrow'), ); $form = $formfactory->createbuilder('form', $defaults) ->add('task', 'text') ->add('duedate', 'date') ->getform(); In questo esempio, i dati predefiniti sono in un array, se invece si usa l'opzione data_class per legare i dati direttamente a oggetti, i dati predefiniti saranno un'istanza dell'oggetto specificato. Resa del form Una volta creato il form, il passo successivo è renderlo. Lo si può fare passando un oggetto "vista" del form a un template (si noti la chiamata a $form->createview() nel controllore visto sopra) e usando delle funzioni aiutanti: Listing - <form action="#" method="post" form_enctype(form) > form_widget(form) <input type="submit" /> </form> Ecco fatto! Richiamando form_widget(form), viene reso ogni campo del form, insieme a label ed eventuali messaggi di errore. Essendo facile, non è ancora molto flessibile. Di solito, si vuole rendere ogni generated on August, 0 Chapter : Il componente Form 0

204 campo del form singolarmente, in modo da poterne controllare l'aspetto. Si vedrà come farlo nella sezione "Rendere un form in un template". Cambiare metodo e azione del form New in version.: La possibilità di configurare metodo e azione del form è stata introdotta in Symfony.. Ogni form viene normalmente inviato allo stesso URI in cui è stato reso, con una richiesta HTTP POST. Si può modificare tale comportamento, usando le opzioni form-option-action e form-option-method (l'opzione method è usata anche da handlerequest(), per determinare se il form sia stato inviato): Listing - $formbuilder = $formfactory->createbuilder('form', null, array( 'action' => '/search', 'method' => 'GET', )); Gestione dell'invio di form Per gestire l'invio del form, usare il metodo handlerequest() : Listing use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; $form = $formfactory->createbuilder() ->add('task', 'text') ->add('duedate', 'date') ->getform(); $request = Request::createFromGlobals(); $form->handlerequest($request); if ($form->isvalid()) $data = $form->getdata(); fare qualcosa, come salvare i dati $response = new RedirectResponse('/task/success'); $response->prepare($request); return $response->send(); In questo modo si definisce un flusso comune per i form, con tre diverse possibilità:. Nella richiesta GET iniziale (cioè quando l'utente apre la pagina), costruire e mostrare il form; Se la richiesta è POST, processare i dati inseriti (tramite handlerequest()). Quindi:. se il form non è valido, rendere nuovamente il form (che ora contiene errori). se il form è valido, eseguire delle azioni e redirigere. Per fortuna, non serve decidere se il form sia stato inviato o meno. Basta passare la richiesta al metodo handlerequest(). Quindi, il componente Form svolgerà tutto il lavoro necessario.. generated on August, 0 Chapter : Il componente Form 0

205 Validazione di form Il modo più facile di aggiungere validazione ai form è tramite l'opzione constraints, durante la costruzione di ogni campo: Listing - 0 use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Type; $form = $formfactory->createbuilder() ->add('task', 'text', array( 'constraints' => new NotBlank(), )) ->add('duedate', 'date', array( 'constraints' => array( new NotBlank(), new Type('\DateTime'), ) )) ->getform(); Al bind del form, questi vincoli di validazione saranno automaticamente applicati e gli eventuali errori mostrati accanto ai rispettivi campi. Per un elenco di tutti i vincoli disponibili, vedere Riferimento per i vincoli di validazione. Accedere agli errori New in version.: Prima di Symfony., geterrors() restituiva un array di oggetti FormError. Il valore restituito è stato cambiato in un FormErrorIterator in Symfony.. New in version.: I parametri $deep e $flatten sono stati introdotti in Symfony.. Si può usare il metodo geterrors() per accedere alla lista degli errori. Ogni elemento è un oggetto FormError : Listing - 0 $form =...; // un array di oggetti FormError, ma solo di errori allegati a questo // livello del form (p.e. "errori globali") $errori = $form->geterrors(); // un array di oggetti FormError, ma solo di errori allegati al campo // "nome" $errori = $form['nome']->geterrors(); // un'istanza di FormErrorIterator in una struttura appiattita // usare getorigin() per determinare il form che ha causato l'errore $errors = $form->geterrors(true);. generated on August, 0 Chapter : Il componente Form 0

206 // un'istanza di FormErrorIterator che rappresenta tutti gli errori dell'intero form $errori = $form->geterrors(true, false); Nelle versioni precedenti di Symfony, geterrors() restituiva un array. Per usare gli errori nello stesso modo in Symfony. o successivi, si deve passarli alla funzione iterator_to_array di PHP: Listing -0 $erroricomearray = iterator_to_array($form->geterrors()); Questo può essere utile, per esempio, se si vuole usare una funzione array_ di PHP sugli errori del form.. generated on August, 0 Chapter : Il componente Form 0

207 Chapter Creare un indovino di tipi Il componente Form può indovinare il tipo e alcune opzioni di un campo, usando gli indovini di tipo. Il componente include già un indovino di tipi, che usa le asserzioni del componente Validation, ma si possono anche aggiungere degli indovini personalizzati. Indovini di tipi di form nei bridge Symfony fornisce anche alcuni indovini di tipi di form nei bridge: PropelTypeGuesser fornito dal Propel bridge; DoctrineOrmTypeGuesser fornito dal Doctrine bridge. Creare un indovino di tipi PHPDoc In questa sezione verrà costruito un indovino, che legga informazioni su un campo dal PHPDoc delle proprietà. Per iniziare, occorre creare una classe che implementi FormTypeGuesserInterface. Tale interfaccia richiede quattro metodi: guesstype() - cerca di indovinare il tipo di un campo; guessrequired() - cerca di indovinare il valore per l'opzione required ; guessmaxlength() - cerca di indovinare il valore per l'opzione max_length ; guesspattern() - cerca di indovinare il valore per l'opzione pattern. Bisogna dunque creare una classe con questi metodi. Saranno riempiti in un momento successivo. Listing generated on August, 0 Chapter : Creare un indovino di tipi 0

208 0 0 namespace Acme\Form; use Symfony\Component\Form\FormTypeGuesserInterface; class PhpdocTypeGuesser implements FormTypeGuesserInterface public function guesstype($class, $property) public function guessrequired($class, $property) public function guessmaxlength($class, $property) public function guesspattern($class, $property) Indovinare il tipo Quando si indovina un tipo, il metodo restituisce un'istanza di TypeGuess determinare che l'indovino di tipi non riesce a indovinare il tipo. Il costruttore di TypeGuess ha bisogno di tre opzioni: oppure niente, per Il nome del tipo (uno dei tipi di form); Opzioni aggiuntive (per esempio, quando il tipo è entity, si deve anche impostare l'opzione class). Se non viene indovinato alcun tipo, va impostato a un array vuoto; La fiducia sulla correttezza del tipo indovinato. Può essere una delle costanti della classe Guess : LOW_CONFIDENCE, MEDIUM_CONFIDENCE, HIGH_CONFIDENCE, VERY_HIGH_CONFIDENCE. Dopo che tutti gli indovini di tipo sono stati eseguiti, viene usato il tipo con fiducia maggiore. Con questo in mente, si può facilmente implementare il metodo guesstype di PHPDocTypeGuesser: Listing - 0 namespace Acme\Form; use Symfony\Component\Form\Guess\Guess; use Symfony\Component\Form\Guess\TypeGuess; class PhpdocTypeGuesser implements FormTypeGuesserInterface public function guesstype($class, $property) $annotations = $this->readphpdocannotations($class, $property); if (!isset($annotations['var'])) return; // non indovinare niente se non è disponibile. generated on August, 0 Chapter : Creare un indovino di tipi 0

209 // altrimenti, basa il tipo switch ($annotations['var']) case 'string': // c'è una fiducia alta che il tipo sia testo, se è stata trovata string return new TypeGuess('text', array(), Guess::HIGH_CONFIDENCE); case 'int': case 'integer': // gli interi possono essere l'id di un'entità o un checkbox (0 o ) return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE); case 'float': case 'double': case 'real': return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE); case 'boolean': case 'bool': return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE); default: // c'è una fiducia molto bassa che questo sia corretto return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); protected function readphpdocannotations($class, $property) $reflectionproperty = new \ReflectionProperty($class, $property); $phpdoc = $reflectionproperty->getdoccomment(); // analizza $phpdoc in un array come: // array('type' => 'string', 'since' => '.0') $phpdoctags =...; return $phpdoctags; Questo indovino di tipi ora può indovinare il tipo di campo per una proprietà, se questa dispone di PHPDoc. Indovinare le opzioni del campo I restanti tre metodi (guessmaxlength, guessrequired e guesspattern) restituiscono un'istanza di ValueGuess 0, con il valore dell'opzione. Questo costruttore ha due parametri: Il valore dell'opzione; La fiducia che il valore indovinato sia corretto (usando le costanti della classe Guess). Viene restituito null quando si ritiene di non poter impostare il valore dell'opzione. 0. generated on August, 0 Chapter : Creare un indovino di tipi 0

210 Occorre molta cautela con il metodo guesspattern. Quando il tipo è un float, non lo si può usare per determinare un valore minimo o massimo per il float (p.e. si vuole che un float sia maggiore di,. non è valido ma length(.) > length(), quindi lo schema avrebbe success). In questo caso, il valore va impostato a null con MEDIUM_CONFIDENCE. Registrare un indovino di tipi L'ultimo passo da eseguire è registrare l'indovino di tipi, usando addtypeguesser() o addtypeguessers() : Listing - use Symfony\Component\Form\Forms; use Acme\Form\PHPDocTypeGuesser; $formfactory = Forms::createFormFactoryBuilder() ->addtypeguesser(new PHPDocTypeGuesser()) ->getformfactory(); Se si usa il framework Symfony, occorre registrare l'indovino di tipi con il tag form.type_guesser. Per maggiori informazioni, vedere the tag reference.. generated on August, 0 Chapter : Creare un indovino di tipi 0

211 Chapter Eventi dei form Il componente Form fornisce un processo strutturato, che consente di personalizzare i form, facendo uso del componente EventDispatcher. Usando gli eventi dei form, si possono modificare informazioni o campi in vari punti del flusso: dal popolamento del form all'invio dei dati dalla richiesta. La registrazione di ascoltatori di eventi è molto facile, usando il componente Form. Per esempio, se si vuole registrare una funzione all'evento FormEvents::PRE_SUBMIT, il codice seguente consente di aggiungere un campo, a seconda dei valori della richiesta: Listing - 0 use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; $listener = function (FormEvent $event) ; $form = $formfactory->createbuilder() // aggiunge campi al form ->addeventlistener(formevents::pre_submit, $listener); generated on August, 0 Chapter : Eventi dei form

212 Il flusso del form Il flusso di invio del form generated on August, 0 Chapter : Eventi dei form

213 ) Pre-popolare il form (FormEvents::PRE_SET_DATA e FormEvents::POST_SET_DATA) Durante il pre-popolamento di un form, sono distribuiti due eventi, al richiamo del metodo Form::setData() : FormEvents::PRE_SET_DATA e FormEvents::POST_SET_DATA. A) L'evento FormEvents::PRE_SET_DATA L'evento FormEvents::PRE_SET_DATA è distribuito all'inizio del metodo Form::setData(). Può essere usato per:. generated on August, 0 Chapter : Eventi dei form

214 Modificare i dati forniti durante il pre-popolamento; Modificare un form a seconda dei dati di pre-popolamento (aggiunta o rimozione dinamica di campi). Tabella informativa sugli eventi dei form Tipo di dati Dati modello Dati normalizzati Dati vista Valore null null null Durante FormEvents::PRE_SET_DATA, Form::setData() è bloccato e lancerà un'eccezione, se usato. Se si vogliono modificare i dati, usare invece FormEvent::setData(). FormEvents::PRE_SET_DATA nel componente Form Il tipo di form collection si appoggia al sottoscrittore ResizeFormListener, ascoltando l'evento FormEvents::PRE_SET_DATA per poter riordinare i campi del form, in base ai dati pre-popolati dell'oggetto, rimuovendo e aggiungendo tutte le righe del form. B) L'evento FormEvents::POST_SET_DATA L'evento FormEvents::POST_SET_DATA è distribuito alla fine del metodo Form::setData(). Questo evento per lo più serve a leggere dati dopo aver pre-popolato il form. Tabella informativa sugli eventi dei form Tipo di dati Dati modello Dati normalizzati Dati vista Valore Dati del modello iniettati in setdata() Dati del modello trasformati con un trasformatore di modello Dati normalizzati trasformati con un trasformatore di vista FormEvents::POST_SET_DATA nel componente Form New in version.: L'estensione per raccogliere i dati è stata introdotta in Symfony.. La classe DataCollectorListener ascolta l'evento FormEvents::POST_SET_DATA, per poter raccogliere informazioni sui form dal modello denormalizzato e dai dati della vista generated on August, 0 Chapter : Eventi dei form

215 ) Inviare un form (FormEvents::PRE_SUBMIT, FormEvents::SUBMIT e FormEvents::POST_SUBMIT) generated on August, 0 Chapter : Eventi dei form

216 generated on August, 0 Chapter : Eventi dei form

The Components Book for Symfony 2.4

The Components Book for Symfony 2.4 The Components Book for Symfony. generated on November, 0 The Components Book (.) This work is licensed under the Attribution-Share Alike.0 Unported license (http://creativecommons.org/ licenses/by-sa/.0/).

Dettagli

The Best Practices Book Version: 2.5

The Best Practices Book Version: 2.5 The Best Practices Book Version: 2.5 The Best Practices Book (2.5) This work is licensed under the Attribution-Share Alike 3.0 Unported license (http://creativecommons.org/ licenses/by-sa/3.0/). You are

Dettagli

The Components Book for Symfony 2.0

The Components Book for Symfony 2.0 The Components Book for Symfony.0 generated on November, 0 The Components Book (.0) This work is licensed under the Attribution-Share Alike.0 Unported license (http://creativecommons.org/ licenses/by-sa/.0/).

Dettagli

COMUNITA TERAPEUTICA IL FARO

COMUNITA TERAPEUTICA IL FARO COMUNITA TERAPEUTICA IL FARO Ristrutturazione per danni provocati dal sisma e adeguamento nuove normative Presentazione al 31.10.2010 STATO DI FATTO PRIMA DEL SISMA DI APRILE 2009 CRITICITA CRITICITA Spazi

Dettagli

dal laboratorio libri in presenza alla collaborazione online

dal laboratorio libri in presenza alla collaborazione online Centro Sovrazonale di Comunicazione Aumentativa Centro Sovrazonale di Comunicazione Aumentativa dal laboratorio libri in presenza alla collaborazione online a cura di Antonio Bianchi sommario L'impostazione

Dettagli

Connessione ad internet

Connessione ad internet Introduzione al C++ Connessione ad internet Istruzioni per la connessione internet: - una volta connessi, aprire un browser (firefox) - in Modifica/preferenze/avanzate/rete/impostazioni - attivare la modalità

Dettagli

Gli XML Web Service. Prof. Mauro Giacomini. Complementi di Informatica Medica 2008/2009 1

Gli XML Web Service. Prof. Mauro Giacomini. Complementi di Informatica Medica 2008/2009 1 Gli XML Web Service Prof. Mauro Giacomini Medica 2008/2009 1 Definizioni i i i Componente.NET che risponde a richieste HTTP formattate tramite la sintassi SOAP. Gestori HTTP che intercettano richieste

Dettagli

The Quick Tour Version: 2.7

The Quick Tour Version: 2.7 The Quick Tour Version:. generated on September, 0 What could be better to make up your own mind than to try out Symfony yourself? Aside from a little time, it will cost you nothing. Step by step you will

Dettagli

By E.M. note sull'uso di GAMBAS

By E.M. note sull'uso di GAMBAS By E.M. note sull'uso di GAMBAS GAMBAS viene installato sotto Xubuntu nella directory: /usr/share/gambas2 link problema su uso dell'esempio SerialPort http://gambasrad.org/zforum/view_topic?topic_id=1057

Dettagli

Marco Faella Classi enumerate

Marco Faella Classi enumerate Marco Faella Classi enumerate 22 Lezione n. Parole chiave: Java Corso di Laurea: Informatica Insegnamento: Linguaggi di Programmazione II Email Docente: faella.didattica@gmail.com A.A. 2009-2010 Il problema

Dettagli

ProgettAzione V anno Unità 3 - Architetture per applicazioni web Lezione: Esempio sviluppo applicazioni

ProgettAzione V anno Unità 3 - Architetture per applicazioni web Lezione: Esempio sviluppo applicazioni Unità 3 - Architetture per applicazioni web Lezione: Esempio sviluppo applicazioni Web service Hello world con Visual Studio 2012 Si tratta di un semplice esempio di web service, infatti come tutti I programmi

Dettagli

Bozza Guida ufficiale vs 1.0

Bozza Guida ufficiale vs 1.0 Bozza Guida ufficiale vs 1.0 Caratteristiche del software Videocopen è un software open source di videoconferenza. Sviluppato in php per la parte web e macromedia flash per la videoconferenza, sfrutta

Dettagli

Sistema Operativo di un Router (IOS Software)

Sistema Operativo di un Router (IOS Software) - Laboratorio di Servizi di Telecomunicazione Sistema Operativo di un Router (IOS Software) Slide tratte da Cisco Press CCNA Instructor s Manual ed elaborate dall Ing. Francesco Immè IOS Un router o uno

Dettagli

Script PHP per Configurare gli Accessi ad Internet di un router CISCO

Script PHP per Configurare gli Accessi ad Internet di un router CISCO Script PHP per Configurare gli Accessi ad Internet di un router CISCO Autore Roberto Bandiera 9 dicembre 2014 Obiettivo: scrivere uno script PHP per poter controllare da remoto la configurazione di un

Dettagli

Struttura logica di un programma

Struttura logica di un programma Struttura logica di un programma Tutti i programmi per computer prevedono tre operazioni principali: l input di dati (cioè l inserimento delle informazioni da elaborare) il calcolo dei risultati cercati

Dettagli

Tecnologie di Sviluppo per il Web

Tecnologie di Sviluppo per il Web Tecnologie di Sviluppo per il Web Applicazioni Web J2EE Framework per il Modello 2 it.unibas.pinco versione 3.2 Questo lavoro è concesso in uso secondo i termini di una licenza Creative Commons (vedi ultima

Dettagli

Ajax. Introdotta da Microsoft nel 1998 è stata standardizzata e adottata da tutti i browser moderni.

Ajax. Introdotta da Microsoft nel 1998 è stata standardizzata e adottata da tutti i browser moderni. Ajax AJAX (Asynchronous JavaScript And XML) è una tecnica fondamentale per la creazione di siti e applicazioni web ad alta interattività, che consente di evitare di ricaricare intere pagine ad ogni richiesta

Dettagli

Programmazione Orientata agli Oggetti in Linguaggio Java

Programmazione Orientata agli Oggetti in Linguaggio Java Programmazione Orientata agli Oggetti in Linguaggio Java Strumenti di Sviluppo: C# versione 1.0 Questo lavoro è concesso in uso secondo i termini di una licenza Creative Commons (vedi ultima pagina) G.

Dettagli

PHP: Interfacce; Gestione errori ed eccezioni. Pasqualetti Veronica

PHP: Interfacce; Gestione errori ed eccezioni. Pasqualetti Veronica PHP: Interfacce; Gestione errori ed eccezioni i Pasqualetti Veronica Interfacce In PHP non si possono ereditare proprietà da più classi. 2 Per ovviare al problema si possono creare delle interfacce che

Dettagli

SMS IN. Rules SMS IN. Rules. Geodrop. Geodrop

SMS IN. Rules SMS IN. Rules. Geodrop. Geodrop SMS IN Rules SMS IN Rules } Geodrop Geodrop SMS In 2.3 Regole per la manipolazione dei messaggi Guida alla scrittura di condizioni complesse Guida alle condizioni complesse v1.0-it, 7 Dicembre 2012 Indice

Dettagli

Introduzione al linguaggio PHP. Matteo Manzali INFN CNAF - Università degli Studi di Ferrara

Introduzione al linguaggio PHP. Matteo Manzali INFN CNAF - Università degli Studi di Ferrara Introduzione al linguaggio PHP Matteo Manzali INFN CNAF - Università degli Studi di Ferrara Introduzione a PHP PHP è un linguaggio di programmazione general-purpose che viene utilizzato in larga parte

Dettagli

Servizi web in LabVIEW

Servizi web in LabVIEW Servizi web in LabVIEW Soluzioni possibili, come si utilizzano. 1 Soluzioni possibili WEB SERVER Dalla versione 5.1 di LabVIEW è possibile implementare un Web server che consente di operare da remoto sul

Dettagli

Data: Altre informazioni: 15.06.2015 w w w.sw isssalary.ch. copyright 1998-2015 Swisssalary Ltd.

Data: Altre informazioni: 15.06.2015 w w w.sw isssalary.ch. copyright 1998-2015 Swisssalary Ltd. IncaMail Data: Altre informazioni: 15.06.2015 w w w.sw isssalary.ch copyright 1998-2015 Swisssalary Ltd. IncaMail 2015 SwissSalary Ltd. All rights reserved. No parts of this work may be reproduced in any

Dettagli

Se c'è un problema con la configurazione di PHP, il risultato del comando darà alcuni consigli su cosa sistemare e su come farlo.

Se c'è un problema con la configurazione di PHP, il risultato del comando darà alcuni consigli su cosa sistemare e su come farlo. Introduzione CELIA (Corpus Elettronico delle Lingue dell Italia Antica) è un software open source per la gestione dell epigrafia dei corpus di lingue antiche. È scritto in PHP grazie al framework symfony

Dettagli

Architettura Connettore Alfresco Share

Architettura Connettore Alfresco Share Direzione Sistemi Informativi Portale e Orientamento Allegato n. 2 al Capitolato Tecnico Indice Architettura Connettore Alfresco Share 1. Architettura del Connettore... 3 1.1 Componente ESB... 4 1.2 COMPONENTE

Dettagli

Microcat Authorisation Server (MAS ) Guida dell'utente

Microcat Authorisation Server (MAS ) Guida dell'utente Microcat Authorisation Server (MAS ) Guida dell'utente Indice Introduzione... 2 Installazione del Microcat Authorisation Server (MAS)... 3 Configurazione del MAS... 4 Opzioni di Licenza... 4 Opzioni Internet...

Dettagli

a cura di Maria Finazzi

a cura di Maria Finazzi Esercitazioni di XML a cura di Maria Finazzi (11-19 gennaio 2007) e-mail: maria.finazzi@unipv.it pagine web: Il trattamento dell'informazione Testo a stampa: Come

Dettagli

SISTEMI INFORMATICI/WEBGIS PER LA GESTIONE DI DATI AMBIENTALI E TERRITORIALI Povo, 11-04-2005 Steno Fontanari

SISTEMI INFORMATICI/WEBGIS PER LA GESTIONE DI DATI AMBIENTALI E TERRITORIALI Povo, 11-04-2005 Steno Fontanari Una piccola impresa trentina Open-Source SISTEMI INFORMATICI/WEBGIS PER LA GESTIONE DI DATI AMBIENTALI E TERRITORIALI Povo, 11-04-2005 Steno Fontanari L IDEA LA STORIA 5 soci fondatori: dalla ricerca al

Dettagli

L o. Francesco Cabras. http://paneb.dyndns.org. un sistema integrato per la gestione dei progetti di sviluppo software

L o. Francesco Cabras. http://paneb.dyndns.org. un sistema integrato per la gestione dei progetti di sviluppo software Introduzione a Trac L o un sistema integrato per la gestione dei progetti di sviluppo software Francesco Cabras http://paneb.dyndns.org 1 Introduzione Trac è un sistema web-based per la gestione dello

Dettagli

Report WordPress plugin di Konora v 0.8.4

Report WordPress plugin di Konora v 0.8.4 ` Report WordPress plugin di Konora v 0.8.4 Konora ltd 4321 Somewhere Street City, State ZIP phone 555-555-5555 fax 555-555-5555 name@address.com website.com Indice Indice 2 Recuperare il codice del circolo

Dettagli

MySQL Command Line Client: operazioni fondamentali

MySQL Command Line Client: operazioni fondamentali MySQL Command Line Client: operazioni fondamentali INTRODUZIONE Il RDBMS MySQL, oltre a fornire un applicazione che abbia un interfaccia user-friendly, ha a disposizione anche un altro client, che svolge

Dettagli

MODULO 1 PARTE 3. Programmazione (scripting) server-side con PHP 3.c Cookies e sessioni. Goy - a.a. 2012/2013 Programmazione Web 1

MODULO 1 PARTE 3. Programmazione (scripting) server-side con PHP 3.c Cookies e sessioni. Goy - a.a. 2012/2013 Programmazione Web 1 MODULO 1 PARTE 3 Programmazione (scripting) server-side con PHP 3.c Cookies e sessioni Goy - a.a. 2012/2013 Programmazione Web 1 Cookie - I Cookie = variabili che il server salva (*) sul client come file

Dettagli

Microcat Authorisation Server (MAS ) Guida dell utente

Microcat Authorisation Server (MAS ) Guida dell utente Microcat Authorisation Server (MAS ) Guida dell utente Indice Introduzione... 2 Installazione del Microcat Authorisation Server (MAS)... 3 Configurazione del MAS... 4 Opzioni di Licenza... 4 Opzioni Internet...

Dettagli

Il sistema IBM DB2. Sistemi Informativi T. Versione elettronica: L01.1.IntroduzioneDB2.pdf

Il sistema IBM DB2. Sistemi Informativi T. Versione elettronica: L01.1.IntroduzioneDB2.pdf Il sistema IBM DB2 Sistemi Informativi T Versione elettronica: L01.1.IntroduzioneDB2.pdf IBM DB2 Il DBMS relazionale IBM DB2 è il prodotto di punta dell IBM per la gestione di basi di dati relazionali

Dettagli

Corso di Web Programming

Corso di Web Programming Corso di Web Programming 11. PHP - Complementi Paolo Milazzo Dipartimento di Informatica, Università di Pisa http://www.di.unipi.it/ milazzo milazzo di.unipi.it Corso di Laurea in Informatica Applicata

Dettagli

Report WordPress plugin di Konora v 0.7

Report WordPress plugin di Konora v 0.7 ` Report WordPress plugin di Konora v 0.7 Konora ltd 4321 Somewhere Street City, State ZIP phone 555-555-5555 fax 555-555-5555 name@address.com website.com Indice Indice 2 Recuperare il codice del circolo

Dettagli

Introduzione a Visual Studio 2005

Introduzione a Visual Studio 2005 Fondamenti di Informatica e Laboratorio T-AB Ingengeria Elettronica e Telecomunicazioni a.a. 2008/2009 Introduzione a Visual Studio 2005 Outline Solutions e Projects Visual Studio e il linguaggio C Visual

Dettagli

Obiettivi d esame PHP Developer Fundamentals on MySQL Environment

Obiettivi d esame PHP Developer Fundamentals on MySQL Environment Obiettivi d esame PHP Developer Fundamentals on MySQL Environment 1.0 Ambiente di sviluppo 1.1 Web server e database MySQL Comprendere la definizione dei processi che si occupano di fornire i servizi web

Dettagli

Fondamenti di Informatica T-1 CdS Ingegneria Informatica a.a. 2011/2012. Introduzione a Visual Studio 2005/2008/2010

Fondamenti di Informatica T-1 CdS Ingegneria Informatica a.a. 2011/2012. Introduzione a Visual Studio 2005/2008/2010 Fondamenti di Informatica T-1 CdS Ingegneria Informatica a.a. 2011/2012 Introduzione a Visual Studio 2005/2008/2010 1 Outline Solution e Project Visual Studio e linguaggio C Visual Studio schermata principale

Dettagli

FileMaker Server 11. Guida all'aggiornamento dei plug-in

FileMaker Server 11. Guida all'aggiornamento dei plug-in FileMaker Server 11 Guida all'aggiornamento dei plug-in 2010 FileMaker, Inc. Tutti i diritti riservati. FileMaker, Inc. 5201 Patrick Henry Drive Santa Clara, California 95054 FileMaker è un marchio di

Dettagli

Strumenti per lo sviluppo del software

Strumenti per lo sviluppo del software Lo sviluppo del software Strumenti per lo sviluppo del software Lo sviluppo del software è l attività centrale del progetto e ha lo scopo di produrre il codice sorgente che, una volta compilato e messo

Dettagli

PHP e MySQL. Guida scaricata da www.webstyling.it

PHP e MySQL. Guida scaricata da www.webstyling.it Home -> Manuali & Tutorials -> Guida PHP PHP e MySQL E' possibile realizzare delle applicazioni in php appoggiandosi ad un database, quale ad esempio MySQL. Con le novità introdotte ai tempi di MySQL 4.1

Dettagli

I colori sui terminali Linux

I colori sui terminali Linux . I colori sui terminali Linux Thorbjørn Ravn Andersen, ravn@dit.ou.dk v1.4, 7 August 1997 Indice 1 Introduzione 1 2 Inizio veloce per gli impazienti 1 3 Ma ce

Dettagli

The Book for Symfony 2.0

The Book for Symfony 2.0 The Book for Symfony.0 generated on November, 0 The Book (.0) This work is licensed under the Attribution-Share Alike.0 Unported license (http://creativecommons.org/ licenses/by-sa/.0/). You are free to

Dettagli

Sistemi informativi e Telemedicina Anno Accademico 2008-2009 Prof. Mauro Giacomini

Sistemi informativi e Telemedicina Anno Accademico 2008-2009 Prof. Mauro Giacomini Sistemi informativi e Telemedicina Anno Accademico 2008-2009 Prof. Mauro Giacomini Concetti di base Tre funzioni fondamentali: Autenticazione: riceve le credenziali, le verifica presso un autorità, se

Dettagli

Utilizzo del linguaggio Basic utilizzando l interfaccia di Excel Silvia Patacchini

Utilizzo del linguaggio Basic utilizzando l interfaccia di Excel Silvia Patacchini Introduzione all utilizzo di Visual Basic for Application Utilizzo del linguaggio Basic utilizzando l interfaccia di Excel Silvia Patacchini PROGRAMMAZIONE Insieme delle attività da svolgersi per creare

Dettagli

Manuale Utente QueryGenerator

Manuale Utente QueryGenerator SOFTWARE DI GESTIONE DEL CORPUS API Manuale Utente QueryGenerator Cirass Sommario Introduzione 1 1. Presentazione 1 2. Requisiti di Sistema 2 3. Installazione 2 4. Utilizzo 3 5. Logica d interrogazione

Dettagli

createsession existssession alivesession deletesession deleteallsession getcredit getcreditsubaccount addtransactionsubaccount addsubaccount

createsession existssession alivesession deletesession deleteallsession getcredit getcreditsubaccount addtransactionsubaccount addsubaccount PROTOCOLLO DI INVIO SOAP SOAP è un protocollo per lo scambio di messaggi basato su protocollo HTTP e scambio dei dati in XML, quindi disponibile per qualsiasi piattaforma/linguaggio di programmazione,

Dettagli

The Best Practices Book Version: 2.6

The Best Practices Book Version: 2.6 The Best Practices Book Version:. generated on September, 0 The Best Practices Book (.) This work is licensed under the Attribution-Share Alike.0 Unported license (http://creativecommons.org/ licenses/by-sa/.0/).

Dettagli

La prima applicazione Java con NetBeans IDE. Dott. Ing. M. Banci, PhD

La prima applicazione Java con NetBeans IDE. Dott. Ing. M. Banci, PhD La prima applicazione Java con NetBeans IDE Dott. Ing. M. Banci, PhD Creare la prima applicazione 1. Creare un progetto: Quando si crea un progetto IDE occorre creare un ambiente nel quale costruire e

Dettagli

BIMPublisher Manuale Tecnico

BIMPublisher Manuale Tecnico Manuale Tecnico Sommario 1 Cos è BIMPublisher...3 2 BIM Services Console...4 3 Installazione e prima configurazione...5 3.1 Configurazione...5 3.2 File di amministrazione...7 3.3 Database...7 3.4 Altre

Dettagli

La connessione php-mysql con MySQLi

La connessione php-mysql con MySQLi La connessione php-mysql con MySQLi Premessa Lo scenario che si intende alla base di questo capitolo è di disporre di un ambiente phpmysql rappresentato nel seguente schema: L'applicazione php viene eseguita

Dettagli

The Book Version: 2.3

The Book Version: 2.3 The Book Version:. generated on September, 0 The Book (.) This work is licensed under the Attribution-Share Alike.0 Unported license (http://creativecommons.org/ licenses/by-sa/.0/). You are free to share

Dettagli

Tecnologie di Sviluppo per il Web

Tecnologie di Sviluppo per il Web Tecnologie di Sviluppo per il Web Programmazione su Basi di Dati: ADO.NET versione 1.1 Questo lavoro è concesso in uso secondo i termini di una licenza Creative Commons (vedi ultima pagina) G. Mecca mecca@unibas.it

Dettagli

Manuale dell Utente 1.0.4 VOLA 4. Release. Vola S.p.A.

Manuale dell Utente 1.0.4 VOLA 4. Release. Vola S.p.A. Release 1.0.4 VOLA 4 Manuale dell Utente Vola S.p.A. Traversa di Via Libeccio snc Z.I. Cotone 55049 Viareggio (LU) Tel +39 0584 43671 Fax +39 0584 436700 info@vola.it www.vola.it Sommario INTRODUZIONE

Dettagli

Esercitazione 8. Basi di dati e web

Esercitazione 8. Basi di dati e web Esercitazione 8 Basi di dati e web Rev. 1 Basi di dati - prof. Silvio Salza - a.a. 2014-2015 E8-1 Basi di dati e web Una modalità tipica di accesso alle basi di dati è tramite interfacce web Esiste una

Dettagli

How to contribute to Symfony for Symfony 2.0

How to contribute to Symfony for Symfony 2.0 How to contribute to Symfony for Symfony.0 How to contribute to Symfony (.0) This work is licensed under the Attribution-Share Alike 3.0 Unported license (http://creativecommons.org/ licenses/by-sa/3.0/).

Dettagli

PHP. A. Lorenzi, R. Giupponi, D. Iovino LINGUAGGI WEB. LATO SERVER E MOBILE COMPUTING Atlas. Copyright Istituto Italiano Edizioni Atlas

PHP. A. Lorenzi, R. Giupponi, D. Iovino LINGUAGGI WEB. LATO SERVER E MOBILE COMPUTING Atlas. Copyright Istituto Italiano Edizioni Atlas PHP A. Lorenzi, R. Giupponi, D. Iovino LINGUAGGI WEB. LATO SERVER E MOBILE COMPUTING Atlas Copyright Istituto Italiano Edizioni Atlas Programmazione lato server PHP è un linguaggio che estende le funzionalità

Dettagli

SVILUPPARE TEMPLATE CON ARIADNE WCM. Linguaggi e tecnologie per sviluppare siti web

SVILUPPARE TEMPLATE CON ARIADNE WCM. Linguaggi e tecnologie per sviluppare siti web SVILUPPARE TEMPLATE CON ARIADNE WCM Linguaggi e tecnologie per sviluppare siti web 1 Le componenti da considerare XML XSL + XPATH TEMPLATE REPOSITORY ACM 2 Sviluppare Template con Ariadne WCM 1. INTRODUZIONE

Dettagli

Ho messo la domotica a casa mia. My Home. Lorenzo Pini

Ho messo la domotica a casa mia. My Home. Lorenzo Pini Ho messo la domotica a casa mia My Home Lorenzo Pini Domotica si, domotica no? Un impianto domotico: Offre davvero maggior comfort? Può migliorare la qualità di vita? E facile da usare? Lo trovo un installatore

Dettagli

I file di dati. Unità didattica D1 1

I file di dati. Unità didattica D1 1 I file di dati Unità didattica D1 1 1) I file sequenziali Utili per la memorizzazione di informazioni testuali Si tratta di strutture organizzate per righe e non per record Non sono adatte per grandi quantità

Dettagli

Scaletta. Estensioni UML per il Web. Applicazioni web - 2. Applicazioni web. WAE: Web Application Extension for UML. «Client page»

Scaletta. Estensioni UML per il Web. Applicazioni web - 2. Applicazioni web. WAE: Web Application Extension for UML. «Client page» Scaletta Estensioni UML per il Web Michele Zennaro 14-05-2004 Le applicazioni web Scopo di un estensione UML per il web Due punti di vista Uno più astratto Uno più vicino ai file fisici conclusivo Commenti

Dettagli

Java Web Services. Uso di Eclipse e Apache Axis

Java Web Services. Uso di Eclipse e Apache Axis Java Web Services Uso di Eclipse e Apache Axis 1 Gli strumenti utili per iniziare Axis (Web Service tool) Eclipse (IDE di sviluppo) Tomcat (servlet/jsp container) N.B. Eclipse e Tomcat possono essere sostituiti

Dettagli

Laboratorio di reti II: Java Server Pages

Laboratorio di reti II: Java Server Pages Laboratorio di reti II: Java Server Pages Stefano Brocchi brocchi@dsi.unifi.it 6 aprile, 2009 Stefano Brocchi Laboratorio di reti II: Java Server Pages 6 aprile, 2009 1 / 34 JSP - Java Server Pages Le

Dettagli

Parte 13. Documentazione. [Frida Kahlo Nucleus Of Creation, 1945] 13.1 - AA. 2012/13

Parte 13. Documentazione. [Frida Kahlo Nucleus Of Creation, 1945] 13.1 - AA. 2012/13 Parte 13 Documentazione - AA. 2012/13 [Frida Kahlo Nucleus Of Creation, 1945] 13.1 13.2 Documentazione La documentazione in campo informatico, comprende il materiale utile alla comprensione delle caratteristiche

Dettagli

Siti interattivi e dinamici. in poche pagine

Siti interattivi e dinamici. in poche pagine Siti interattivi e dinamici in poche pagine 1 Siti Web interattivi Pagine Web codificate esclusivamente per mezzo dell HTML non permettono alcun tipo di interazione con l utente, se non quella rappresentata

Dettagli

PHP: Hypertext Preprocessor

PHP: Hypertext Preprocessor Corso di Laurea Specialistica in Ingegneria Informatica Corso di Linguaggi e Tecnologie Web A. A. 2011 - PHP: Hypertext Preprocessor Caratteristiche avanzate Eufemia Tinelli 1 Contenuti PHP ad oggetti

Dettagli

Il Regalo di Natale più bello

Il Regalo di Natale più bello Il Regalo di Natale più bello This ebook is distributed under Creative Common License 3.0 http://creativecommons.org/licenses/by-nc-nd/3.0/ You are free to copy, distribute and transmit this work under

Dettagli

Programmazione Orientata agli Oggetti in Linguaggio Java

Programmazione Orientata agli Oggetti in Linguaggio Java Programmazione Orientata agli Oggetti in Linguaggio Java Ruoli e Responsabilità: C# versione 1.1 Questo lavoro è concesso in uso secondo i termini di una licenza Creative Commons (vedi ultima pagina) G.

Dettagli

IceWarp Server - Implementazione di MySQL

IceWarp Server - Implementazione di MySQL IceWarp Server - Implementazione di MySQL MySQL è la tecnologia DBMS utilizzabile con IceWarp Server maggiormente efficiente in termini di prestazioni e stabilità, oltre ad essere quella utilizzata come

Dettagli

1.7.x template, stili e layout predefiniti s.bellandi@prismanet.com

1.7.x template, stili e layout predefiniti s.bellandi@prismanet.com Stefano Bellandi 1.7.x template, stili e layout predefiniti s.bellandi@prismanet.com L intervento ha una durata di circa 45 minuti dove affronteremo alla velocità della luce: introduzione ai template joomla1.7

Dettagli

2 Reti di Calcolatori XML

2 Reti di Calcolatori XML 2 3 4 5 6 7 8 root element Risultati Basi di dati 10 Dicembre 2003 Mario Rossi Corso Data List a_st

Dettagli

Sistema integrato dei portali RAS: specifiche di integrazione

Sistema integrato dei portali RAS: specifiche di integrazione Sistema integrato dei portali RAS: specifiche di integrazione Linee guida tecniche per l'integrazione con il presentation layer del CMS RAS Data: File: Allegato 3 - Specifiche di integrazione SIP-RAS.odt

Dettagli

4. AUTENTICAZIONE DI DOMINIO

4. AUTENTICAZIONE DI DOMINIO 4. AUTENTICAZIONE DI DOMINIO I computer di alcuni laboratori dell'istituto sono configurati in modo da consentire l'accesso solo o anche ad utenti registrati (Workstation con autenticazione di dominio).

Dettagli

Agent and Object Technology Lab Dipartimento di Ingegneria dell Informazione Università degli Studi di Parma. Ingegneria del software A.

Agent and Object Technology Lab Dipartimento di Ingegneria dell Informazione Università degli Studi di Parma. Ingegneria del software A. Agent and Object Technology Lab Dipartimento di Ingegneria dell Informazione Università degli Studi di Parma Ingegneria del software A Apache Ant Michele Tomaiuolo Progetto Apache Open source, community

Dettagli

Laboratorio di reti II: Gestione di database lato server

Laboratorio di reti II: Gestione di database lato server Laboratorio di reti II: Gestione di database lato server Stefano Brocchi brocchi@dsi.unifi.it 23 marzo, 2009 Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 1 / 32 Uso di database lato

Dettagli

Password Safe 1.92b. Introduzione. Dettagli Tecnici. Istruzioni d uso. Egizio Raffaele

Password Safe 1.92b. Introduzione. Dettagli Tecnici. Istruzioni d uso. Egizio Raffaele Password Safe 1.92b Introduzione Dettagli tecnici Istruzioni d uso Egizio Raffaele Introduzione Password Safe 1.92b è stato progettato per mantenere le password in un database criptato protetto da Safe

Dettagli

Indice. 1.13 Configurazione di PHP 26 1.14 Test dell ambiente di sviluppo 28

Indice. 1.13 Configurazione di PHP 26 1.14 Test dell ambiente di sviluppo 28 Indice 25 184 Introduzione XI Capitolo 1 Impostazione dell ambiente di sviluppo 2 1.1 Introduzione ai siti Web dinamici 2 1.2 Impostazione dell ambiente di sviluppo 4 1.3 Scaricamento di Apache 6 1.4 Installazione

Dettagli

Il linguaggio PHP. Parte I Introduzione. Paolo Milazzo

Il linguaggio PHP. Parte I Introduzione. Paolo Milazzo Il linguaggio PHP Parte I Introduzione Paolo Milazzo Dipartimento di Informatica, Università di Pisa http://www.di.unipi.it/ milazzo milazzo di.unipi.it A.A. 2010/2011 Paolo Milazzo (Università di Pisa)

Dettagli

A. Bardine - Introduzione a PostgreSQL. PostgreSQL è un software relazionale e ad oggetti per la gestione di basi di dati

A. Bardine - Introduzione a PostgreSQL. PostgreSQL è un software relazionale e ad oggetti per la gestione di basi di dati Basi di dati PostgreSQL è un software relazionale e ad oggetti per la gestione di basi di dati PostgreSQL è Open-Source ed il suo sviluppo procede da 15 anni il suo codice sorgente è quindi disponibile

Dettagli

Utilizzare il debugger di Visual Studio

Utilizzare il debugger di Visual Studio Appendice F Utilizzare il debugger di Visual Studio Obiettivi Essere in grado di impostare i breakpoint e di eseguire un programma nel debugger. Essere in grado di utilizzare il comando Continue per continuare

Dettagli

PROGETTO - Ingegneria del Software. Università degli Studi di Milano Polo di Crema. Corso di laurea in Scienze Matematiche, Fisiche e Naturali

PROGETTO - Ingegneria del Software. Università degli Studi di Milano Polo di Crema. Corso di laurea in Scienze Matematiche, Fisiche e Naturali Università degli Studi di Milano Polo di Crema Corso di laurea in Scienze Matematiche, Fisiche e Naturali INFORMATICA Corso di Ingegneria del Software progetto IL SISTEMA CALENDAR Presentato al dott. Paolo

Dettagli

Basi di dati. Microsoft Access. Cosa è. Pietro Pala (pala@dsi.unifi.it) Come iniziare. Aprire un database. Creare un database. Creare un database

Basi di dati. Microsoft Access. Cosa è. Pietro Pala (pala@dsi.unifi.it) Come iniziare. Aprire un database. Creare un database. Creare un database Cosa è Basi di dati Pietro Pala (pala@dsi.unifi.it) Microsoft Access Access è un DBMS relazionale in grado di supportare: Specifica grafica dello schema della base dati Specifica grafica delle interrogazioni

Dettagli

[HOW-TO] Installazione e configurazione DBMS

[HOW-TO] Installazione e configurazione DBMS [HOW-TO] Installazione e configurazione DBMS Ai fini del corso sarà fondamentale possedere le capacità di installare e configurare correttamente almeno un DBMS sul proprio calcolatore. Vedremo in dettaglio

Dettagli

COM_HELLOWORLD_PART4

COM_HELLOWORLD_PART4 COM_HELLOWORLD_PART1 Nel manifest la presenza del tag permette di gestire le versioni incrementali di aggiornamento anche grazie all uso del tag . Le installazioni successive del componente

Dettagli

Costruzione di Siti Web con PHP e MySQL. Lezione 4: Operatori, Array, Controlli e Cicli

Costruzione di Siti Web con PHP e MySQL. Lezione 4: Operatori, Array, Controlli e Cicli Costruzione di Siti Web con PHP e MySQL Lezione 4: Operatori, Array, Controlli e Cicli Argomenti della lezione In questa lezione si riprenderà il concetto di Array, nel suo aspetto basilare, ed in particolare

Dettagli

CdL MAGISTRALE in INFORMATICA

CdL MAGISTRALE in INFORMATICA 05/11/14 CdL MAGISTRALE in INFORMATICA A.A. 2014-2015 corso di SISTEMI DISTRIBUITI 7. I processi : il naming Prof. S.Pizzutilo Il naming dei processi Nome = stringa di bit o di caratteri utilizzata per

Dettagli

IMPLEMENTAZIONE DEI TAG SOFTWARE NEI PRODOTTI ADOBE NOTE TECNICHE

IMPLEMENTAZIONE DEI TAG SOFTWARE NEI PRODOTTI ADOBE NOTE TECNICHE IMPLEMENTAZIONE DEI TAG SOFTWARE NEI PRODOTTI ADOBE NOTE TECNICHE 2011 Adobe Systems Incorporated. All rights reserved. Software Tag Implementation in Adobe Products Tech Note Adobe, the Adobe logo, and

Dettagli

Introduzione allo sviluppo per SharePoint

Introduzione allo sviluppo per SharePoint Introduzione allo sviluppo per SharePoint Esercitazione pratica Manuale dell esercitazione SPCHOL305 - Sviluppo di un flusso di lavoro di SharePoint 2010 con un modulo di avvio in Visual Studio 2010 -

Dettagli

QUEUE : considerazioni. QUEUE : considerazioni. QUEUE : esempio. QUEUE : esempio

QUEUE : considerazioni. QUEUE : considerazioni. QUEUE : esempio. QUEUE : esempio QUEUE : considerazioni QUEUE : considerazioni Si è realizzata una struttura dati complessa utilizzandone una primitiva, l array. Il pregio di tale implementazione è il basso costo computazionale, mentre

Dettagli

JAVASCRIPT. Tale file è associato alla pagina web mediante il tag