Development

Documentation/it_IT/book/1.0/17-Extending-Symfony

You must first sign up to be able to contribute.

Version 8 (modified by Simone.Carletti, 10 years ago)
Fixed typo

Capitolo 17 - Estendere symfony

Qualche volta ti potrebbe capitare di dover modificare il comportamento di symfony. Sia che tu debba modificare il comportamento di una certa classe, o aggiungere nuove funzionalità, il momento arriverà: nessun framework può includere tutte le funzionalità di cui i clienti hanno bisogno. Per tale motivo symfony fornisce dei meccanismi, chiamati mixin, per estendere le classi. Potresti anche sostituire le classi di core di symfony, utilizzando le impostazioni di default. Una volta creata la tua estensione, puoi facilmente pacchettizzarla come plugin, in modo che possa essere usata da altre applicazioni o da altri utenti symfony.

Mixin

Fra tutte le limitazioni di PHP, una delle più fastidiose è che non è permetto ad una classe di estendere più di una classe alla volta. Un'altra limitazione è quella di non poter aggiungere metodi ad una classe esistente o fare l'override di quelli esistenti. Per evitare tali inconvenienti e per rendere il framework veramente estendibile, symfony introduce una classe chiamata sfMixer. Essa non è in alcun modo correlata a dispositivi per cucinare, piuttosto al concetto di mixin che si trova nella programmazione ad oggetti. Un mixin è un gruppo di metodi o funzioni che possono essere mescolati in una classe per estenderla.

Capire l'ereditarietà multipla

L'ereditarietà multipla è la capacità di una classe di estendere più di una classe, ed ereditarne proprietà e metodi. Facciamo un esempio. Immagina le classi Story e Book, ognuna delle quali con i propri metodi e proprietà, come nel Listato 17-1.

Listato 17-1 - Due classi di esempio

[php]
class Story
{
  protected $title = '';
  protected $topic = '';
  protected $characters = array();

  public function __construct($title = '', $topic = '', $characters = array())
  {
    $this->title = $title;
    $this->topic = $topic;
    $this->characters = $characters;
  }

  public function getSummary()
  {
    return $this->title.', a story about '.$this->topic;
  }
}

class Book
{
  protected $isbn = 0;

  function setISBN($isbn = 0)
  {
    $this->isbn = $isbn;
  }

  public function getISBN()
  {
    return $this->isbn;
  }
}

Una classe ShortStory estende Story, ComputerBook estende Book, e, logicamente, una Novel dovrebbe estendere sia Story che Book ed usufruire dei vantaggi di entrambe. Sfortunatamente, ciò non è possibile in PHP. Non puoi scrivere la dichiarazione di Novel come nel Listato 17-2.

Listato 17-2 - L'ereditarietà multipla non è possibile in PHP

[php]
class Novel extends Story, Book
{
}

$myNovel = new Novel();
$myNovel->getISBN();

Una possibilità sarebbe che Novel implementasse due interfacce invece di estendere due classi, ma questo inficierebbe la possibilità di avere i metodi implementati nella classe genitore.

Mescolare le classi

La classe sfMixer affronta il problema in maniera diversa, prendendo la classe esistente ed estendendola a posteriori, supposto che tale classi abbia i giusti hook. Il processo è diviso in due passi:

  • Dichiarare la classe come estendibile
  • Registrare estensioni (o mixin) dopo la dichiarazione della classe

Il Listato 17-3 mostra come sarebbe implementata la classe Novel tramite sfMixer.

Listato 17-3 - L'ereditarietà multipla è possibile tramite sfMixer

[php]
class Novel extends Story
{
  public function __call($method, $arguments)
  {
    return sfMixer::callMixins();
  }
}

sfMixer::register('Novel', array('Book', 'getISBN'));
$myNovel = new Novel();
$myNovel->getISBN();

Una delle classi (Story) viene scelta come genitore principale, in quanto PHP permette l'ereditarietà da una sola classe. La classeNovel viene dichiarata come estendibile tramite il codice del metodo __call(). Il metodo dell'altra classe (Book) viene aggiunto a posteriori alla classe Novel tramite una chiamata asfMixer::register(). La prossima sezione spiega il procedimento in dettaglio.

Quando viene chiamato il metodo getISBN() della classe Novel, tutto accade come se la classe fosse stata definita come nel Listato 17-2, eccetto per il fatto che il tutto viene simulato tramite i metodi __call() e sfMixer. Il metodo getISBN() viene mescolato nella classe Novel.

SIDEBAR Quando usare mixin

Il meccanismo di mixin di symfony è utile in diversi casi. Simulare l'ereditarietà multipla, come nel caso precedente, è solo una delle possibilità.

Puoi usare i mixin per alterare un metodo dopo la sua dichiarazione. Ad esempio, nella costruzione di una libreria grafica, probabilmente implementeresti un oggetto Line che rappresenta una linea. Esso avrà quattro attributi (le coordinate delle estremità) ed un metodo draw() per disegnarla. Una ColoredLine dovrebbe avere gli stessi metodi e proprietà, ma con un attributo aggiuntivo, color, per specificarne il colore. Inoltre, il metodo draw() sarebbe un pò diverso da quello della semplice Line, per poter usare il colore. Potresti pacchettizzare le funzionalità di un elemento grafico per gestire il colore in una classe ColoredElement. Questo ti permetterebbe di riutilizzare il metodo del colore per altri elementi grafici (Dot, Polygon, e così via). In questo caso, l'implementazione ideale della classe ColoredLine sarebbe un'estensione della classe Line, con i metodi di ColoredElement mescolati al suo interno. Il metodo finale draw() sarebbe un mix tra l'originale di Line e quello di ColoredElement.

I mixin possono anche essere visti come un modo per aggiungere nuovi metodi ad una classe esistente. Ad esempio, la classe action di symfony, chiamata sfActions, è definita nel framework. Uno dei limiti di PHP è che non puoi cambiare la definizione di sfActions dopo la sua dichiarazione. Potresti voler aggiungere un metodo personalizzato a sfActions in una delle tue applicazioni; ad esempio, per redirigere una richiesta ad uno speciale web service. A questo scopo, PHP da solo fallirebbe, ma il meccanismo di mixin fornisce la soluzione perfetta.

Dichiarare una classe come estendibile

Per dichiarare una classe come estendibile, devi inserire uno dei diversi "hook" nel codice, in modo che in seguito la classe sfMixer lo possa identificare. Questi hook vengono chiamati tramite il metodo sfMixer::callMixins(). Diverse classi symfony contengono già tali hook, come sfRequest, sfResponse, sfController, sfUser, sfAction, ed altre.

