Durante le fasi di sviluppo e di deployment, gli sviluppatori richiedono un flusso costante di informazioni diagnostiche, al fine di determinare se l'applicazione sta funzionando come previsto. Tali informazioni generalmente vengono aggregate attraverso utility per il log e il debug. A causa del ruolo centrale dei framework come symfony, utilizzati come motore delle applicazioni, è essenziale che tali capacità siano strettamente integrate in modo da garantire uno sviluppo efficiente.
Durante la vita di un'applicazione sul server di produzione, l'amministratore dell'applicazione ripete un gran numero di task, dalla rotazione dei log agli aggiornamenti. Un framework deve, per quanto possibile anche fornire strumenti per automatizzare questi task.
Questo capitolo spiega come gli strumenti di gestione delle applicazioni di symfony siano in grado di rispondere a tutte queste esigenze.
I log
L'unico modo per capire cosa è andato storto durante l'esecuzione di una richiesta, è quello di vedere un trace del processo di esecuzione. Fortunatamente, come si vedrà in questa sezione, sia PHP che symfony salvano grandi quantità di questo tipo di dati nei log.
I log di PHP
PHP ha un parametro error_reporting
, definito in php.ini
, che specifica quali eventi devono essere registrati nel log. Symfony consente di sovrascrivere questo valore, per applicazione e ambiente, nel file settings.yml
, come mostrato nel listato 16-1.
Listato 16-1 - Impostazione del livello di segnalazione degli errori, in frontend/config/settings.yml
prod: .settings: error_reporting: <?php echo (E_PARSE | E_COMPILE_ERROR | E_ERROR | E_CORE_ERROR | E_USER_ERROR)."\n" ?> dev: .settings: error_reporting: <?php echo (E_ALL | E_STRICT)."\n" ?>
Al fine di evitare problemi di prestazioni nell'ambiente di produzione, il server registra solo gli errori critici PHP. Tuttavia, nell'ambiente di sviluppo, vengono registrati tutti i tipi di eventi, in modo che lo sviluppatore può avere tutte le informazioni necessarie per rintracciare errori.
La posizione del file di log PHP dipendono dalla configurazione nel file php.ini
. Se non ci si è mai preoccupati di definire tale posizione, PHP probabilmente userà il log fornito dal server web (come i log di errore di Apache). In questo caso, i log di PHP si troveranno sotto la cartella di log del web server.
I log di symfony
In aggiunta ai log standard di PHP, symfony può registrare molti eventi personalizzati. È possibile trovare tutti i log di symfony sotto la cartella mioprogetto/log/
. C'è un file per applicazione e ambiente. Per esempio, il file di log dell'ambiente di sviluppo dell'applicazione frontend
si chiama frontend_dev.log
, quello di produzione si chiama frontend_prod.log
e così via.
Se si dispone di un'applicazione in esecuzione di symfony, si può dare un'occhiata al suo file di log. La sintassi è molto semplice. Per ogni evento, viene aggiunta una riga al file di log dell'applicazione. Ogni linea include l'ora esatta dell'evento, la natura dell'evento, l'oggetto che è stato processato e ogni altro dettaglio rilevante. Il listato 16-2 mostra un esempio del contenuto di un file log di symfony.
Listato 16-2 - Esempio del contenuto di un file log di symfony, in log/frontend_dev.log
Nov 15 16:30:25 symfony [info ] {sfAction} call "barActions->executemessages()" Nov 15 16:30:25 symfony [info ] {sfPropelLogger} executeQuery: SELECT bd_message.ID... Nov 15 16:30:25 symfony [info ] {sfView} set slot "leftbar" (bar/index) Nov 15 16:30:25 symfony [info ] {sfView} set slot "messageblock" (bar/mes... Nov 15 16:30:25 symfony [info ] {sfView} execute view for template "messa... Nov 15 16:30:25 symfony [info ] {sfView} render "/home/production/mioprogetto/... Nov 15 16:30:25 symfony [info ] {sfView} render to client
Si possono trovare molti dettagli in questi file, comprese le query SQL effettive inviate al database, i template chiamati, la catena di chiamate tra gli oggetti e così via.
note
Il formato dei file di log è configurabile sovrascrivendo le impostazioni format
e/o time_format
presenti in factories.yml
come mostrato nel listato 16-3.
Listato 16-3 - Cambiare il formato del log
all: logger: param: sf_file_debug: param: format: %time% %type% [%priority%] %message%%EOL% time_format: %b %d %H:%M:%S
Configurazione del livello di log di symfony
Ci sono otto livelli per i messaggi di log di symfony: emerg
, alert
, crit
, err
, warning
, notice
, info
e debug
, che sono gli stessi dei livelli dei pacchetti PEAR::Log
. Si può configurare il livello massimo che deve essere nei log per ciascun ambiente nel file di configurazione factories.yml
di ciascuna applicazione, come mostrato nel listato 16-4.
Listato 16-4 - Configurazione predefinita del log, in frontend/config/factories.yml
prod: logger: param: level: err
Per impostazione predefinita, in tutti gli ambienti escluso quello di produzione, tutti i messaggi vengono registrati nel log (fino al livello meno importante, il livello debug
). Nell'ambiente di produzione, per impostazione predefinita il log è disabilitato; se in settings.yml
si cambia logging_enabled
a true
, nei log verranno registrati solo i messaggi più importanti (da crit
a emerg
)
Nel file factories.yml
si può cambiare il livello dei log per ciascun ambiente per limitare il tipo dei messaggi registrati.
tip
Per vedere se la registrazione dei log è abilitata, chiamare sfConfig::get('sf_logging_enabled')
.
Aggiungere un messaggio di log
È possibile aggiungere manualmente un messaggio nel file di log di symfony dal codice utilizzando una delle tecniche descritte nel listato 16-5.
Listato 16-5 - Aggiungere un messaggio di log personalizzato
// Da una azione $this->logMessage($message, $level); // Da un template <?php use_helper('Debug') ?> <?php log_message($message, $level) ?>
$level
può avere gli stessi valori dei messaggi di log.
In alternativa, per scrivere un messaggio nel log da qualsiasi punto della propria applicazione, si possono utilizzare direttamente i metodi sfLogger
, come mostrato nel listato 16-6. I metodi disponibili portano i nomi stessi dei livelli di log.
Listato 16-6 - Aggiungere un messaggio di log personalizzato da qualsiasi parte
if (sfConfig::get('sf_logging_enabled')) { sfContext::getInstance()->getLogger()->info($message); }
Ridurre e ruotare i file di log
Non bisogna dimenticare di eliminare periodicamente la cartella log/
delle applicazioni, perché questi file hanno la strana abitudine di crescere di diversi megabyte in pochi giorni, a seconda, naturalmente, del traffico. Symfony fornisce un task speciale log:clear
per questo scopo, ed è possibile lanciarlo periodicamente a mano o metterlo in una tabella di cron. Per esempio il seguente comando cancella i file di log di symfony:
$ php symfony log:clear
Sia per questioni di prestazioni che di sicurezza, probabilmente si vorrà memorizzare i log di symfony in diversi piccoli file invece di un singolo file di grandi dimensioni. La strategia di memorizzazione ideale per i file di log è quella di eseguire il backup e svuotare il file di log principale regolarmente, ma di tenere solo un numero limitato di backup. È possibile attivare un tale rotazione dei log con un periodo
di 7
giorni e uno storico
(numero di copie di backup) di 10
, come mostrato nel listato 16-7. Si potrebbe lavorare con un file di log attivo più dieci file di backup contenenti sette giorni di storia per ciascuno. Ogni volta che il prossimo periodo di sette giorni termina, il file di registro corrente va nel backup e il backup più vecchio viene cancellato.
Listato 16-7 - Avviare la rotazione dei log
$ php symfony log:rotate frontend prod --period=7 --history=10
I file con i backup dei log vengono salvati nella cartella logs/history/
e gli viene aggiunto un suffisso con la data di quando vengono salvati.
Debug
Non importa quanto si sia abili a programmare, perché si fanno sempre degli errori, anche se si utilizza symfony. L'individuazione e la comprensione degli errori è una delle chiavi per sviluppare velocemente delle applicazioni. Fortunatamente, symfony fornisce molti strumenti di debug per lo sviluppatore.
La modalità debug di symfony
Symfony ha una modalità di debug che facilita lo sviluppo e il debug delle applicazioni. Quando è abilitata, succedono le cose seguenti:
- La configurazione viene verificata a ogni richiesta, così la modifica di un qualunque file di configurazione ha un effetto immediato, senza che ci sia la necessità di cancellare la cache della configurazione.
- I messaggi di errore visualizzano l'intero stack trace in modo chiaro, in modo che si possa trovare velocemente la causa del problema.
- Sono disponibili più strumenti per il debug (ad esempio i dettagli delle query al database).
- È anche attivata la modalità di debug per Propel/Doctrine, quindi per qualunque errore in una chiamata a un oggetto Propel/Doctrine verrà mostrata una catena dettagliata con le chiamate dell'architettura Propel/Doctrine.
Al contrario, quando la modalità debug è disattivata, l'elaborazione viene gestita nel modo seguente:
- I file con la configurazione YAML vengono letti solo una volta e trasformati in file PHP salvati nella cartella
cache/config/
. Ogni richiesta dopo la prima ignora i file YAML e usa al suo posto la configurazione presente nella cache. Di conseguenza, l'elaborazione delle richieste è molto più veloce. - Per eseguire una rielaborazione della configuazione, è necessario cancellare manualmente la cache della configurazione.
- Un errore durante l'elaborazione della richiesta restituisce una risposta con codice 500 (errore interno del server), senza nessuna spiegazione su quale possa essere la causa del problema.
La modalità debug è attivata per applicazione nel front controller. Viene gestita dal valore del terzo parmatro passato nella chiamata del metodo getApplicationConfiguration()
, come mostrato nel listato 16-8.
Listato 16-8 - Esempio di front controller con la modalità di debug attivata, in web/frontend_dev.php
<?php require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php'); $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true); sfContext::createInstance($configuration)->dispatch();
caution
Nel server di produzione, bisognerebbe disabilitare la modalità di debug senza lasciare nessun front controller con la modalità di debug disponibile. Non solo la modalità di debug rallenta la consegna delle pagine, ma può anche rivelare informazioni sull'interno dell'applicazione. Anche se gli strumenti di debug non rivelano mai informazioni sulla connessione al database, lo stack delle eccezioni è pieno di informazioni pericolose per un visitatore maleintenzionato.
Le eccezioni di symfony
Quando si verifica un'eccezione nella modalità di debug, symfony mostra un'utile informazione dull'eccezione che contiene tutto quello di cui si ha bisogno per trovare la causa del problema.
I messaggi con le eccezioni sono scritti in modo chiaro e indicano la causa più probabile del problema. Spesso forniscono una possibile soluzione per risolvere il problema e per la maggior parte dei problemi più comuni, la pagina con l'eccezione può contenere un link a una pagina del sito di symfony con maggiori dettagli sull'eccezione. La pagina dell'eccezione mostra dove è avvenuto l'errore nel codice PHP (con evidenziazione colorata della sintassi), insieme allo stack completo delle chiamate di metodoto, come mostrato in figura 16-1. È possibile seguire il trace alla prima chiamata che ha causato il problema. Sono anche indicati i parametri che sono stati passati ai metodi.
note
Symfony si basa proprio sulle eccezioni PHP per la segnalazione degli errori. Ad esempio, l'errore 404 può essere lanciato da un sfError404Exception
.
Figura 16-1 - Esempio di messaggio di una eccezione per una applicazione symfony
Durante la fase di sviluppo, le eccezioni symfony saranno di grande utilità, per il debug dell'applicazione.
Estensione Xdebug
L'estensione PHP Xdebug permette di aumentare la quantità di informazioni che vengono loggate dal web server. Symfony integra i messaggi Xdebug nei propri feedback per il debug, quindi è una buona idea attivare questa estensione quando si esegue il debug dell'applicazione. L'installazione dell'estensione dipende molto dalla piattaforma; consultare il sito web di Xdebug per le linee guida dettagliate relative all'installazione. Una volta che Xdebug è installato, è necessario attivarlo manualmente nel proprio file php.ini
. Per le piattaforme *nix, questo si fa aggiungendo la seguente riga:
zend_extension="/usr/local/lib/php/extensions/no-debug-non-zts-20041030/xdebug.so"
Per le piattaforme Windows, l'attivazione di Xdebug è gestita da questa linea:
extension=php_xdebug.dll
Il listato 16-9 mostra un esempio della configurazione di Xdebug, che deve essere aggiunto nel file php.ini
.
Listato 16-9 - Esempio di configurazione di Xdebug
;xdebug.profiler_enable=1 ;xdebug.profiler_output_dir="/tmp/xdebug" xdebug.auto_trace=1 ; enable tracing xdebug.trace_format=0 ;xdebug.show_mem_delta=0 ; memory difference ;xdebug.show_local_vars=1 ;xdebug.max_nesting_level=100
Bisogna riavviare il web server perché la modalità Xdebug venga attivata.
caution
Non dimenticarsi di disabilitare la modalità Xdebug nella piattaforma di produzione. Non facendolo, si rallenta di molto l'esecuzione di ogni pagina.
La barra web per il debug
I file di log contengono informazioni interessanti, ma non sono molto facili da leggere. L'azione più semplice, che è quella di trovare le linee di log per una particolare richiesta, può diventare molto complicata se si hanno molti utenti che utilizzano contemporaneamente l'applicazione e un lungo storico con gli eventi. Questo è il momento in cui si comincia a sentire il bisogno di una barar web per il debug.
Questa barra appare come una finestra semitrasparente sovrapposta al normale contenuto del browser, nell'angolo in alto a destra della finestra, come mostrato nella figura 16-2. Offre l'accesso al log degli eventi di symfony, alla configurazione corrente, alle proprietà degli oggetti request e response, ai dettagli delle query al database inviate durante la richiesta e a un grafico dei tempi di elaborazione legati alla richiesta.
Figura 16-2 - La barra web per il debug appare nell'angolo in alto a destra della finestra
Il colore di sfondo della barra di debug dipende dal più alto livello di messaggio di log verificatosi durante la richiesta. Se nessun messaggio passa il livello debug
, la barra degli strumenti ha un fondo grigio. Se un singolo messaggio raggiunge il livello err
, la barra degli strumenti ha uno sfondo rosso.
note
Non bisogna confondere la modalità di debug con la barra web per il debug. La barra per il debug può essere visualizzata anche nella modalità di debug impostata a off, anche se in questo caso mostrerà molte meno informazioni.
Per attivare la barra web di debug per una applicazione, aprire il file settings.yml
e cercare la chiave web_debug
. Negli ambienti prode
test, il valore predefinito per
web_debugè
false, per cui se la si vuole utilizzare bisogna abilitarla manualmente. Nell'ambiente
devla configurazione predefinita è impostata a
true`, come mostrato nel listato 16-10.
Listato 16-10 - Attivazione della barra web per il debug, in frontend/config/settings.yml
dev: .settings: web_debug: true
Quando presente, la barra web per il debug, rende disponibili molte informazioni:
- Cliccare sul logo di symfony per ridurre la visibilità della barra. Quando è ridotta, la barra non nasconde più gli elementi posizionati nella zona alta della pagina.
- Cliccare sulla sezione "config" per visualizzare i dettagli di request, response, settings, globals e proprità PHP, come mostrato nella figura 16-3. La linea in alto racchiude le impostazioni delle configurazioni importanti, come la modalità di debug, la cache e la presenza di un acceleratore PHP (appaiono in rosso se sono disattivati e in verde se sono attivati).
Figura 16-3 - La sezione "config" mostra tutte le variabili e costanti della request
- Quando la cache è abilitata, nella barra appare una freccia verde. Cliccare questa freccia per rieseguire la pagina, indipendentemente da cosa è memorizzato nella cache (la cache non viene cancellata).
- Cliccare nella sezione "logs" per visualizzare i messaggi di log della richiesta corrente, come mostrato nella figura 16-4. Secondo l'importanza degli eventi, vengono visualizzati in righe grigie, gialle o rosse. È possibile filtrare in base alla categoria gli eventi che vengono visualizzati, utilizzando i link presenti all'inizio dell'elenco.
Figura 16-4 - La sezione "logs" visualizza i messaggi di log per la richiesta corrente
note
Quando l'azione corrente proviene da una redirezione, nel pannello "logs" sono presenti solo i log dell'utlima richiesta, quindi i file di log rimangono indispensabili per fare il debug.
- Quando ci sono richieste di esecuzione di query SQL, appare l'icona di un database nella barra degli strumenti. Cliccare per vedere il dettaglio delle query, come mostrato nella figura 16-5.
- Alla destra dell'icona dell'orologio c'è il tempo totale necessario per elaborare la richiesta. Bisogna tener conto che la barra web per il debug e la modalità stessa di debug rallentano l'esecuzione della richiesta, quindi non bisogna considerare i tempi in sé, ma prestare attenzione solo alle differenze tra i tempi di esecuzione di due pagine diverse. Fare clic sull'icona dell'orologio per visualizzare i dettagli dei tempi di elaborazione categoria per categoria, come mostrato nella figura 16-6. Symfony visualizza il tempo trascorso in diversi momenti dell'elaborazione della richiesta. Solo i tempi relativi alla richiesta corrente hanno un senso per l'ottimizzazione, quindi il tempo impiegato dal nucleo di symfony non viene visualizzato. Ecco perché la somma di questi tempi non è uguale al tempo totale.
- Fare clic sulla x rossa all'estremità destra della barra degli strumenti, per nascondere la barra stessa.
Figura 16-5 - La sezione query del database, mostra le query eseguite nella richiesta corrente
Figura 16-6 - L'icona con l'orologio mostra il tempo di esecuzione per categoria
note
La barra web di debug, nella modalità predefinita non è presente per le risposte Ajax e per i documenti che non hanno il content-type HTML. Per le altre pagine, si può disabilitare la barra manualmente dall'interno di una azione, chiamando sfConfig::set('sf_web_debug', false)
.
Debug manuale
È bello avere l'accesso ai messaggi di debug del framework, ma è ancora meglio essere in grado di accedere ai propri messaggi. Symfony fornisce scorciatoie, accessibili sia dalle azioni che dai modelli, per aiutare a traccaire eventi e/o valori durante l'esecuzione della richiesta.
I messaggi di log personalizzati vengono salvati nel file di log di symfony e compaiono nella barra web per il debug. (Il listato 16-5 fornisce un esempio della sintassi per un messaggio di log personalizzato. Un messaggio personalizzato è un buon modo per controllare il valore di una variabile da un template, ad esempio. Il listato 16-11 mostra come usare la barra web per il debug per avere il feedback da un template (da una azione invece, si può utilizzare $this->logMessage()
).
Listato 16-11 - Inserire un messaggio nel log a scopo di debug
<?php use_helper('Debug') ?> ... <?php if ($problem): ?> <?php log_message('{sfAction} been there', 'err') ?> ... <?php endif ?>
L'utilizzo del livello err
garantisce che l'evento sarà ben visibile nell'elenco dei messaggi, come mostrato nella figura 16-7.
Figura 16-7 - Un messaggio di log personalizzato visualizzato nella sezione "logs" della barra web per il debug
Usare symfony fuori dal contesto web
Si può volere eseguire uno script da riga di comando (o tramite cron) che abbia accesso a tutte le classi e le caratteristiche di symfony, ad esempio per inviare e-mail in batch e per aggiornare periodicamente il modello tramite una elaborazione intensiva. Il modo più semplice per farlo è quello di creare uno script PHP che riproduca i primi passi di un front controller, in modo che symfony possa venire correttamente inizializzato. È inoltre possibile utilizzare il sistema a riga di comando di symfony, per trarre vantaggio dell'analisi dei parametri e dell'inizializzazione automatizzata del database.
File batch
L'inizializzazione di symfony richiede solo un paio di righe di codice PHP. È possibile usufruire di tutte le funzionalità di symfony creando un file PHP, per esempio sotto la cartella lib/
del progetto, scrivendo le linee mostrate nel listato 16-12.
Listato 16-12 - Esempio di script batch, in lib/mioScript.php
<?php require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php'); $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true); // Remove the following lines if you don't use the database layer $databaseManager = new sfDatabaseManager($configuration); // aggiungere qua il codice
Assomiglia molto alle prime righe di un front controller (vedere il capitolo 6), perché queste linee fanno la stessa cosa: inizializzano symfony, analizzano la configurazione del progetto e dell'applicazione. Notare che il metodo ProjectConfiguration::getApplicationConfiguration
richiede tre parametri:
- un nome di applicazione
- un nome di ambiente
- un booleano, per indicare se le funzionalità di debug devono essere abilitate o no
Per eseguire il codice, basta chiamare lo script dalla riga di comando:
$ php lib/mioScript.php
Task personalizzati
Un modo alternativo per creare script personalizzati a riga di comando è scrivere un task symfony. Proprio come i task cache:clear
e propel:build-model
, è possibile lanciare i propri task personalizzati dalla linea di comando con php symfony
. I task personalizzati traggono vantaggio della capacità di analizzare parametri e opzioni della linea di comando, possono incorporare i propri messaggi di aiuto e possono estendere task esistenti.
Un task personalizzato è solo una classe che estende sfBaseTask
, situata in una cartella lib/task/
o sotto la radice del progetto o in una cartella di un plugin. Il nome del file deve terminare con 'Task.class.php'. Il listato 16-13 mostra un esempio di task presonalizzato.
Listato 16-13 - Esempio di task, in lib/task/testCiaoTask.class.php
<?php class testCiaoTask extends sfBaseTask { protected function configure() { $this->namespace = 'test'; $this->name = 'ciao'; $this->briefDescription = 'Dice ciao'; } protected function execute($arguments = array(), $options = array()) { // scrivere qua il codice $this->log('Ciao, mondo!'); } }
Il codice scritto nel metodo execute
ha accesso a tutte le librerie di symfony, proprio come nel precedente script batch. La differenza è come viene chiamato il task personalizzato:
$ php symfony test:ciao
Il nome del task proviene dalle proprietà protette namespace
e name
(non dal nome della classe e neanche dal nome del file). E dal momento che il task è integrato nella riga di comando di symfony, compare nell'elenco dei task quando si digita:
$ php symfony
Invece che scrivere manualmente lo scheletro del task, si può usare il task symfony generate:task
. Questo cera un task vuoto e ha molte opzioni di personalizzazione. Si possono vedere queste opzioni emdiante:
$ php symfony help generate:task
I task possono accettare parametri (obbligatori, in un ordine predefinito) e opzioni (parametri opzionali non ordinati). Il listato 16-14 mostra un task più completo, che sfrutta tutte queste caratteristiche.
Listato 16-14 - Un esempio di task più completo, in lib/task/mioSecondoTask.class.php
class mioSecondoTask extends sfBaseTask { protected function configure() { $this->namespace = 'foo'; $this->name = 'mioSecondoTask'; $this->briefDescription = 'Fa alcune cose con stile'; $this->detailedDescription = <<<EOF Il task [foo:mioSecondoTask|INFO] gestisce il processo della realizzazione di qualche compito. Chiamarlo con: [php symfony foo:mioSecondoTask frontend|INFO] Si può abilitare l'output dettagliato utilizzando l'opzione [verbose|COMMENT]: [php symfony foo:mioSecondoTask frontend --verbose=on|INFO] EOF; $this->addArgument('application', sfCommandArgument::REQUIRED, 'The application name'); $this->addOption('verbose', null, sfCommandOption::PARAMETER_REQUIRED, 'Enables verbose output', false); } protected function execute($arguments = array(), $options = array()) { // aggiungere qua il codice } }
note
Se il task ha bisogno di accedere al livello del database, deve estendere sfPropelBaseTask
invece di sfBaseTask
. L'inizializzazione del task si prenderà cura di caricare le classi di Propel. Si può aprire una connessione al database nel metodo execute()
chiamando:
$databaseManager = new sfDatabaseManager($this->configuration);
Se la configurazione del task definisce un parametro application
e env
, questi vengono automaticamente considerati quando viene creata la configurazione del task, quindi un task può utilizzare una qualunque delle connessioni a database definite nel file databases.yml
. Per impostazione predefinita, gli scheletri generati dalla chiamata a generate:task
comprendono questa inizializzazione.
Per ulteriori esempi sulle capacità del sistema dei task, si possono guaradre i sorgenti dei task esistenti di symfony.
Popolare un database
Nel processo di sviluppo delle applicazioni, gli sviluppatori devono spesso affrontare il problema di popolare il database. Esistono alcune soluzioni specifiche per alcuni sistemi di database, ma nessuno può essere utilizzato sopra la nostra mappatura oggetti-relazioni. Grazie a YAML e all'oggetto sfPropelData
, symfony può trasferire automaticamente i dati da una sorgente di semplice testo a un database. Sebbene la scrittura di un file di testo come sorgente per i dati possa sembrare che necessiti di più lavoro che inserire i record a mano con un'interfaccia CRUD, essa farà risparmiare tempo nel lungo periodo. Questa caratteristica verrà molto utile per archiviare automaticamente e inserire i dati di test per l'applicazione.
Sintassi del file per le fixture
Symfony è in grado di leggere file di dati che seguono una sintassi molto semplice chiamata YAML, a condizione che si trovino sotto la cartella data/fixtures/
. I file con le fixture sono organizzati per classi e ogni sezione di una classe inizia con il nome della classe come intestazione. Per ciascuna classe, i record etichettati con una stringa univoca sono definiti da una serie di coppie nomecampo: valore
.
Il listato 16-15 mostra un esempio di un file di dati per popolare il database.
Listato 16-15 - Esempio di un file fixture, in data/fixtures/import_data.yml
Article: ## Inserisce i record nella tabella blog_article first_post: ## Etichetta del primo record title: I miei primi ricordi content: | Per un lungo periodo sono andato a dormire presto. Qualche volta, quando avevo la mia candela, i miei occhi si chiudevano così in fretta che non avevo nemmeno il tempo di dire "Vado a dormire". second_post: ## Etichetta del secondo record title: Le cose peggiorarono content: | A volte sperava che fosse morta, senza dolore, in un qualche incidente, lei che era fuori di casa per le strade, attraversando vie occupate, attraversando vie trafficate, dalla mattina alla sera.
Symfony traduce le chiavi delle colonne in metodi setter utilizzando un convertitore camelCase (setTitle()
, setContent()
). Ciò significa che è possibile definire una chiave password
, anche se la tabella attuale non dispone di un campo password
; basta definire un metodo setPassword()
nell'oggetto User
e si possono popolare le altre colonne con un algoritmo basato sulla password (ad esempio, una versione hash della password).
La colonna della chiave primaria non ha bisogno di essere definita. Poiché si tratta di un campo a incremento automatico, il livello del database sa come calcolarlo.
Anche le colonne created_at
non devono essere impostate, perché symfony sa che i campi chiamati in questo modo, quando vengono creati devono essere impostati all'ora corrente del sistema.
Avviare l'importazione
Il task propel:data-load
importa i dati dai file YAML al database. Le impostazioni di connessione provengono dal file databases.yml
e quindi hanno bisogno di un nome di applicazione per l'esecuzione. Opzionalmente, è possibile specificare un nome di ambiente con l'aggiunta di una opzione --env
(dev
per impostazione predefinita).
$ php symfony propel:data-load --env=prod --application=frontend
Questo comando legge tutti i file YAML con le fixture dalla cartella data/fixtures/
e inserisce i record nel database. Per impostazione predefinita, sostituisce il contenuto esistente del database, ma se si aggiunge l'opzione --append
, il comando non cancellerà i dati già presenti.
$ php symfony propel:data-load --append --application=frontend
Nella chiamata è possibile specificare un'altra cartella per le fixture. In questo caso, aggiungere un percorso relativo alla cartella del progetto dove sono state inserite.
$ php symfony propel:data-load --application=frontend data/miefixture
Utilizzo di tabelle collegate
Si è imparato come aggiungere record a una singola tabella, ma come si fa ad aggiungere record con chiavi esterne a un'altra tabella? Dal momento che la chiave primaria non è inclusa nei dati delle fixture, c'è bisogno di un modo alternativo per relazionare dei record con altri.
Torniamo all'esempio del capitolo 8, dove la tabella blog_article
è collegata alla tabella blog_comment
, come mostrato in figura 16-8.
Figura 16-8 - Esempio di un modello relazionale di database
Questo è un esempio di come le etichette date ai record siano effettivamente utili. Per aggiungere un campo Comment
all'articolo first_post
, si possono semplicemente aggiungere le righe mostrate nel listato 16-16 al file di dati import_data.yml
.
Listato 16-16 - Aggiungere un record a una tabella collegata, in data/fixtures/import_data.yml
Comment: first_comment: article_id: first_post author: Anonymous content: La tua prosa è troppo prolissa. Scrivere frasi più brevi.
Il task propel:data-load
riconosce l'etichetta che è stata data precedentemente a un articolo nel file import_data.yml
e prende la chiave primaria del corrispondente record di Article
per impostare il campo article_id
. Non si vedranno mai gli ID dei record, è sufficiente collegarli alle loro etichette.
L'unico vincolo per i record collegati è che gli oggetti chiamati in una chiave esterna devono essere definiti in precedenza nel file; il che è come si farebbe se si definissero uno per uno. I file di dati vengono analizzati dall'alto verso il basso e l'ordine in cui sono scritti i record è importante.
Questo metodo funziona anche per le relazioni molti a molti, dove due classi sono collegate attraverso una terza classe. Ad esempio, un Article
può avere molti Authors
e un Author
può avere molti Articles
. Di solito per fare questo si utilizza la classe ArticleAuthor
, corrispondente a una tabella article_author
con una colonna article_id
e una colonna author_id
.
Il listato 16-17 mostra come scrivere un file fixture per definire relazioni molti a molti con questo modello. Notare il nome della tabella in plurale che è stato usato qui. Questo è ciò che fa scattare la voglia di avere una classe intermedia.
Listato 16-17 - Aggiungere un record a una tabella collegata con una relazione molti a molti, in data/fixtures/import_data.yml
Author: first_author: name: John Doe article_authors: [first_post, second_post]
Un file di dati può contenere dichiarazioni di più classi. Ma quando si ha la necessità di inserire molti dati per numerose tabelle, il file con le fixture potrebbe divenare troppo grosso per essere gestito con facilità.
Il task propel:data-load
analizza tutti i file che vengono trovati nella cartella fixtures/
, quindi nessuno impedisce di suddividere un file YAML con le fixture in file più piccoli. La cosa importante da ricordare è che le le chiavi esterne impongono un ordine di elaborazione per le tabelle. Per essere sicuri che vengano analizzati nel giusto ordine, prefissare i nomi dei file con un numero ordinale.
100_article_import_data.yml 200_comment_import_data.yml 300_rating_import_data.yml
note
Doctrine non ha bisogno di nomi di file specifici perché si prende cura automaticamente di eseguire i comandi SQL nel giusto ordine.
Il deploy delle applicazioni
Symfony offre comandi manuali per sincronizzare due versioni di un sito web. Questi comandi sono per lo più utilizzati per caricare un sito web da un server di sviluppo a un host finale connesso ad Internet.
L'utilizzo di rsync
per il trasferimento incrementale di file
L'invio della cartella principale di un progetto tramite FTP va bene per il primo trasferimento, ma quando si ha bisogno di caricare un aggiornamento dell'applicazione, in cui sono cambiati solo alcuni file, l'FTP non è l'ideale. È necessario trasferire nuovamente l'intero progetto, il che è uno spreco di tempo e di larghezza di banda. In alternativa si può passare alla cartella in cui si sa che alcuni file sono stati modificati e trasferire solo quelli con certe date di modifica. Questo è un lavoro che richiede tempo ed è suscettibile di errori. Inoltre, il sito web può diventare non disponibile o restituire errori durante il tempo del trasferimento.
La soluzione supportata da symfony è la sincronizzazione rsync attraverso SSH. [Rsync] (http://samba.anu.edu.au/rsync/) è una utility a riga di comando che consente il trasferimento incrementale di file in modo veloce ed è open source. Con il trasferimento incrementale, solo i dati modificati verranno inviati. Se un file non è stato modificato, non sarà inviato all'host. Se un file è stato modificato solo in parte, sarà inviata solo la parte cambiata. Il vantaggio principale è che la sincronizzazione con rsync trasferire solo una piccola quantità di dati ed è molto veloce.
Symfony aggiunge SSH a rsync per mettere in sicurezza il trasferimento dei dati. Sempre più host commerciali supportano un tunnel SSH per mettere in sicurezza l'upload dei file sui loro server e questa è una buona pratica per evitare problemi di sicurezza.
Il client SSH chiamato da symfony utilizza le impostazioni di connessione del file config/properties.ini
. Il listato 16-18 fornisce un esempio di impostazioni di connessione per un server di produzione. Scrivere le impostazioni del proprio server di produzione in questo file prima di ogni sincronizzazione. È anche possibile definire una impostazione unica dei parametri per fornire dei propri parametri per la riga di comando rsync.
Listato 16-18 - Esempio di impostazioni di connessione per una concronizzazione con il server, in mioprogetto/config/properties.ini
name=mioprogetto [production] host=miaapp.esempio.com port=22 user=mioutente dir=/home/mioaccount/mioprogetto/
note
Non bisogna confondere il server di produzione (il server host, come definito nel file properties.ini
del progetto) con l'ambiente di produzione (il front controller e la configurazione utilizzati in produzione, presenti nei file di configurazione di una applicazione).
Eseguireun rsync su SSH richiede diversi comandi e la sincronizzazione può necessitare di di un po' di tempo nel ciclo di vita di una applicazione. Per fortuna, symfony automatizza questo processo con un unico comando:
$ php symfony project:deploy production
Questo comando lancia il comando rsync
in modalità dry; questo vuol dire che mostra i file che devono essere sincronizzati, senza sincronizzarli realmente. Se si desidera effettuare la sincronizzazione, è necessario richiederlo esplicitamente con l'aggiunta dell'opzione --go
.
$ php symfony project:deploy production --go
Non dimenticarsi di cancellare la cache nel server di produzione dopo la sincronizzazione.
tip
Prima di distribuire l'applicazione nel server di produzione, è meglio controllare la configurazione con check_configuration.php
. Questa utility si trova nella cartella data/bin
di symfony. Controlla l'ambiente rispetto ai requisiti di symfony. Lo si può lanciare ovunque:
$ php /percorso/di/symfony/data/bin/check_configuration.php
Anche se si può utilizzare questa utility da riga di comando, è fortemente raccomandato di lanciarla da web, copiandola nella radice della cartella web, perché PHP può utilizzare differenti file di configurazione php.ini
per l'interfaccia a riga di comando e il web.
Ignorare i file irrilevanti
Quando si sincronizza il progetto symfony con un host di produzione, alcuni file e cartelle non dovrebbero essere trasferiti:
- Tutte le cartelle del controllo di versione (
.svn/
,CVS/
e così via) e il loro contenuto, sono necessari solo per lo sviluppo. - Il front controller dell'ambiente di sviluppo non deve essere disponibile all'utente finale. Gli strumenti per il debug e i log disponibili quando si usa l'applicazione attraverso questo front controller rallentano l'applicazione e forniscono informazioni sulle variabili delle azioni. È qualcosa da tenere lontano dal pubblico dominio.
- Le cartelle
cache/
elog/
del progetto nel server host non devono essere cancellate ogni volta che si fa una sincronizzazione. Queste cartelle devono essere ignorate. Se si ha una cartellastats/
, anche questa probabilmente dovrebbe essere trattata allo stesso modo. - I file caricati dagli utenti non dovrebbero essere trasferiti. Una delle buone pratiche per i progetti symfony è di memorizzare i file caricati nella cartella
web/uploads/
. Questo permette di escludere tutti questi file dalla sincronizzazione indicando una sola cartella.
Per escludere dei file dalla sincronizzazione con rsync, aprire e modificare il file rsync_exclude.txt
presente nella cartella mioprogetto/config/
. Ogni linea può contenere un file, una cartella o uno schema. La struttura dei file di symfony è organizzata logicamente e progettata per ridurre al minimo il numero di file o di cartelle da escludere manualmente dalla sincronizzazione. Vedere il listato 16-19 per un esempio.
Listato 16-19 - Esempio di configurazione di esclusione file con rsync, in mioprogetto/config/rsync_exclude.txt
# Project files /cache/* /log/* /web/*_dev.php /web/uploads/* # SCM files .arch-params .bzr _darcs .git .hg .monotone .svn CVS
note
Le cartelle cache/
e log/
non dovrebbero essere sincronizzate rispetto al server di svuluppo, ma devono comunque esistere nel server in produzione. Quindi se non sono presenti nel progetto, bisogna crearle a mano dentro a mioprogetto/
.
Gestire una applicazione in produzione
Il comando che è usato più spesso nei server in produzione è cache:clear
. Bisogna lanciarlo ogni volta che si aggiorna symfony o il progetto (ad esempio, dopo aver chiamato i ltask project:deploy
) e ogni volta che si fanno dei cambiamenti nella configurazione in produzione.
$ php symfony cache:clear
tip
Se l'interfaccia a riga di comando non è disponibile nel server di produzione, si può cancellare la cache manualmente cancellando il contenuto della cartella cache/
.
Si può disabilitare temporaneamente l'applicazione per, ad esempio, aggiornare una libreria o una grossa mole di dati.
$ php symfony project:disable APPLICATION_NAME ENVIRONMENT_NAME
Per impostazione predefinita, una applicazione disabilitata visualizza la pagina sfConfig::get('sf_symfony_lib_dir')/exception/data/unavailable.php
, ma se si crea un proprio file unavailable.php
nella cartella config/
del progetto, symfony userà quella.
Il task project:enable
riabilita l'applicazione e cancella la cache.
$ php symfony project:enable APPLICATION_NAME ENVIRONMENT_NAME
caution
project:disable
attualmente non ha effetto se il parametro check_lock
non è
impostato a true
in settings.yml.
Riepilogo
Combinando i log di PHP con quelli di symfony, è possibile monitorare ed eseguire il debug dell'applicazione facilmente. Durante lo sviluppo, la modalità di debug, le eccezioni e la barra web di debug aiutano a individuare i problemi. Per facilitare il debug è anche possibile inserire messaggi personalizzati nei file di log o nella barra degli strumenti.
L'interfaccia a riga di comando fornisce un gran numero di strumenti che facilitano la gestione delle applicazioni, durante le fasi di sviluppo e di produzione. Tra gli altri, i task per il popolamento dei dati e quello per la sincronizzazione fanno risparmiare molto tempo.
This work is licensed under the GFDL license.