Development

Documentation/it_IT/book/1.1/16-Application-Management-Tools

You must first sign up to be able to contribute.

Capitolo 16 - Strumenti di gestione delle applicazioni

Sia durante la fase di sviluppo che durante quella di distribuzione gli sviluppatori hanno bisogno di strumenti di diagnostica per verificare che l'applicazione funzioni come ci si aspetta. Tale informazioni sono generalmente aggregate tramite utility di log e di debug. A causa del ruolo centrale che il framework (come symfony) gioca nello sviluppo, è importante che tali strumenti siano strettamente integrati.

Durante la vita di un'applicazione sul server di produzione, l'amministratore opera ripetutamente una grande quantità di operazioni, dalla rotazione dei log agli upgrade. Un framework dovrebbe fornire anche strumenti per automatizzare il più possibile tali task.

Questo capitolo spiega come gli strumenti di gestione delle applicazioni di symfony aiutano a soddisfare tali bisogni.

Logging

L'unico modo per capire cosa è andato storto durante l'esecuzione di una richiesta è controllare le tracce dell'esecuzione del processo. Fortunatamente, come imparerai in questa sezione, sia PHP che symfony tendono ad immagazzinare una grande quantità di dati di questo tipo.

Log di PHP

PHP possiede un parametro chiamato error_reporting definito in php.ini, che specifica quali eventi PHP devono essere loggati. Symfony ti permette di fare l'override di tale valore, sia per applicazione che per ambiente, nel file settings.yml, come mostrato nel Listato 16-1.

Listato 16-1 - Impostare il livello di reporting 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:
    <?php echo (E_ALL | E_STRICT)."\n" ?>

Per evitare problemi di prestazioni nell'ambiente di produzione, il server logga solo gli errori PHP critici. Nell'ambiente di sviluppo, invece, tutti gli eventi vengono loggati, in modo che lo sviluppatore abbia tutte le informazioni necessarie per rintracciare errori.

Puoi trovare i file dei log di PHP a seconda di come hai configurato il tuo php.ini. Se non hai mai cambiato la configurazione di default, probabilmente PHP usa i log del web server (come quelli di Apache). In tal caso, troverai i file nella cartella di log del web server.

Nuovo in symfony 1.1: Il formato dei file di log ora è configurabile sovrascrivendo le impostazioni format e/o time_format in factories.yml, come mostrato nel Listato 16-3.

Listato 16-3 - Cambiare il formato dei log

all:
  logger:
    param:
      sf_file_debug:
        format:      %time% %type% [%priority%] %message%%EOL%
        time_format: %b %d %H:%M:%S

Configurazione del livello di log di symfony

In aggiunta ai log standard di PHP, symfony può tenere traccia di molti eventi personalizzati. Puoi trovare tutti i log di symfony nella cartella myproject/log/. C'è un file per applicazione e ambiente. Ad esempio, il file di log per l'ambiente di sviluppo dell'applicazione frontend si chiama frontend_dev.log, mentre quello per l'ambiente di produzione si chiama frontend_prod.log, e così via.

Se stai facendo girare un'applicazione symfony, dai un'occhiata ai suoi file di log. La sintassi è molto semplice. Per ogni evento viene aggiunta una linea al file dell'applicazione. Ogni linea include l'ora esatta dell'evento, la sua natura, l'oggetto che viene processato, e qualche altro dettaglio importante. Il Listato 16-2 ne mostra un esempio.

Listato 16-2 - Esempio di file di log di symfony, in log/frontend_dev.php [php] Nov 15 16:30:25 symfony [info ] {sfAction} call "barActions->executemessages()" Nov 15 16:30:25 symfony [debug] SELECT bd_message.ID, bd_message.SENDER_ID, bd_... Nov 15 16:30:25 symfony [info ] {sfCreole} 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/myproject/... Nov 15 16:30:25 symfony [info ] {sfView} render to client

Puoi trovare molti dettagli in questi file, incluse le query effettivamente spedite al db, le template chiamate, le catene di chiamate tra gli oggetti e così via.

Configurazione dei livelli di log di symfony