L'hook può essere situato in parti diverse della classe, secondo il grado di estendibilità desiderato:

  • Per poter aggiungere nuovi metodi alla classe, devi inserire l'hook nel metodo __call() e restituire il suo risultato, come dimostrato nel Listato 17-4.

Listato 17-4 - Fornire ad una classe la capacità di aggiungere nuovi metodi

[php]
class SomeClass
{
  public function __call($method, $arguments)
  {
    return sfMixer::callMixins();
  }
}
  • Per poter alterare il funzionamento di un metodo esistente, devi inserire l'hook nel metodo, come mostrato nel Listato 17-5. Il codice aggiunto dalla classe mixin sarà eseguito quando l'hook è posizionato.

Listato 17-5 - Fornire ad un metodo la possibilità di essere modificato

[php]
class SomeOtherClass
{
  public function doThings()
  {
    echo "I'm working...";
    sfMixer::callMixins();
  }
}

Potresti voler inserire più di un hook in un metodo. In questo caso, devi dare un nome agli hook, in modo da capire in un secondo tempo quale hook deve essere utilizzatot, come mostrato nel Listato 17-6. Un hook con un nome è una chiamata a callMixins() con il nome come parametro. Tale nome sarà usato in seguito, nella registrazione del mixin, per specificare il punto nel metodo in cui il mixin deve essere eseguito.

Listato 17-6 - Un metodo può contenere più di un hook, nel qual caso occorre dargli un nome

[php]
class AgainAnotherClass
{
  public function doMoreThings()
  {
    echo "I'm ready.";
    sfMixer::callMixins('beginning');
    echo "I'm working...";
    sfMixer::callMixins('end');
    echo "I'm done.";
  }
}

Ovviamente, puoi combinare queste tecniche per creare classi con la capacità di essere assegnate come nuove ed estendibili metodi, come mostrato dal Listato 17-7.

Listato 17-7 - Una classe può essere estesa in diversi modi

[php]
class BicycleRider
{
  protected $name = 'John';

  public function getName()
  {
    return $this->name;
  }

  public function sprint($distance)
  {
    echo $this->name." sprints ".$distance." meters\n";
    sfMixer::callMixins(); // Il metodo sprint() è estendibile
  }

  public function climb()
  {
    echo $this->name.' climbs';
    sfMixer::callMixins('slope'); // Il metodo climb() è estendibile qui
    echo $this->name.' gets to the top';
    sfMixer::callMixins('top'); // ed anche qui
  }

  public function __call($method, $arguments)
  {
    return sfMixer::callMixins(); // La classe BicyleRider è estendibile
  }
}

CAUTION Solo le classi dichiarate come estendibili possono essere estese tramite sfMixer. Ciò significa che non puoi usare questo meccanismo per estendere una classe che non è "iscritta" a questo servizio.

Registrare estensioni

Per registrare un'estensione ad un hook esistente, usa il metodo sfMixer::register(). Il suo primo argomento è l'elemento da estendere, il secondo una chiamata PHP che rappresenta il mixin.

Il formato del primo argomento dipende da cosa stai cercando di estendere:

  • Se estendi una classe, usa il suo nome.
  • Se estendi un metodo con hook anonimo, usa il pattern class:method.
  • Se estendi un metodo con hook con un nome, usa il pattern class:method:hook.

Il Listato 17-8 illustra questo principio estendendo la classe definita nel Listato 17-7. L'oggetto esteso viene passato automaticamente come primo parametro ai metodi mixin (fatta eccezione, ovviamente, se il metodo è statico). Il metodo mixin ha accesso anche ai parametri della chiamata al metodo originale.

Listato 17-8 - Registrare estensioni

[php]
class Steroids
{
  protected $brand = 'foobar';

  public function partyAllNight($bicycleRider)
  {
    echo $bicycleRider->getName()." spends the night dancing.\n";
    echo "Thanks ".$brand."!\n";
  }

  public function breakRecord($bicycleRider, $distance)
  {
    echo "Nobody ever made ".$distance." meters that fast before!\n";
  }

  static function pass()
  {
    echo " and passes half the peloton.\n";
  }
}

sfMixer::register('BicycleRider', array('Steroids', 'partyAllNight'));
sfMixer::register('BicycleRider:sprint', array('Steroids', 'breakRecord'));
sfMixer::register('BicycleRider:climb:slope', array('Steroids', 'pass'));
sfMixer::register('BicycleRider:climb:top', array('Steroids', 'pass'));

$superRider = new BicycleRider();
$superRider->climb();
=> John climbs and passes half the peloton
=> John gets to the top and passes half the peloton
$superRider->sprint(2000);
=> John sprints 2000 meters
=> Nobody ever made 2000 meters that fast before!
$superRider->partyAllNight();
=> John spends the night dancing.
=> Thanks foobar!

Il meccanismo di estensione non si occupa solo di aggiungere metodi. Il metodo partyAllNight() usa un attributo della classe Steroids. Ciò significa che quando estendi la classe BicycleRider con un metodo della classe Steroids, effettivamente crei una nuova istanza di Steroids all'interno dell'oggetto BicycleRider.

CAUTION Non puoi aggiungere due metodi con lo stesso nome all'interno di una classe esistente. Queto perchè la chiamata callMixins() nel metodo __call() usa il nome del metodo mixin come chiave. Inoltre, non puoi aggiungere un metodo ad una classe che possiede già un metodo con lo stesso nome, in quanto il meccanismo di mixin si basa sul metodo magico __call(), ed in questo caso particolare non sarebbe mai chiamato.

Il secondo argomento di register() è una chiamata PHP, per cui potrebbe essere un array class::method, oppure object->method od anche un nome di funzione. Alcuni esempi nel Listato 17-9.

Listato 17-9 - Qualsiasi oggetto PHP valido può essere registrato come Mixer Extension

[php]
// Metodo di una classe
sfMixer::register('BicycleRider', array('Steroids', 'partyAllNight'));

// Oggetto di un metodo
$mySteroids = new Steroids();
sfMixer::register('BicycleRider', array($mySteroids, 'partyAllNight'));

// Funzione
sfMixer::register('BicycleRider', 'die');

Il meccanismo di estensione è dinamico, ovvero anche se hai già istanziato un oggetto puoi avvantaggiarti di estensioni ulteriori nella sua classe. Un esempio nel Listato 17-10.

Listato 17-10 - Il meccanismo di estensione è dinamico e può avvenire anche dopo l'istanziazione

[php]
$simpleRider = new BicycleRider();
$simpleRider->sprint(500);
=> John sprints 500 meters
sfMixer::register('BicycleRider:sprint', array('Steroids', 'breakRecord'));
$simpleRider->sprint(500);
=> John sprints 500 meters
=> Nobody ever made 500 meters that fast before!

Estendere con maggiore precisione

L'istruzione sfMixer::callMixins() è in effetti una scorciatoia ad un qualcosa di un pò più elaborato. Esso cicla automaticamente sulla lista dei mixin registrati e li chiama ad uno ad uno, passando l'oggetto ed i parametri correnti. In breve, una chiamata sfMixer::callMixins() si comporta più o meno come nel Listato 17-11.

Listato 17-11 - callMixin() cicla sui mixin registrati e li esegue

[php]
foreach (sfMixer::getCallables($class.':'.$method.':'.$hookName) as $callable)
{
  call_user_func_array($callable, $parameters);
}

Se vuoi passare altri parametri o fare qualcosa di speciale con il valore restituito, puoi scrivere esplicitamente il ciclo foreach invece di usare la scorciatoia. Controlla il Listato 17-12 per un esempio di mixin maggiormente integrato in una classe.

Listato 17-12 - Sostituire callMixin() con un loop personalizzato

[php]
class Income
{
  protected $amout = 0;

  public function calculateTaxes($rate = 0)
  {
    $taxes = $this->amount * $rate;
    foreach (sfMixer::getCallables('Income:calculateTaxes') as $callable)
    {
      $taxes += call_user_func($callable, $this->amount, $rate);
    }

    return $taxes;
  }
}

class FixedTax
{
  protected $minIncome = 10000;
  protected $taxAmount = 500;

  public function calculateTaxes($amount)
  {
    return ($amount > $this->minIncome) ? $this->taxAmount : 0;
  }
}

sfMixer::register('Income:calculateTaxes', array('FixedTax', 'calculateTaxes'));

SIDEBAR Comportamenti di Propel

I comportamenti di Propel, discussi precedentemente nel Capitolo 8, sono un tipo speciale di mixin: estendono l'oggetto generato da Propel. Vediamo un esempio.

Gli oggetti Propel corrispondono alle tabelle del database che possiedono un metodo delete(), che cancella i record relativi dal database. Ma per una classe Invoice, per la quale non puoi eliminare un record, potresti voler alterare il metodo delete() in modo da mantenere il record nel database e cambiare il valore di un attributo is_deleted a true. I metodi di recupero di oggetti usuali (doSelect(), retrieveByPk()) considererebbero solo i record per i quali is_deleted fosse false. Avresti inoltre bisogno di un altro metodo, chiamato ad esempio forceDelete(), che cancella fisicamente il record. In effetti, tutti questi metodi possono essere pacchettizati in una nuova classe chiamata ParanoidBehavior. La classe finale Invoice estente la BaseInvoice di Propel ed ha mescolati al suo interno i metodi di ParanoidBehavior.