Esistono otto livelli per i messaggi di log di symfony: emerg, alert, crit, err, warning, notice, info, e debug, che sono gli stessi livelli del pacchetto PEAR::Log ([http://pear.php.net/package/Log/]). Puoi definire il massimo livello da loggare per ogni ambiente nel file di configurazione factories.yml di ogni applicazione, come mostrato nel Listato 16-4.

Listato 16-4 - Configurazione di default del logging, in frontend/config/logging.yml

prod:
  logger:
    param:
      level: err

Per default, in tutti gli ambienti tranne quello di produzione, tutti gli eventi vengono loggati (fino al minimo importante, il livello debug). Nell'ambiente di produzione, il logging è disabilitato per default; se imposti enabled a on, appariranno nel logo solo i messaggi più importanti (da crit a emerg).

Puoi cambiare il livello di logging nel file factories.yml per ogni ambiente per limitare il tipo di messaggi da loggare.

TIP Per controllare se il logging è abilitato, richiama sfConfig::get('sf_ logging_enabled').

Aggiungere un messaggio di log

Puoi aggiungere un messaggio nei file di log di symfony usando una delle tecniche descritte nel Listato 16-5.

Listato 16-5 - Aggiungere un messaggio di log personalizzato

[php]
// Da un'azione
$this->logMessage($message, $level);

// Da una template
<?php use_helper('Debug') ?>
<?php log_message($message, $level) ?>

Alternativamente, per scrivere un messaggio di log da un qualsiasi punto della tua applicazione, usa il metodo sfLogger direttamente, come mostrato nel Listato 16-6. I metodi disponibili prendono lo stesso nome dei livelli di log.

Listato 16-6 - Aggiungere un messaggio di log da un qualsiasi punto

[php]
if (sfConfig::get('sf_logging_enabled'))
{
  sfContext::getInstance()->getLogger()->info($message);
}

SIDEBAR Nuovo in symfony 1.1: Personalizzare il logging

Il sistema di logging di symfony è molto semplice, per cui è anche semplice personalizzarlo. Il solo prerequisito è che la classe di log estenda la classe sfLogger, che definisce il metodo doLog(). Symfony richiama il metodo doLog() con due parametri: $message (il messaggio da loggare) e $priority (il livello di log)

La classe myLogger definisce un semplice logger che usa la funzione PHP error_log:

class myLogger extends sfLogger { protected function doLog($message, $priority) { error_log(sprintf('%s (%s)', $message, sfLogger::getPriorityName($priority))); } }

Per creare un logger da una classe esistente, basta implementare l'interfaccia sfLoggerInterface, che definisce un metodo log(). Il metodo accetta gli stessi parametri del metodo doLog():

require_once('Log.php'); require_once('Log/error_log.php');
// Definisce un piccolo contenitore che implementa l'interfaccia // per il logger che vogliamo usare con symfony class Log_my_error_log extends Log_error_log implements sfLoggerInterface { }

Pulire e ruotare i file di log

Non dimenticare di pulire periodicamente la cartella log/ delle tue applicazioni, perché tali file hanno la strana abitudine di crescere di diversi megabyte al giorno, in relazione, ovviamente, al traffico. A tale scopo symfony fornisce un task log-purge, che puoi lanciare a mano od impostare in una tabella di cron. Ad esempio, il comando seguente elimina i file di log delle applicazioni in cui è specificato purge: on nel file logging.yml (che è il default):

> php symfony log:clear

Sia per le prestazioni che per la sicurezza, probabilmente vorrai tenere i log di symfony in diversi piccoli file piuttosto che in un unico file di grandi dimensioni. La strategia di storage dei file di log ideale è di fare il backup per poi svuotare il file di log regolarmente, ma di mantenere un numero limitato di file di backup. Puoi abilitare questo tipo di rotazione e specificarne i parametri nel file logging.yml. Ad esempio, con un period di 7 giorni ed una history (numero di backup) di 10, come mostrato nel Listato 16-7, lavoreresti con un file di log attivo più 10 backup contenenti ognuno 7 giorni di log.

Listato 16-7 - Lanciare la rotazione dei log

> php symfony log:rotate frontend prod --period=7 --history=10

Il backup dei file di log viene tenuto nella cartella logs/history/, con un suffisso che indica la data in cui è stato fatto il backup.

Debugging

Non ha importanza quanto sei bravo nello sviluppo, prima o poi, anche se usi symfony, qualche errore lo commetterai. Trovare e capire gli errori è una delle chiavi dello sviluppo veloce di un'applicazione. Fortunatamente, symfony mette a disposizione diversi strumenti di debugging per lo sviluppatore.

Modalità di debug

Symfony possiede una modalità di debug per facilitare lo sviluppo ed il debug delle applicazioni. Quando è attivo, succede questo:

  • La configurazione viene controllata ad ogni richiesta, per cui qualsiasi cambiamento non richiede la pulizia della cache.
  • I messaggi di errore vengono visualizzati in modo chiaro ed utile, in modo che si possa trovare immediatamente il "colpevole".
  • Sono disponibili strumenti di debugging ulteriori quali ad esempio i dettagli delle query al db.
  • Anche la modalità di debug di Propel viene attivata, per cui qualsiasi errore in una chiamata Propel visualizzerà dettagliatamente la catena delle chiamate tramite l'architettura Propel.

D'altra parte, quando la modalità di debug non è attiva, il processo è gestito nel modo seguente:

  • I file di configurazione YAML vengono processati una volta sola, e poi trasformati in file PHP nella cartella cache/config/. Ogni richiesta successiva alla prima ignora i file YAML e consulta quelli in cache. Di conseguenza, il processo delle richieste è molto più veloce.
  • Per permettere nuovamente la consultazione dei file di configurazione, occorre pulire la cache.
  • Un errore durante l'esecuzione genera un codice 500 (Internal Server Error), senza alcune spiegazioni sulle cause.

La modalità di debug viene attivata per applicazione nel front controller. È controllata dal valore della costante SF_DEBUG, come mostrato nel Listato 16-8.

Listato 16-8 - Esempio di front controller con la modalità di debug attiva, 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 Non dovresti attivare la modalità di debug sul server di produzione, e neppure lasciare la possibilità di farlo. Non solo peggiorerebbe le prestazioni, ma potrebbe anche rivelare le interna della tua applicazione, mettendone a rischio la sicurezza.

Le eccezioni di symfony

Quando capita un'eccezione in modalità di debug, symfony mostra un insieme di informazioni interessanti che aiutano a capire la causa del problema.

I messaggi di eccezione sono scritti chiaramente e si riferiscono alla causa più probabile del problema. Spesso suggeriscono anche una soluzione al problema, e per quelli più comuni c'è un link alla pagina del sito di symfony con maggiori dettagli sull'eccezione generata. Tramite la syntax highlighting viene mostrato il tratto di codice PHP che ha generato l'eccezione, e l'insieme delle chiamate ai metodi, come mostrato in Figura 16-1. Vengono mostrati anche gli argomenti passati ai metodi.

NOTE Symfony si basa veramente sulle eccezioni PHP per il report degli errori, che è molto meglio rispetto al modo in cui lavora PHP 4. Ad esempio, un errore 404 può essere triggerato tramite sfError404Exception.

Figura 16-1 - Esempio di eccezione di un'applicazione symfony

Esempio di eccezione di un'applicazione symfony

Durante la fase di sviluppo, le eccezioni symfony saranno di grande utilità per il debug.

L'estensione Xdebug

L'estensione Xdebug (http://xdebug.org/) permette di estendere le informazioni che vengono loggate dal web server. Symfony integra i messaggi di Xdebug nel proprio feedback, per cui è una buona idea attivare tale estensione durante il debug di un'applicazione. L'installazione dell'estensione dipende molto dalla piattaforma; consulta il sito di Xdebug per informazioni dettagliate al riguardo. Una volta che hai installato Xdebug, lo devi attivare nel file php.ini. Per le piattaforme *nix, questo si può fare aggiungendo la linea:

zend_extension="/usr/local/lib/php/extensions/no-debug-non-zts-20041030/xdebug.so"

mentre per le piattaforme Windows:

extension=php_xdebug.dll

Il Listato 16-9 mostra un esempio di configurazione di Xdebug, quale deve essere aggiunto nel file php.ini.

Listato 16-9 - Esempio di configurazione 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

Per poterla attivare devi infine riavviare il tuo web server.

CAUTION Non dimenticare di disattivare Xdebug sul tuo sistema in produzione. Altrimenti la velocità di esecuzione delle pagine ne soffrirà molto.

La toolbar Web Debug

I file di log contengono informazioni interessanti, ma non sono molto facili da leggere. Il compito più semplice, ovvero trovare le linee loggate per una richiesta particolare, può risultare difficoltoso se diversi utenti usano l'applicazione nello stesso momento e l'history del log è lunga. Ecco quando la web debug toolbar diventa utile.

Questa toolbar appare come un rettangolo semitrasparente nell'angolo in alto a destra del browser, come mostrato in Figura 16-2. Essa fornisce accesso agli eventi di log di symfony, alla configurazione corrente, alle proprietà della richiesta e gli oggetti della risposta, ai dettagli delle query eseguite sul db ed alle prestazioni.

Figura 16-2 - La web debug toolbar appare sull'angolo in alto a destra

La web debug toolbar appare sull'angolo in alto a destra

Il colore del background della toolbar dipende dal più alto livello dei messaggi di log durante la richiesta. Se non vi è alcun messaggio a livello debug, la toolbar rimane grigia. Se anche un solo messaggio raggiunge il livello err, la toolbar diventa rossa.

NOTE Non confondere la modalità di debug con la debug toolbar. Essa può essere visualizzata anche quando la modalità di debug è off, anche se in quel caso mostra molte meno informazioni.

Per attivare la web debug toolbar per un'applicazione, apri il file settings.yml e cerca la chiave web_debug. Negli ambienti prod e test, il suo valore di default è off, per cui se la desideri la devi attivare manualmente. Nell'ambiente dev, il valore di default è on, come mostrato nel Listato 16-10.

Listato 16-10 - Attivazione della web debug toolbar, in frontend/config/settings.yml

dev:
  .settings:
    web_debug:              on

Quando visualizzata, la toolbar offre una grande varietà di informazioni/interazioni:

  • Clicca sul logo di symfony per nasconderla; quando è nascosta, non copre gli elementi in quell'area della pagina.
  • Clicca la sezione vars & config per vedere i dettagli della richiesta, risposta, impostazioni e variabili PHP, come mostrato in Figura 16-3. La prima linea riassume le impostazioni più importanti della configurazione, come la modalità di debug, la cache, e la presenza di un acceleratore PHP (essi appaiono in rosso se disattivati, in verde se attivi).

Figura 16-3 - La sezione vars & config mostra tutte le variabili e costanti della richiesta

La sezione vars & config mostra tutte le variabili e costanti della richiesta

  • Quando la cache è abilitata, appare una freccia verde nella toolbar. Clicca tale freccia per riprocessare la pagina, ignorando la cache (la quale però non viene svuotata).
  • Clicca su logs & msg per vedere le informazioni dei file di log della richiesta corrente, come mostrato in Figura 16-4. A seconda dell'importanza degli eventi, esse saranno visualizzate in grigio, giallo o rosso. Puoi filtrare gli eventi visualizzati usando i link all'inizio della lista.

Figura 16-4 - La sezione logs & msg mostra i log per la richiesta corrente

La sezione logs & msg mostra i log per la richiesta corrente

NOTE Quando l'azione corrente è il risultato di un redirect, saranno presenti nel pannello solo i log dell'ultima richiesta, per cui i file di log sono ancora indispensabili per un buon debug.

  • Per le richieste che eseguono query SQL, nella toolbar appare l'icona di un database. Cliccala per vedere i dettagli delle query, come mostrato in Figura 16-5.
  • Alla destra dell'orologio c'è il tempo totale necessario a processare la richiesta. Ricorda che la web debug toolbar e la modalità di debug rallentano l'esecuzione delle richieste, per cui evita di considerare tali valori in se e confronta l'esecuzione fra due pagine. Clicca sull'orologio per vedere i dettagli del tempo di processione per categoria, come mostrato in Figura 16-6. Symfony visualizza il tempo speso su parti specifiche della richiesta. Solo i tempi relativi alla richiesta corrente hanno senso per l'ottimizzazione, per cui il tempo nel core di symfony non viene visualizzato. Ecco perché tale valore non è la somma del tempo totale.
  • Clicca sulla X rossa a destra per chiuderla.

Figura 16-5 - La sezione del db mostra le query eseguite per la richiesta corrente

La sezione del db mostra le query eseguite per la richiesta corrente

Figura 16-6 - L'icona dell'orologio mostra i tempi di esecuzione per categoria

L'icona dell'orologio mostra i tempi di esecuzione per categoria

SIDEBAR Aggiungere il tuo timer Symfony usa la classe sfTimer per calcolare il tempo utilizzato nella configurazione, nel modello, azione e vista. Utilizzando lo stesso oggetto, puoi calcolare un processo custom e visualizzare il risultato con gli altri timer nella web debug toolbar. Questo può risultare molto utile quando lavori sull'ottimizzazione delle performance.

Per inizializzare il timing su uno specifico frammento di codice, chiama il metodo getTimer(). Esso restituirà un oggetto sfTimer e comincerà il calcolo. Chiama il metodo addTime() su tale oggetto per fermare il calcolo. Il tempo necessario sarà disponibile tramite il metodo getElapsedTime(), e visualizzato nella toolbar insieme agli altri.

[php]
// Initialize the timer and start timing
$timer = sfTimerManager::getTimer('myTimer');

// Do things
...

// Stop the timer and add the elapsed time
$timer->addTime();

// Get the result (and stop the timer if not already stopped)
$elapsedTime = $timer->getElapsedTime();

Il beneficio derivante dal dare un nome od ogni timer è quello di poter accumulare i timing. Ad esempio, se il timer myTimer viene utilizzato in una utility chiamata due volte a richiesta, la seconda chiamata a getTimer('myTimer') farà ripartire il timing dall'ultimo punto in cui è stato chiamato il metodo addTime(), sommando. Chiamando getCalls() sull'oggetto timer troverai il numero di volte in cui un timer è stato lanciato, ed anche tale informazione viene visualizzata nella toolbar.

[php]
// Get the number of calls to the timer
$nbCalls = $timer->getCalls();

In modalità Xdebug, i messaggi di log sono molto più ricchi. Tutti gli script PHP e le funzioni chiamate vengono loggate, e symfony sa come collegare tale informazioni con i propri log interni. Ogni linea dei messaggi di log finisce con un pulsante con una doppia freccia, che puoi cliccare per vedere più informazioni sulla richiesta relativa. Se qualcosa andasse male, la modalità Xdebug ti fornisce la maggior quantità possibile di dettagli per capire dove e come.

NOTE La web debug toolbar non è inclusa per default in risposte Ajax e documenti che non abbiano un content-type HTML. Per le altre pagine, puoi disabilitare la toolbar manualmente da dentro un'azione chiamando semplicemente sfConfig::set('sf_web_debug', false).

Debugging manuale

Avere accesso ai messaggi del framework di debug è carino, ma è meglio avere la capacità di loggare i tuoi propri messaggi. Symfony mette a disposizione dei collegamenti, disponibili sia nelle template che nelle azioni, per tenere traccia dei tuoi eventi durante l'esecuzione.

I tuoi messaggi di log appaiono sia nei file di log di symfony sia nella toolbar, esattamente come gli eventi normali. (Il Listato 16-4 ne mostrava la sintassi) Un messaggio personalizzato è un buon modo di controllare il valore di una variabile in una template, ad esempio. Il Listato 16-11 mostra come usare la web debug toolbar per il feedback dello sviluppatore dalla template (da un'azione puoi usare $this->logMessage()).

Listato 16-11 - Inserire un messaggio nel log per il debugging

[php]
<?php use_helper('Debug') ?>
...
<?php if ($problem): ?>
  <?php log_message('{sfAction} been there', 'err') ?>
  ...
<?php endif ?>

L'utilizzo del livello err garantisce che il messaggio sarà chiaramente visibile nella lista dei messaggi, come mostrato in Figura 16-7.

Figura 16-7 - Un messaggio personalizzato appare nella sezione logs & msgs della web debug toolbar

Un messaggio personalizzato appare nella sezione logs & msgs della web debug toolbar

Se tu non volessi aggiungere una linea al log, ma semplicemente tracciare un valore, dovresti usare debug_message invece di log_message. Tale metodo (esiste anche un helper con lo stesso nome) visualizza un messaggio nella toolbar, all'inizio della sezione logs & msgs. Controlla il Listato 16-12 per un esempio.

Listato 16-12 - Inserire un messaggio nella web debug toolbar

[php]
// From an action
$this->debugMessage($message);

// From a template
<?php use_helper('Debug') ?>
<?php debug_message($message) ?>

Usare symfony fuori da un contesto web

Potresti voler eseguire uno script da linea di comando (o tramite cron) con accesso a tutte le classi e le feature di symfony, per esempio per lanciare dei job che inviino email o per aggiornare periodicamente il tuo modello tramite un calcolo intensivo per il processore. Il modo semplice di farlo è creare uno script PHP che riproduca i primi passi di un front controller, in modo che symfony sia inizializzato propriamente. Puoi anche usare il sistema a linea di comando di symfony, per sfruttare i vantaggi del parsing dei parametri e dell'inizializzazione automatica del database.

L'inizializzazione di symfony richiede solo un paio di righe di codice PHP. Puoi sfruttare tutte le feature di symfony creando un file PHP, per esempio sotto la cartella lib/ del tuo progetto, iniziando con le righe mostrate nel Listato 16-13.

Listato 16-13 - Semplice script batch, in lib/myScript.php

<?php

require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true);
sfContext::createInstance($configuration);

// Remove the following lines if you don't use the database layer
$databaseManager = new sfDatabaseManager($configuration);
$databaseManager->loadConfiguration();

// add code here

Assomiglia molto alle prime righe del front controller (vedi Chapter 6), perché queste righe fanno la stessa cosa: inizializzano symfony, fanno il parsing delle configurazioni di un progetto e di un'applicazione. Nota che il metodo ProjectConfiguration::getApplicationConfiguration si aspetta tre parametri:

  • il nome di un'applicazione an application name
  • il nome di un ambiente
  • un booleano, che definisce se il debug deve essere abilitato o meno

Per eseguire il tuo codice, basta richiamare lo script da linea di comando:

> php lib/myScript.php

Task personalizzati (nuovo in symfony 1.1)

Un modo alternativo di creare script personalizzati da linea di comando usando symfony è scrivere un task di symfony. Proprio come i task cache:clear e propel:build-model, puoi lanciare questi tuoi task personalizzati da linea di comando con php symfony. I task personalizzati hanno la possibilità di fare il parsing dei parametri e delle opzioni della linea di comando, possono includere dei propri testi di aiuto, e possono estendere dei task esistenti.

Un task personalizzato è solo una classe che estende sfBaseTask e va collocata sotto la cartella lib/task/, o sotto la radice del progetto o nella cartella di un plugin. Il nome deve terminare con 'Task.class.php'. Il Listato 16-14 mostra un esempio di task personalizzato.

Listato 16-14 - Task di esempio, in lib/task/testHelloTask.class.php

[php]
class testHelloTask extends sfBaseTask
{
  protected function configure()
  {
    $this->namespace = 'test';
    $this->name = 'hello';
    $this->briefDescription = 'Says hello';
  }

  protected function execute()
  {
    // your code here
    $this->log('Hello, world!');
  }
}

Il codice scritto nel metodo execute ha accesso a tutte le librerie di symfony, proprio come il precedente script batch. La differenza è nel modo in cui richiami il task personalizzato:

> php symfony test:hello

Il nome del task proviene dalle proprietà protette namespace e name (non dal nome della classe, né dal nome del file). E siccome il tuo task è integrato nella linea di comando di symfony, compare nella lista dei task se scrivi:

> php symfony

Piuttosto che scriverti da solo uno scheletro per i task, puoi usare il task di symfony generate:task. Esso crea un task vuoto, ed ha molte opzioni di personalizzazione. Sii certo di controllarle richiamando:

> php symfony help generate:task

I task possono accettare dei parametri (obbligatori, in un ordine predefinito) e delle opzioni (parametri opzionali e non ordinati). Il Listato 16-15 mostra un task più completo, che sfrutta tutte queste caratteristiche.

Listato 16-15 - Task di esempio più complesso, in lib/task/mySecondTask.class.php

[php]
class mySecondTask extends sfBaseTask
{
  protected function configure()
  {
    $this->namespace        = 'foo';
    $this->name             = 'mySecondTask';
    $this->briefDescription = 'Does some neat things, with style';
    $this->detailedDescription = <<<EOF
The [foo:mySecondTask|INFO] task manages the process of achieving things for you.
Call it with:

  [php symfony foo:mySecondTask frontend|INFO]

You can enable verbose output by using the [verbose|COMMENT] option:

  [php symfony foo:mySecondTask 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())
  {
    // add code here

  }
}

NOTE Se il tuo task ha bisogno di accedere al database, dovrebbe estendere sfPropelBaseTask invece che sfBaseTask. L'inizializzazione del task si occuperà allora di caricare le classi Propel addizionali. Puoi iniziare una connessione al database nel metodo execute() richiamando:

$databaseManager = new sfDatabaseManager($this->configuration);

Se la configurazione del task definisce dei parametri application ed env, questi sono considerati automaticamente quando viene costruita la configurazione del task, in modo che il task possa usare ogni connessione definita nel tuo databases.yml. Di default, gli scheletri generati da generate:task includono tale inizializzazione.

Per ulteriori esempi sulle abilità del sistema dei task, guarda i sorgenti dei task predefiniti di symfony.

Se dichiari un task con lo stesso nome di uno già esistente, la tua classe sovrascrive il task esistente. Questo vuol dire che un plugin può sovrascrivere un task di symfony, e che un progetto può sovrascrivere il task di un plugin ed un task di symfony. Aggiungilo all'abilità di estendere una classe task esistente ed avrai un sistema a linea di comando molto estensibile!

Popolare un database

Nel processo di sviluppo di un'applicazione, spesso gli sviluppatori devono affrontare la problematica della popolazione di un db. Esiste qualche soluzione specifica, ma nessuna che possa essere usata in generale sulla base di un E/R. Grazie all'oggetto sfPropelData ed a YAML, symfony può trasferire automaticamente dati da un file di testo ad un database. Anche se potrebbe sembrare che scrivere un file di testo necessiti di più tempo che utilizzare un'interfaccia CRUD, alla fine noterai che invece ne risparmi.

Sintassi di file fixture

Symfony può leggere file di dati che seguano una sintassi YAML molto semplice, presupposto che tali file siano situati nella cartella data/fixtures/. I file fixture sono organizzati in classi, ogni sezione di una classe comincia con il nome della classe ed un header. Per ogni classe, i record chiamati con un nome unico sono identificati da coppie fieldname: value. Il Listato 16-16 mostra un esempio di file di dati per la popolazione.

Listato 16-16 - Esempio di file fixture, in data/fixtures/import_data.yml

Article:                             ## Insert records in the blog_article table
  first_post:                        ## First record label
    title:       My first memories
    content: |
      For a long time I used to go to bed early. Sometimes, when I had put
      out my candle, my eyes would close so quickly that I had not even time
      to say "I'm going to sleep."

  second_post:                       ## Second record label
    title:       Things got worse
    content: |
      Sometimes he hoped that she would die, painlessly, in some accident,
      she who was out of doors in the streets, crossing busy thoroughfares,
      from morning to night.

Symfony traduce le colonne chiavi in metodi setter utilizzando un convertitore camelCase (setTitle(), setContent()). Ciò significa ad esempio che tu potresti definire una chiave password anche se effettivamente nella tabella un campo password non esiste; dovresti solo definire un metodo setPassword() nell'oggetto User, potendo così popolare le altre colonne basandoti sulla password (ad esempio, una sua versione hash).

Le chiavi primarie non hanno bisogno di essere definite. Dato che sono campi auto-increment, il database sa come comportarsi.

Anche le colonne created_at non ne hanno bisogno, perché symfony sa che i campi che si chiamano in quel modo devono essere impostati con l'ora corrente.

Lanciare l'importazione

Il task propel:data-load importa i dati dai file YAML al database. Le impostazioni di connessione vengono dal file databases.yml, perciò c'è bisogno di un nome di applicazione per farlo girare. Opzionalmente puoi specificare un ambiente aggiungendo l'opzione --env (dev è il default).

> php symfony propel:data-load frontend

Questo comando legge tutti i file fixture nella cartella data/fixtures/ ed inserisce i record nel db. Per default, esso sostituisce i dati esistenti, ma se l'ultimo argomento della chiamata è append, il comando aggiungerà i dati.

> php symfony propel:data-load frontend append

Puoi anche specificare un altro file fixture o cartella nella chiamata. In questo caso, aggiungi un path relativo alla cartella data/ del progetto.

> php symfony propel:data-load frontend --dir[]=data/myfixtures

Usare tabelle collegate

Ora sai come importare dati in tabelle singole, ma come funziona con chiavi importate? Dato che le chiavi primarie non sono incluse nelle fixture, c'è bisogno di un modo alternativo.

Torniamo all'esempio del Capitolo 8, dove la tabella blog_article è collegata alla blog_comment, come mostrato in Figura 16-8.

Figura 16-8 - Esempio di modello database relazionale

Esempio di modello database relazionale

Ecco dove le etichette date ai record risultano veramente utili. Per aggiungere un campo Comment all'articolo first_post, devi solo aggiungere le linee del Listato 16-17 al file import_data.yml.

Listato 16-17 - Aggiungere record ad una tabella collegata, in data/fixtures/import_data.yml

Comment:
  first_comment:
    article_id:   first_post
    author:       Anonymous
    content:      Your prose is too verbose. Write shorter sentences.

Il task propel:data-load riconoscerà la label che avevi precedentemente dato ad un articolo nel file import_data.yml, per cui rintraccerà la chiave primaria del corrispondente record nella tabella Article per impostare il campo article_id. Non vedrai nemmeno gli ID dei record; li collegherai tramite le label, ciò non potrebbe essere più semplice.

L'unico vincolo per i record collegati è che gli oggetti chiamati nelle chiavi importate devono essere definiti prima nel file; che è poi quello che faresti se li definissi uno a uno. I file dati vengono parsati dall'alto al basso, per cui è importante l'ordine in cui sono scritti i record.

Nuovo in symfony 1.1: Funziona anche per relazioni molti-a-molti, in cui due classi sono collegate tramite una terza classe. Per esempio, un Article può avere divers Authors, ed un Author può avere diversi Articles. Di solito usi una classe ArticleAuthor per gestirlo, corrispondene ad una tabella article_author con delle colonne article_id e author_id. Il Listato 16-18 mostra come scrivere un file fixture per definire delle relazioni molti-a-molti con questo modello. Nota che le tabelle usano nomi al plurale: è questo che attiva la ricerca di una classe di tramite.

Listato 16-18 - Aggiungere un record ad un tabella legata da 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 dati può contenere le dichiarazioni di diverse classi. Ma se hai bisogno di inserire molti dati per diverse tabelle, il tuo file fixture potrebbe diventare troppo lungo e non facile da manipolare.

Il task propel:data-load fa il parsing di tutti i file della cartella fixtures/, per cui potresti suddividere i file YAML in parti più piccole. L'unica cosa da tenere a mente è l'ordine per le chiavi importate, per cui per essere sicuro di preservarlo puoi ad esempio aggiungere un prefisso numerico ai file.

100_article_import_data.yml
200_comment_import_data.yml
300_rating_import_data.yml

Distribuire applicazioni

Symfony offre comandi veloci per sincronizzare due versioni di un sito web. Tali comandi vengono principalmente usati per fare il deploy di un sito da un server di sviluppo ad uno di produzione, connesso ad Internet.

Congelamento di un progetto per il trasferimento tramite FTP

Il modo più comune per il deploy di un sito in produzione è quello di trasferire tutti i suoi file tramite FTP (o SFTP). Ad ogni modo, i progetti symfony usano le proprie librerie, ed a meno di sviluppare in una sandbox (che non è consigliato), o se le cartelle lib/ e data/ sono collegate tramite svn:externals, tali directory non si trovano nella cartella del progetto. Sia utilizzando la PEAR che link simbolici, riprodurre la stessa struttura di cartelle può richiedere tempo.

Ecco perché symfony fornisce una utility per "congelare" un progetto, ovvero per copiare le necessarie librerie nelle cartelle del progetto data/, lib/ e web/. In tal modo il progetto diventa come una sandbox, un'applicazione stand-alone.

> php symfony project:freeze symfony_data_dir

Il parametro symfony_data_dir è il percorso della cartella dei dati di symfony (se hai installato symfony tramite Subversion o un archivio, questa cartella è nella stessa cartella di lib di symfony; se hai installato symfony tramite il pacchetto PEAR, questa cartella si trova nella cartella data di PEAR).

Una volta che il progetto è congelato, puoi trasferire la cartella del progetto in produzione, e tutto funzionerà senza bisogno di PEAR, link simbolici o quant'altro.

TIP Diversi progetti congelati possono funzionare sullo stesso server con diverse versioni di symfony senza alcun problema.

Per riportare un progetto al suo stato iniziale, usa il task project:unfreeze. Esso elimina le cartelle data/symfony/, lib/symfony/ e web/sf/.

> php symfony project:unfreeze

Nota che se tu avessi avuto link simbolici in un'installazione symfony prima del congelamento, symfony se lo ricorderebbe e li ripristinerebbe nella loro posizione originale.

Usare rsync per il trasferimento incrementale

Inviare la cartella di root di un progetto via FTP per il primo trasferimento va bene, ma quando hai bisogno di aggiornare la tua applicazione con un update dove solo alcuni file sono cambiati, tale metodo non è ideale. Dovresti caricare di nuovo tutto il progetto, con spreco di tempo e banda, oppure cercare nel progetto solo i file modificati e trasferirli. Ciò è un lavoro noioso e incline ad errori. Inoltre, il sito in produzione potrebbe risultare, durante il trasferimento, non disponibile o con bug.

La soluzione supportata da symfony è una sincronizzazione via rsync tramite un layer SSH. Rsync (http://samba.anu.edu.au/rsync/) è una utility a linea di comando che fornisce un trasferimento incrementale veloce, ed è open source. Tramite il trasferimento incrementale, solo i file modificati verranno inviati; quelli che non sono cambiati non saranno spediti al server in produzione. Se un file è stato cambiato parzialmente, solo tale parte verrà inviata. Il vantaggio principale di questa tecnica è quindi che solo piccole porzioni di dati verranno spediti, ed il procedimento risulterà molto veloce.

Symfony aggiunge SSH alla sincronizzazione rsync per la sicurezza del trasferimento. Sempre più hosting commerciali mettono a disposizione un tunnel SSH per avere upload sicuri sui propri server, e questo è una buona pratica per evitare falle di sicurezza.

Il client SSH chiamato da symfony utilizza le impostazioni di connessione specificate nel file config/properties.ini. Il Listato 16-19 mostra un esempio di impostazioni di connessione per un server di produzione. Scrivi le impostazioni del tuo server di produzione in tale file prima di qualsiasi sincronizzazione.

Listato 16-19 - Esempio di impostazioni di connessione per una sincronizzazione con un server, in myproject/config/properties.ini

[symfony]
  name=myproject

[production]
  host=myapp.example.com
  port=22
  user=myuser
  dir=/home/myaccount/myproject/

NOTE Non 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 usata in produzione, come riportato nei file di configurazione di un'applicazione).

Eseguire un rsync su SSH richiede diversi comandi, e la sincronizzazione può ricorrere molte volte nella vita di un'applicazione. Fortunatamente, symfony automatizza tale processo con un semplice comando:

> php symfony project:deploy production

Questo comando esegue rsync in dry mode; ciò significa che mostra quali file dovrebbero essere sincronizzati senza farlo veramente. Se vuoi effettivamente eseguire la sincronizzazione, devi esplicitarlo aggiungendo go.

> php symfony project:deploy production --go

Non dimenticare di pulire la cache del server di produzione dopo una sincronizzazione.

TIP Alcune volte compaiono dei bug in produzione che non sono invece presenti in sviluppo. Nel 90% dei casi, ciò è dovuto a differenze nelle versioni (di PHP, web server o database) o nelle configurazioni. Per evitare spiacevoli sorprese, dovresti definire la configurazione di PHP del file php.yml della tua applicazione, in modo che venga controllato che l'ambiente di sviluppo applichi le stesse impostazioni. Consulta il Capitolo 19 per maggiori informazioni in merito.

SIDEBAR La tua applicazione è completata? Prima di spedire la tua applicazione in produzione, dovresti accertarti che sia pronta per l'utilizzo pubblico. Controlla che le seguenti cose siano effettivamente state fatte prima di fare il deploy:

Le pagine di errore dovrebbero essere personalizzate con il look and feel della tua applicazione. Consulta il Capitolo 19 per capire come personalizzare gli errori 500, 404 e le pagine di sicurezza, e la sezione "Gestire un'applicazione in produzione" più avanti in questo capitolo per vedere come personalizzare le pagine mostrate quando il tuo sito non è disponibile.

Il modulo default dovrebbe essere rimosso dall'array enabled_modules in settings.yml, in modo che non appaiano pagine symfony per errore.

Il meccanismo di gestione delle sessioni usa un cookie lato client, e tale cookie si chiama symfony per default. Prima di fare il deploy, dovresti probabilmente rinominarlo per evitare di mostrare che la tua applicazione si basa su symfony. Controlla il Capitolo 6 per vedere come personalizzare il nome del cookie nel file factories.yml.

Il file robots.txt, situato nella cartella web/ del progetto, è vuoto per default. Dovresti personalizzarlo per informare gli spider web ed altri robot di quale parte del sito dovrebbero navigare e quale dovrebbero evitare. La maggior parte delle volte, tale file viene usato per evitare di indicizzare certi URL space, ad esempio pagine che richiedono molte risorse, pagine che non ne abbiano bisogno (come archivi di bug), o URL space infiniti dove i robot potrebbero rimanere intrappolati.

I browser moderni richiedono un file favicon.ico quando un utente naviga per la prima volta il tuo sito, per rappresentare la tua applicazione con un'icona nella barra degli indirizzi e nei bookmark. Fornire tale file non solo completerà il look and feel della tua applicazione, ma eviterà anche la presenza di errori 404 nei file di log del server.

Ignorare file irrilevanti

Se sincronizzi il tuo progetto symfony con un host di produzione, alcuni file e cartelle dovrebbero non essere inviate:

  • Tutte le cartelle di versioning (.svn/, CVS/ e così via) ed il loro contenuto sono necessari solo per lo sviluppo e l'integrazione.
  • Il front controller e l'ambiente di sviluppo non devono essere disponibili all'utente finale. Gli strumenti di debugging e di logging disponibili durante l'utilizzo dell'applicazione tramite tale controller rallentano ne rallentano l'esecuzione e forniscono informazioni sulle variabili di core delle tue azioni. E' qualcosa da tenere lontano dal pubblico.
  • Le cartelle cache/ e log/ di un progetto non devono venire cancellate sul server di produzione ad ogni sincronizzazione. Anche queste cartelle devono essere ignorate. Se hai una cartella stats/, probabilmente dovrebbe essere trattata nello stesso modo.
  • I file caricati dagli utenti non devono essere trasferiti. Una delle buone pratiche dei progetti symfony è quella di tenere i file caricati nella cartella web/uploads/. Questo ti permette di escludere tali file dalla sincronizzazione puntando ad una sola cartella.

Per escludere file dalla sincronizzazione rsync, apri e modifica il file rsync_exclude.txt nella cartella myproject/config/. Ogni linea può contenere un nome di file, di directory od un pattern. La struttura di file di symfony è organizzata logicamente, e disegnata per minimizzare il numero di file e cartelle da escludere manualmente da una sincronizzazione. Vedi il Listato 16-20 per un esempio.

Listato 16-20 - Esempio di impostazione di esclusione rsync, in myproject/config/rsync_exclude.txt

.svn
/cache/*
/log/*
/stats/*
/web/uploads/*
/web/frontend_dev.php

NOTE Le cartelle cache/ e log/ non devono essere sincronizzate con il server di sviluppo, ma devono almeno esistere sul server di produzione. Creale a mano se non sono già presenti nella cartella myproject/.

Gestire un'applicazione in produzione

Il comando usato più spesso in server di produzione è clear-cache. Lo devi eseguire ogni qualvolta aggiorni symfony od il tuo progetto (ad esempio tramite il comando sync), ed ogni volta che cambi la configurazione di produzione.

> symfony cache:clear

TIP Se l'interfaccia a linea di comando non fosse disponibile sul tuo server di produzione, puoi ancora pulire la cache manualmente cancellando il contenuto della cartella cache/.

Puoi disabilitare temporaneamente la tua applicazione, ad esempio quando devi aggiornare una libreria od una grande quantità di dati.

> php symfony project:disable APPLICATION_NAME ENVIRONMENT_NAME

Per default, un'applicazione disabilitata mostra la pagina $sf_symfony_lib_dir/exception/data/unavailable.php, ma puoi creare la tua pagina personalizzata unavailable.php nella cartella config/ del progetto, symfony userà quella.

Il task project:enable riabilita l'applicazione e pulisce la cache.

> symfony project:enable APPLICATION_NAME ENVIRONMENT_NAME

SIDEBAR Visualizzare una pagina non disponibile mentre si pulisce la cache Se imposti il parametro check_lock a on nel file settings.yml, symfony bloccherà l'applicazione durante la pulizia della cache, e tutte le richieste che arriveranno prima che la pulizia sia stata completata, verranno ridirezionate su una pagina che dice che l'applicazione è temporaneamente non disponibile. Se la cache è molto ampia, il ritardo necessario potrebbe essere maggiore di qualche millisecondo, e se il traffico sul tuo sito è alto questa è un'impostazione consigliata. Questa pagina che mostra il messaggio di non disponibile è la stessa di quella visualizzata quando chiami il task disable. Il parametro check_lock è disattivato per default perché ha un impatto leggermente negativo sulle prestazioni.

Il task project:clear-controllers pulisce la cartella web/ da tutti i controller che non siano quelli che girano in produzione. Se non includi il controller di sviluppo nel file rsync_exclude.txt, questo comando evita che una backdoor riveli gli interni della tua applicazione.

> symfony project:clear-controllers

I permessi dei file e cartelle di progetto possono essere sbagliati se usi un checkout da un repository SVN. Il task project:permissions rimette a posto i permessi, ad esempio per cambiare i permessi di cache/ e log/ in 0777 (tali cartelle hanno bisogno di essere scrivibili dal framework per funzionare correttamente).

> symfony project:permissions

Sommario

Combinando i log di PHP e di symfony, puoi facilmente monitorare e debuggare le tue applicazioni. Durante lo sviluppo, la modalità di debug, le eccezioni, e la web debug toolbar ti aiutano a trovare i problemi. Puoi anche inserire messaggi personalizzati nei file di log o nella toolbar per facilitare il debug.

L'interfaccia a linea di comando fornisce un grande numero di strumenti che facilitano la gestione delle tue applicazioni, durante le fasi di sviluppo e di produzione. Soprattutto, il popolamento dei dati, il congelamento e la sincronizzazione fanno risparmiare molto tempo.