Per cui un behavior è un mixin di un oggetto Propel. Effettivamente, il termine "behavior" in symfony comprende una cosa in più: il fatto che il mixin è pacchettizzato come plugin. La classe ParanoidBehavior appena menzionata corrisponde ad un vero plugin di symfony chiamato sfPropelParanoidBehaviorPlugin. Controlla il wiki di symfony (http://www.symfony-project.com/trac/wiki/sfPropelParanoidBehaviorPlugin) per i dettagli sull'installazione e l'uso di tale plugin.

Un'ultima cosa sui behavior: per poterli supportare, l'oggetto generato da Propel deve contenere un buon numero di hook. Essi potrebbero rallentare un pò l'esecuzione e penalizzare così le prestazioni se non usi i behavior. Ecco perchè gli hook non sono abilitati di default. Per poterli utilizzare ed abilitare il supporto ai behavior, devi prima impostare la proprietà propel.builder.addBehaviors a true nel file propel.ini e fare il rebuild del modello.

Factories

Una factory è la definizione di una classe per certi task. Symfony si basa su factories per le sue funzionalità core come controller e sessioni. Ad esempio, quando il framework ha bisogno di creare una nuova richiesta, cerca nella definizione di factory il nome della classe da utilizzare a tale scopo. La definizione factory di default per le richieste è sfWebRequest, quindi symfony crea un oggetto di questa classe per gestire le richieste. Il grande vantaggio di usare una definizione factory è la facilità con cui si possono alterare le funzionalità di core del framework: cambia semplicemente la definizione di factory, e symfony userà la tua classe di richiesta invece della sua.

Le definizioni di factory sono memorizzate nel file di configurazione factories.yml. Il Listato 17-13 mostra il file di default. Ogni definizione è composta da un nome di una classe caricata automaticamente ed, opzionalmente, un insieme di parametri. Ad esempio, la memorizzazione di sessioni (sotto la chiave storage:) usa un parametro session_name per nominare il cookie creato sul computer client e permettere sessioni persistenti.

Listato 17-13 - File di impostazioni factories di default, in myapp/config/factories.yml

cli:
  controller:
    class: sfConsoleController
  request:
    class: sfConsoleRequest

test:
  storage:
    class: sfSessionTestStorage

#all:
#  controller:
#    class: sfFrontWebController
#
#  request:
#    class: sfWebRequest
#
#  response:
#    class: sfWebResponse
#
#  user:
#    class: myUser
#
#  storage:
#    class: sfSessionStorage
#    param:
#      session_name: symfony
#
#  view_cache:
#    class: sfFileCache
#    param:
#      automaticCleaningFactor: 0
#      cacheDir:                %SF_TEMPLATE_CACHE_DIR%
#
#  i18n:
#    class: sfI18N
#
#  routing:
#    class: sfPatternRouting

Il modo migliore di cambiare una factory è creare una nuova classe che eredita da quella di default ed aggiungere i nuovi metodi ad essa. Ad esempio, la sessione utente factory è impostata sulla classe myUser (situata in myapp/lib/), ed eredita da sfUser. Usa lo stesso meccanismo per avvantaggiarti dei factories esistenti. Il Listato 17-14 mostra un esempio di nuova factory per l'oggetto richiesta.

Listato 17-14 - Override di factories

[php]
// Create a myRequest.class.php in an autoloaded directory,
// For instance in myapp/lib/
<?php

class myRequest extends sfRequest
{
  // Your code here
}

// Declare this class as the request factory in factories.yml
all:
  request:
    class: myRequest

Collegamenti ad altri componenti del framework

Se hai bisogno di funzionalità fornite da una classe di terze parti, e se non vuoi copiare tale classe in una delle cartelle lib/ di symfony, probabilmente la installerai fuori dai soliti percorsi dove symfony cerca i file. In tal caso, l'utilizzo di questa classe implicherà una require a mano nel tuo codice, a meno di avvantaggiarti della capacità di autoloading di symfony.

Symfony non (ancora) fornisce tool per qualsiasi cosa. Se hai bisogno di un generatore di PDF, un'API a Google Maps, od una implementazione PHP del motore di ricerca Lucene, probabilmente avrai bisogno di qualche libreria dello Zend Framework. Se vuoi manipolare immagini direttamente in PHP, connetterti ad un account POP3 per leggere e-mail, o disegnare un'interfaccia per una console potresti scegliere gli eZcomponents. Fortunatamente, con le giuste impostazioni, i componenti di entrambe le librerie funzioneranno immediatamente in symfony.

La prima cosa che hai bisogno di dichiarare (a meno di aver installato la libreria tramite PEAR) è il percorso alla cartella di root delle librerie. Questo deve essere fatto in settings.yml:

.settings:
  zend_lib_dir:   /usr/local/zend/library/
  ez_lib_dir:     /usr/local/ezcomponents/

Quindi, estendi la routine di autoload specificando quale libreria considerare quando l'autoloading con symfony fallisce:

.settings:
  autoloading_functions:
    - [sfZendFrameworkBridge, autoload]
    - [sfEzComponentsBridge,  autoload]

Nota che questa impostazione è distinta dalle regole definite in autoload.yml (vedi il Capitolo 19 per maggiori informazioni). L'impostazione autoloading_functions specifica le classi di bridge, e autoload.yml specifica i percorsi e le regole per la ricerca. A seguire la descrizione di quello che succede quando crei un nuovo oggetto di una classe non caricata:

  1. La funzione di autoload di symfony (sfCore::splAutoload()) cerca prima di tutto la classe nei percorsi specificati in autoload.yml.
  2. Se non viene trovata, i metodi di callback dichiarati in sf_autoloading_functions saranno chiamati uno dopo l'altro, fino a che uno restituisca true:
  3. sfZendFrameworkBridge::autoload()
  4. sfEzComponentsBridge::autoload()
  5. Se entrambi restituiscono false, se usi PHP 5.0.X, symfony genera un'eccezione dicendo che la classe non esiste. A cominciare da PHP 5.1, l'errore verrà generato da PHP stesso.

Ciò significa gli altri componenti del framework traggono beneficio dal meccanismo di autoload, e puoi usarli ancora più facilmente che non all'interno del loro stesso ambiente. Ad esempio, se vuoi usare il componente Zend_Search con lo Zend Framework per implementare l'equivalente del motore di ricerca Lucene in PHP, devi scrivere:

[php]
require_once 'Zend/Search/Lucene.php';
$doc = new Zend_Search_Lucene_Document();
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
...

Con symfony e lo Zend Framework bridge, è ancora più semplice. Devi solo scrivere:

[php]
$doc = new Zend_Search_Lucene_Document(); // The class is autoloaded
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
...

I bridge disponibili sono memorizzati nella cartella $sf_symfony_lib_dir/addon/bridge/.

Plug-Ins

Probabilmente ti capiterà di dover riutilizzare parti di codice scritte per un'applicazione symfony. Non ci sono problemi se puoi pacchettizzare tale codice in una classe singola: metti la classe in una delle cartelle lib/ di un'altra applicazione e la funzionalità di autoloader si occuperà del resto. Ma se il codice è sparso su più file, ad esempio come un nuovo tema per il generatore di amministrazione, oppure una combinazione di file ed helper JavaScript con i tuoi effetti visuali preferiti, la mera copia dei file non è la soluzione migliore.

I plugins offrono un modo di pacchettizzare il codice diviso in diversi file e riutilizzarlo in altri progetti. In un plugin puoi pacchettizzare classi, filtri, mixin, helper, configurazioni, task, moduli, schemi ed estensioni dei modelli, fixtures, ecc... I plugin sono facili da installare, aggiornare ed eliminare. Possono essere distribuiti come archivi .tgz, oppure pacchetti PEAR od anche come semplice checkout da un repository. Come pacchetti PEAR hanno il vantaggio della gestione delle dipendenze, sono più facili da mantenere e da aggiornare. Il meccanismo di caricamento di symfony tiene conto dei plugin, e le funzionalità offerte da un plugin sono disponibili nel progetto come se il codice del plugin fosse parte del framework.

Quindi, essenzialmente, un plugin è una estensione pacchettizzata per un progetto symfony. Con i plugin, non solo puoi riutilizzare il codice nelle tue applicazioni, puoi anche usare sviluppi di altri ed aggiungerre estensioni di terze parti al core di symfony.

Trovare i plugin di symfony

Il sito web di symfony contiene una pagina dedicata ai plugin. Si trova nel wiki ed è accessibile alla seguente URL:

http://www.symfony-project.com/trac/wiki/SymfonyPlugins

Ogni plugin elencato ha la propria pagina, con istruzioni dettagliate per l'installazione e documentazione.

Alcuni di tali plugin sono contributi della comunità, ed alcuni invece degli sviluppatori del core di symfony. A proposito di quest'ultimi, troverai:

  • sfFeedPlugin: Automatizza la gestione di feed RSS e Atom
  • sfThumbnailPlugin: Crea thumbnail: ad esempio, per il caricamento di immagini
  • sfMediaLibraryPlugin: Permette la gestione ed il caricamento di media, inclusa un'estensione per rich text editor
  • sfShoppingCartPlugin: Permette la gestione di un carrello
  • sfPagerNavigationPlugin: Fornisce i controlli di paginazione si classici che Ajax, basati sull'oggetto sfPager
  • sfGuardPlugin: Fornisce autenticazione, permessi e le altre funzionalità di gestione degli utenti
  • sfPrototypePlugin: Fornisce i file JavaScript prototype e script.aculo.us come librerie standalone
  • sfSuperCachePlugin: Scrive pagine nella cartella di cache in modo da aumentare le prestazioni del web server
  • sfOptimizerPlugin: Ottimizza il codice della tua applicazione in modo che sia più performante in produzione (vedi il prossimo capitolo per i dettagli)
  • sfErrorLoggerPlugin: Logga tutti gli errori 404 e 500 in un database e fornisce un modulo di amministrazione per gestirli
  • sfSslRequirementPlugin: Fornisce il supporto per la crittazione SSL nelle azioni

IL wiki propone inoltre plugin pensati per estendere i tuoi oggetti Propel, chiamati anche behavior. Tra questi, puoi trovare:

  • sfPropelParanoidBehaviorPlugin: Disabilita la cancellazione delli oggetti e la sostituisce con l'aggiornamento di una colonna deleted_at
  • sfPropelOptimisticLockBehaviorPlugin: Implementa l'optimistic locking per gli oggetti Propel

Dovresti controllare il wiki regolarmente, in quanto nuovi plugin vengono aggiunti continuamente, ed essi risultano molto utili nei diversi aspetti della programmazione web.

A parte il wiki, le altre modalità di distribuzione dei plugin sono il download, hosting su un canale PEAR o metterli su un cvs pubblico.

Installare un Plug-In

L'installazione di un plugin varia a seconda di come è stato pacchettizzato. Controlla sempre il file README e/o le istruzioni di installazione della pagina di download del plugin. Inoltre, pulisci sempre la cache di symfony dopo che hai installato un plugin.

I plugin sono applicazioni installate su un progetto. Tutti i metodi descritti nella prossima sezione alla fine posizionano i file di un plugin in una cartella myproject/plugins/pluginName/.

Plug-Ins PEAR

I plugin elencati nel wiki sono inclusi come pacchetti PEAR ad una pagina del wiki. Per installarli, usa il task plugin-install con la URL completa, come mostrato nel Listato 17-15.

Listato 17-15 - Installare un plugin dal wiki di symfony

> cd myproject
> php symfony plugin-install http://plugins.symfony-project.com/pluginName
> php symfony cc

In alternativa, puoi scaricare il plugin e installarlo dal disco. In questo caso, sostituisci il nome del canale con il percorso assoluto del file, come mostrato nel Listato 17-16.

Listato 17-16 - Installare un plugin da un pacchetto PEAR scaricato

> cd myproject
> php symfony plugin-install /home/path/to/downloads/pluginName.tgz
> php symfony cc

Alcuni plugin sono in host su canali PEAR. Installali con il task plugin-install, e non dimenticare di mettere il nome del canale, come mostrato nel Listato 17-17.

Listato 17-17 - Installare un plugin da un canale PEAR

> cd myproject
> php symfony plugin-install channelName/pluginName
> php symfony cc

Questi tre tipi di installazione usano i pacchetti PEAR, per cui il termine "plugin PEAR" sarà usato indiscriminatamente per parlare di plugin installati dal wiki di symfony, da un canale PEAR od un pacchetto PEAR scaricato.

Plug-Ins archivi

Alcuni plugin sono semplici archivi di file. Per installarli, scompattali nella tua cartella plugins/ del progetto. Se il plugin contiene una sottocartella web/, fanne una copia od un link simbolico nella cartella web/ del progetto, come mostrato nel Listato 17-18. Infine non dimenticare di pulire la cache.

Listato 17-18 - Installare un plugin da un archivio

> cd plugins
> tar -zxpf myPlugin.tgz
> cd ..
> ln -sf plugins/myPlugin/web web/myPlugin
> php symfony cc

Installare i Plug-Ins da un Version Control Repository

Qualche volta i plugin hanno il proprio repositoryr di codice sorgente per il controllo di versione. Li puoi installare con un semplice checkout nella cartella plugins/, ma questo diventa problematico se il progetto stesso è sotto cvs.

In alternativa, puoi dichiarare il plugin come dipendenza esterna in modo che ogni aggiornamento del codice del tuo progetto aggiorni anche il codice del plugin. Ad esempio, Subversion memorizza le dipendenze esterne nella proprietà svn:externals. Quindi puoi aggiungere un plugin modificando tale proprietà ed aggiornando il tuo codice, come mostrato dal Listato 17-19.

Listato 17-19 - Installare un plugin da un repository di controllo versione

> cd myproject
> svn propedit svn:externals plugins
  pluginName   http://svn.example.com/pluginName/trunk
> svn up
> php symfony cc

NOTE Se il plugin contiene una cartella web/ directory, devi creare un link simbolico come per i plugin archivi.

Attivare un modulo plugin

Alcuni plugin contengono moduli interi. L'unica differenza tra un modulo plugin ed uno classico è che il primo non appare nella cartella myproject/apps/myapp/modules/ (perchè sia facilmente aggiornabile). Inoltre deve essere attivato nel file settings.yml, come mostrato nel Listato 17-20.

Listato 17-20 - Attivare un modulo plugin, in myapp/config/settings.yml

all:
  .settings:
    enabled_modules:  [default, sfMyPluginModule]

Ciò viene fatto per evitare una situazione in cui un modulo plugin per errore sia reso disponibile ad un'applicazione che non lo richieda, il che potrebbe aprire una falla di sicurezza. Pensa ai plugin che forniscono moduli di frontend e backend. Dovrai abilitare i moduli frontend solo nell'applicazione frontend, ed i moduli backend solo nell'applicazione backend. Ecco perchè i plugin non sono attivati per default.

TIP Il modulo di default è l'unico abilitato per default. Ecco perchè non è un vero modulo plugin, per cui risiede nel framework, in $sf_symfony_data_dir/modules/default/. Questo è il modulo che fornisce la pagina di congratulazioni, e quella di default per errore 404 e per i permessi. Se non vuoi usare le pagine di default di symfony, elimina questo modulo dall'impostazione enabled_modules.

Elencare i plugin installati

Se un'occhiata alla cartella plugins/ del tuo progetto può rivelare quali sono i plugin installati, il task plugin-list ti dice di più: la versione ed il canale di ognuno di essi (vedi il Listato 17-21).

Listato 17-21 - Elenco dei plugin installati

> cd myproject
> php symfony plugin-list

Installed plugins:
sfPrototypePlugin               1.0.0-stable # pear.symfony-project.com (symfony)
sfSuperCachePlugin              1.0.0-stable # pear.symfony-project.com (symfony)
sfThumbnail                     1.1.0-stable # pear.symfony-project.com (symfony)

Aggiornare e rimuovere i plugin

Per rimuovere un plugin PEAR, chiama il comando plugin-uninstall dalla cartella di root del progetto, come mostrato nel Listato 17-22. Devi anteporre al nome del plugin il proprio canale (usa il comando plugin-list per sapere quale).

Listato 17-22 - Rimuovere un plugin

> cd myproject
> php symfony plugin-uninstall pear.symfony-project.com/sfPrototypePlugin
> php symfony cc

TIP Certi canali hanno un alias. Ad esempio, il canale pear.symfony-project.com può essere visto come symfony, il che significa che puoi rimuovere sfPrototypePlugin come nel Listato 17-22 chiamando semplicemente php symfony plugin-uninstall symfony/sfPrototypePlugin.

Per rimuovere un plugin archivio o un plugin SVN, rimuovi i file dalle cartelle plugins/ e web/ e pulisci la cache.

per aggiornare un plugin, usa plugin-upgrade (per un plugin PEAR) o svn update (per un plugin da cvs). I plugin archivi non possono essere aggiornati facilmente.

Anatomia di un plugin

I plugin sono scritti in PHP. Se puoi capire com'è organizzata un'applicazione, puoi capire la struttura dei plugin.

Struttura dei file

Una cartella di plugin è organizzata più o meno come quella di un progetto. I file devono essere nelle giuste cartelle per poter essere caricati automaticamente da symfony quando necessario. Vedi il Listato 17-23.

Listato 17-23 - Struttura dei file di un plugin

pluginName/
  config/
    *schema.yml        // Data schema
    *schema.xml
    config.php         // Specific plug-in configuration
  data/
    generator/
      sfPropelAdmin
        */             // Administration generator themes
          templates/
          skeleton/
    fixtures/
      *.yml            // Fixtures files
    tasks/
      *.php            // Pake tasks
  lib/
    *.php              // Classes
    helper/
      *.php            // Helpers
    model/
      *.php            // Model classes
  modules/
    */                 // Modules
      actions/
        actions.class.php
      config/
        module.yml
        view.yml
        security.yml
      templates/
        *.php
      validate/
        *.yml
  web/
    *                  // Assets

Funzionalità dei plugin

I plugin possono fare molte cose. Il loro contenuto viene preso in considerazione automaticamente dalla tua applicazione a runtime quando esegui i comandi. Ma per funzionare correttamente, essi devono rispettare qualche convenzione:

  • Gli schemi di database sono riconosciuti al comando propel-. Quando chiami propel-build-model nel tuo progetto, insieme ad esso puoi rucistruire il modello e tutti i plugin. Nota che uno schema di plugin deve sempre un attributo di pacchetto nella forma plugins.pluginName. lib.model, come mostrato nel Listato 17-24.

Listato 17-24 - Esempio di dichiarazione di uno schema di plugin, in myPlugin/config/schema.yml

propel:
  _attributes:    { package: plugins.myPlugin.lib.model }
  my_plugin_foobar:
    _attributes:    { phpName: myPluginFoobar }
      id:
      name:           { type: varchar, size: 255, index: unique }
      ...
  • La configurazione di un plugin deve essere inclusa nello script di bootstrap (config.php). Questo file viene eseguito dopo la configurazione dell'applicazione e del progetto, quindi symfony in quel momento è già partito. Puoi usare questo file, ad esempio, per aggiungere cartelle al path include di PHP o per estendere classi esistenti con i mixin.
  • I file di fixture situati nella cartella data/fixtures/ sono processati dal comando propel-load-data.
  • I task sono immediatamente disponibili alla linea di comando di symfony appena il plugin è installato. È buona pratica anteporre al nome del task qualcosa di significativo: ad esempio il nome del plugin. Scrivi symfony per vedere la lista dei task disponibili inclusi quelli aggiunti dai plugin.
  • Le classi personalizzate sono caricate automaticamente come quelle situate nelle cartelle lib/ del progetto.
  • Gli helper sono trovati automaticamente alla chiamata `use_helper()nelle template. Devono essere situati nella sottocartellahelper/di una delle cartellelib/ del plugin.
  • I modelli in myplugin/lib/model/ specializzano il modello generato da Propel (in myplugin/lib/model/om/ e myplugin/lib/model/map/). Ovviamente, sono caricati automaticamente. Non puoi fare l'override del modello generato di un plugin nelle cartelle del tuo progetto.
  • I moduli forniscono nuove azioni accessibili dall'esterno, supposto che siano dichiarate nell'impostazione enabled_modules della tua applicazione.
  • I Web asset (immagini, script, stylesheet, ecc...) sono disponibili al server. Quando installi un plugin tramite la linea di comando, symfony crea un link simbolico alla cartella web/ del progetto se il sistema lo permette, o copia il contenuto del modulo nella cartella web/ del progetto. Se il plugin viene installato da un archivio o da un cvs devi copiarne la cartella web/ a mano (come dovrebbe essere menzionato nel file README allegato al plugin).

Setup manuale di plugin

Ci sono alcuni elementi che il task plugin-install non può gestire, e che richiedono pertanto un setup manuale:

  • Configurazioni personalizzate dell'applicazione possono essere usate nel plugin (ad esempio, usando sfConfig::get('app_myplugin_foo')), ma non puoi mettere i valori di default nel file app.yml situato nella cartella config/ del plugin. Per gestire i valori di default, usa il secondo parametro del metodo sfConfig::get(). Le impostazioni possono essere overridate a livello applicazione (controlla il Listato 17-25 per un esempio).
  • Regole di routing personalizzate devono essere aggiunte manualmente nel file routing.yml dell'applicazione.
  • Filtri personalizzati devono essere aggiunti manualmente in filters.yml.
  • Factories personalizzate devono essere aggiunte manualmente in factories.yml.

In generale, tutte le configurazioni che devono finire nella configurazione dell'applicazione devono essere aggiunte manualmente. I plugin con tale setup manuale devono includere un file README che ne descrive l'installazione in dettaglio.

Personalizzare un plugin per un'applicazione

Se vuoi personalizzare un plugin, non alterarne direttamente il codice nella cartella plugins/. In tale maniera perderesti tutte le modifiche quando lo aggiorni. Per le personalizzazioni i plugin forniscono metodi di override.

Plugin scritti nella maniera giusta utilizzano impostazioni che possono essere cambiate nel file app.yml dell'applicazione, come mostrato nel Listato 17-25.

Listato 17-25 - Personalizzare un plugin che utilizza la configurazione dell'applicazione

[php]
// example plug-in code
$foo = sfConfig::get('app_my_plugin_foo', 'bar');

// Change the 'foo' default value ('bar') in the application app.yml
all:
  my_plugin:
    foo:       barbar

Le impostazioni del modulo ed i loro valori di default sono spesso descritti nel file README dei plugin.

Puoi sostituire i contenuti di default di un modulo plugin creando un modulo con lo stesso nome nella tua applicazione. Non si tratta di un vero override, dato che vengono usati gli elementi della tua applicazione invece che quelli del plugin. Questo metodo funziona bene se crei template e configurazioni con lo stesso nome di quelli del plugin.

D'altra parte, se un plugin vuole offrire un modulo con la capacità di fare ovverride delle sue azioni, il file actions.class.php del plugin deve essere vuoto ed ereditare da una classe caricata automaticamente, in modo che i metodi di tale classe vengano ereditati come quelli di actions.class.php dell'applicazione. Vedi il Listato 17-26 per un esempio.

Listato 17-26 - Personalizzare le azioni di un plugin

[php]
// In myPlugin/modules/mymodule/lib/myPluginmymoduleActions.class.php
class myPluginmymoduleActions extends sfActions
{
  public function executeIndex()
  {
    // Some code there
  }
}

// In myPlugin/modules/mymodule/actions/actions.class.php
class mymoduleActions extends myPluginmymoduleActions
{
  // Nothing
}

// In myapp/modules/mymodule/actions/actions.class.php
class mymoduleActions extends myPluginmymoduleActions
{
  public function executeIndex()
  {
    // Override the plug-in code there
  }
}

Come scrivere un plugin

Solo i plugin PEAR possono essere tramite il comando plugin-install. Ricorda che tali plugin possono essere distribuiti tramite il wiki di symfony, un canale PEAR od un semplice download. Quindi se vuoi scrivere un plugin, la scelta migliore è quella di pubblicarlo come pacchetto PEAR. Inoltre in questo modo i plugin sono più facili da aggiornare, possono dichiarare dipendenze ed automaticamente distribuire gli asset nella cartella web/.

Organizzazione dei file

Supponi di aver sviluppato una nuova funzionalità e di volerla pacchettizzare come plugin. Il primo passo è quello di organizzare i file logicamente, in modo che i meccanismi di autocaricamento di symfony possono trovarli quando necessario. A tale scopo, segui la struttura del Listato 17-23. Il listato 17-27 mostra un esempio di struttura dei file per un plugin sfSamplePlugin.

Listato 17-27 - Esempio di elenco di file da pacchettizzare come plugin

sfSamplePlugin/
  README
  LICENSE
  config/
    schema.yml
  data/
    fixtures/
      fixtures.yml
    tasks/
      sfSampleTask.php
  lib/
    model/
      sfSampleFooBar.php
      sfSampleFooBarPeer.php
    validator/
      sfSampleValidator.class.php
  modules/
    sfSampleModule/
      actions/
        actions.class.php
      config/
        security.yml
      lib/
        BasesfSampleModuleActions.class.php
      templates/
        indexSuccess.php
  web/
    css/
      sfSampleStyle.css
    images/
      sfSampleImage.png

Durante la scrittura, il luogo della cartella del plugin (sfSamplePlugin/ nel Listato 17-27) non è importante. Può essere in qualsiasi punto del disco.

TIP Segui l'esempio dei plugin esistenti e, per la tua prima prova, cerca di riprodurre la loro convenzione nel nominare i file e per la struttura delle cartelle.

Creare il file package.xml

Il passo successivo nella scrittura di un plugin è di aggiungere un file chiamato package.xml nella root della cartella del plugin. package.xml segue la sintassi PEAR. Dai un'occhiata ad un tipico package.xml nel Listato 17-28.

Listato 17-28 - Esempio di package.xml per un plugin symfony

[xml]
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.4.6" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
 <name>sfSamplePlugin</name>
 <channel>pear.symfony-project.com</channel>
 <summary>symfony sample plugin</summary>
 <description>Just a sample plugin to illustrate PEAR packaging</description>
 <lead>
  <name>Fabien POTENCIER</name>
  <user>fabpot</user>
  <email>fabien.potencier@symfony-project.com</email>
  <active>yes</active>
 </lead>
 <date>2006-01-18</date>
 <time>15:54:35</time>
 <version>
  <release>1.0.0</release>
  <api>1.0.0</api>
 </version>
 <stability>
  <release>stable</release>
  <api>stable</api>
 </stability>
 <license uri="http://www.symfony-project.com/license">MIT license</license>
 <notes>-</notes>
 <contents>
  <dir name="/">
   <file role="data" name="README" />
   <file role="data" name="LICENSE" />
   <dir name="config">
    <!-- model -->
    <file role="data" name="schema.yml" />
   </dir>
   <dir name="data">
    <dir name="fixtures">
     <!-- fixtures -->
     <file role="data" name="fixtures.yml" />
    </dir>
    <dir name="tasks">
     <!-- tasks -->
     <file role="data" name="sfSampleTask.php" />
    </dir>
   </dir>
   <dir name="lib">
    <dir name="model">
     <!-- model classes -->
     <file role="data" name="sfSampleFooBar.php" />
     <file role="data" name="sfSampleFooBarPeer.php" />
    </dir>
    <dir name="validator">
     <!-- validators ->>
     <file role="data" name="sfSampleValidator.class.php" />
    </dir>
   </dir>
   <dir name="modules">
    <dir name="sfSampleModule">
     <file role="data" name="actions/actions.class.php" />
     <file role="data" name="config/security.yml" />
     <file role="data" name="lib/BasesfSampleModuleActions.class.php" />
     <file role="data" name="templates/indexSuccess.php" />
    </dir>
   </dir>
   <dir name="web">
    <dir name="css">
     <!-- stylesheets -->
     <file role="data" name="sfSampleStyle.css" />
    </dir>
    <dir name="images">
     <!-- images -->
     <file role="data" name="sfSampleImage.png" />
    </dir>
   </dir>
  </dir>
 </contents>
 <dependencies>
  <required>
   <php>
    <min>5.0.0</min>
   </php>
   <pearinstaller>
    <min>1.4.1</min>
   </pearinstaller>
   <package>
    <name>symfony</name>
    <channel>pear.symfony-project.com</channel>
    <min>1.0.0</min>
    <max>1.1.0</max>
    <exclude>1.1.0</exclude>
   </package>
  </required>
 </dependencies>
 <phprelease />
 <changelog />
</package>

Le parti interessanti qui sono i ta<contents> e <dependencies>, descritti nel seguito. Per i rimanenti, non c'è niente da specificare a symfony, per cui puoi fare riferimento al manuale online di PEAR (http://pear.php.net/manual/en/) per maggiori dettagli sul formato di package.xml.

Contenuti

Il tag <contents> è il luogo in cui descrivere la struttura dei file del plugin. Esso riferirà a PEAR quali file copiare e dove. Descrivi tale struttura con i tag <dir> e <file>. Tutti i tag <file> devono avere un attributo role="data". La parte <contents> del Listato 17-28 descrive l'esatta struttura di cartelle del Listato 17-27.

NOTE L'uso dei tag <dir> non è obbligatorio, dato che potresti usare path relativi come valori di name nei tag <file>. Ad ogni modo, è raccomandato che il file package.xml sia leggibile.

Dipendenze

I plugin sono disegnati per funzionare con un dato insieme di versioni di PHP, PEAR, symfony, pacchetti PEAR, od altri plugin. Dichiarare queste dipendenze nei tag <dependencies> dice alla PEAR di controllare che i pacchetti richiesti siano già installati, o di generare un'eccezione altrimenti.

Dovresti sempre dichiarare le dipendenze da PHP, PEAR e symfony, almeno quelle corrispondenti alla tua installazione, come requisiti minimi. Se non sai cosa inserire, aggiungi PHP 5.0, PEAR 1.4, e symfony 1.0.

Si raccomanda inoltre di aggiungere un numero massimo di versione di symfony per ogni plugin. Questo produrrà un messaggio di errore se si cerca di usare un plugin con versione più nuova del framework, e questo obbligherà l'autore del plugin ad assicurarsi che esso funzioni correttamente con tale versione prima di rilasciarlo di nuovo. È meglio avere un messaggio di avviso e scaricare un upgrade invece di avere un fallimento silenzioso del plugin.

Costruire il Plugin

La PEAR possiede un comando (pear package) che crea un archivio .tgz del pacchetto, posto che venga chiamato il comando mostrato nel Listato 17-29 da una cartella contenente un file chiamato package.xml.

Listato 17-29 - Pacchettizzare un Plugin come pacchetto PEAR.

> cd sfSamplePlugin
> pear package

Package sfSamplePlugin-1.0.0.tgz done

Una volta creato il plugin, controlla che funzioni installandolo tu stesso, come mostrato nel Listato 17-30.

Listato 17-30 - Installare il Plug-In

> cp sfSamplePlugin-1.0.0.tgz /home/production/myproject/
> cd /home/production/myproject/
> php symfony plugin-install sfSamplePlugin-1.0.0.tgz

Secondo la propria descrizione nel tag <contents>, i file pacchettizzati finiranno in cartelle diverse del tuo progetto. Il Listato 17-31 mostra dove saranno copiati i file di sfSamplePlugin dopo l'installazione.

Listato 17-31 - I file del plugin verranno installati nelle cartelle plugins/ e web/

plugins/
  sfSamplePlugin/
    README
    LICENSE
    config/
      schema.yml
    data/
      fixtures/
        fixtures.yml
      tasks/
        sfSampleTask.php
    lib/
      model/
        sfSampleFooBar.php
        sfSampleFooBarPeer.php
      validator/
        sfSampleValidator.class.php
    modules/
      sfSampleModule/
        actions/
          actions.class.php
        config/
          security.yml
        lib/
          BasesfSampleModuleActions.class.php
        templates/
          indexSuccess.php
web/
  sfSamplePlugin/               ## Copia o link simbolico, a seconda del sistema
    css/
      sfSampleStyle.css
    images/
      sfSampleImage.png

Testa il modo in cui il plugin si comporta nella tua applicazione. Se funziona bene, è pronto per essere distribuito su progetti od alla comunità symfony.

Mettere il tuo plugin in hosting sul sito web di Symfony Project

Un plugin symfony raccoglie un'utenza maggiore se distribuito sul sito symfony-project.com. Anche il tuo plugin può essere distribuito in tale maniera, ma devi seguire i seguenti passi:

  1. Assicurati che il file README descriva l'installazione e l'utilizzo del tuo plugin, e che il file LICENSE fornisca i dettagli della licenza. Formatta il tuo README con la sintassi Wiki Formatting (http://www.symfony-project.com/trac/wiki/WikiFormatting).
  2. Crea un pacchetto PEAR per il tuo plugin chiamando il comando pear package, e testalo. Il pacchetto PEAR deve essere chiamato sfSamplePlugin-1.0.0.tgz (1.0.0 è la versione del plugin).
  3. Crea una nuova pagina sul wiki si symfony chiamata sfSamplePlugin (Plugin è un suffisso obbligatorio). In tale pagina, descrivi l'utilizzo del plugin, la licenza, le dipendenze, e la procedura di installazione. Puoi riutilizzare il contenuto del file README. Controlla le pagine sul wiki dei plugin esistenti ed utilizzale come esempio.
  4. Allega il tuo pacchetto PEAR alla pagina del wiki (sfSamplePlugin-1.0.0.tgz).
  5. Aggiungi la nuova pagina del wiki ([wiki:sfSamplePlugin]) alla lista dei plugin disponibili, anch'essa è una pagina del wiki (http://www.symfony-project.com/trac/wiki/SymfonyPlugins).

Se segui questa procedura, gli utenti potranno installare il tuo plugin semplicemente scrivendo il comando seguente da una cartella di progetto:

> php symfony plugin-install http://plugins.symfony-project.com/sfSamplePlugin

Convenzione sui nomi

Per mantenere pulita la cartella plugins/, assicurati che tutti i nomi dei plugin siano nel formato camelCase e finiscano con Plugin (ad esempio, shoppingCartPlugin, feedPlugin, e così via). Prima di dare un nome al tuo plugin, controlla che non ne esista già uno con lo stesso nome.

NOTE I plugin basati su Propel devono contenere Propel nel nome. Ad esempio, un plugin di autenticazione che usa gli oggetti di accesso ai dati Propel di dovrebbe chiamare sfPropelAuth.

I plugin dovrebbero sempre includere un file LICENSE che ne descriva le condizioni di utilizzo e la licenza scelta. Dovresti inoltre sempre aggiungere un file README per spiegare i cambiamenti di versione, lo scopo del plugin, i suoi effetti, le istruzioni di installazione e configurazione, ecc.

Sommario

Le classi symfony contengono hook sfMixer che le permettono di essere modificate a livello applicazione. Il meccanismo di mixin permette l'ereditarietà multipla e l'override di classe a runtime, anche se le limitazioni di PHP lo vietano. In questo modo puoi facilmente estendere le capacità di symfony, anche se devi modificare le classi di core per farlo: ecco perchè esiste la configurazione "della casa madre".

Esistono già diverse estensioni di questo tipo; esse sono pacchettizzate come plugin, per essere facilmente installate, aggiornate e rimosse tramite la linea di comando di symfony. Creare un plugin è facile come creare un pacchetto PEAR, e fornisce riusabilità tra le applicazioni.

Il wiki di symfony contiene molti plugin, e puoi aggiungere i tuoi. Ora che sai come fare, noi speriamo che aiuterai a migliorare il core di symfony con tante utili estensioni!