Scrivere un Plugin

1.  Introduzione

WordPress crede nella possibilità di mantenere tutto semplice, e molti sforzi sono tesi ad includere nuove funzionalità in modo da rispettare lo standard di programmazione di WordPress (il cosiddetto nocciolo di WordPress), in modo da mantenere WordPress veloce e leggero. Gli utenti possono estendere le funzionalità di WordPress tramite i Plugin. E possibile anche scrivere propri plugin per colmare eventuali lacune o per fare qualcosa di speciale con il proprio blog. Questo articolo ti guiderà attraverso il processo, spiegandoti le basi e facendoti qualche esempio.

Allora, per cominciare, che ne dici di progettare un plugin?

Plugin per WordPress
Un plugin per WordPress è un programma, oppure un insieme di funzioni scritte in PHP, che aggiunge un determinato gruppo di funzionalità o di servizi al tuo blog WordPress. Tale programma viene integrato in modo trasparente nel blog mediante l'uso di metodi e punti di accesso forniti da WordPress. Si tratta della cosiddetta Interfaccia di Programmazione dell'Applicazione, o più comunemente Plugin API.

Detto questo, non basta che un file contenga delle funzioni PHP perché sia un Plugin WordPress a tutti gli effetti.

1.1  Requisiti

Questi sono i requisiti fondamentali per un Plugin WordPress:

  • Un plugin può essere contenuto in un unico file, o in un insieme di file. Lo stesso file del plugin deve essere posizionato nella directory wp-content/plugins/ (p. es. wp-content/plugins/plugin.php), o in una sottodirectory (attenzione, non più di una sottodirectory) wp-content/plugins/BAStats/, (p. es. wp-content/plugins/BAStats/BAStats.php).

  • Eventuali file di supporto al plugin dovrebbero essere posizionati nella directory wp-content/ del blog, dal momento che questa non viene sovrascritta quando si aggiorna WordPress ad una versione superiore. Se insieme al plugin hai necessità di distribuire molti file (javascript, immagini, etc.), tieni presente che è meglio installare tutto in una directory contenuta in /wp-content/plugins/{il-tuo-plugin}. In questo modo puoi assicurare all'utente una maggior comodità, tanto più che non ci sono limiti alla creazione di sotto-directory all'interno di wp-content.

  • Le prime righe di tutti i plugin devono essere conformi agli Standard della Struttura del Plugin specificati in questo documento.

  • Gli utenti devono poter attivare e usare il plugin dal menu "Plugin" nella sezione amministrativa del loro Blog. Ancora meglio, un plugin non dovrebbe richiedere la modifica dei file di sistema di WordPress. L'aggiunta di nuovi agganci per plugin può essere discussa nella WP-Hackers mailing list.

  • L'utente deve essere in grado di impostare le preferenze e le variabili richieste dal plugin, attraverso la pagina delle Opzioni specifiche del Plugin, senza dover modificare il codice sorgente del Plugin. Per maggiori informazioni si veda Salvare i Dati del Plugin nel Database e Aggiungere Pannelli di Amministrazione in questo articolo.

  • Assicurati di usare un nome unico per le funzioni definite nei plugin che hai intenzione di sviluppare. Se il tuo plugin infatti, usa una funzione che ha lo stesso nome di una funzione usata in un altro plugin, questo bloccherà il funzionamento di WordPress. Un buon sistema per evitare conflitti tra nomi di funzioni è quello di usare metodi statici per le classi. Per maggiori informazioni, si veda Evitare Funzioni Duplicate.

In aggiunta a questi requisiti, verranno in seguito indicate delle raccomandazioni sulle pratiche e gli standard. Per i principianti, diamo un'occhiata alla Struttura dei File dei Plugin.

2.  La Struttura del File del Plugin

Un Plugin per WordPress deve avere la seguente struttura:

 <?php
/*
 Plugin Name: Nome_Del_Plugin
 Plugin URI: URI_Della_Pagina_Che_Descrive_Il_Plugin_e_Aggiornamenti
 Description: Breve_Descrizione_Del_Plugin
 Version: Il_Numero_Di_Versione_Del_Plugin
 Author: Nome_Autore_del_Plugin
 Author URI: URI_Autore_del_Plugin
 */

Se vuoi, puoi anche aggiungere un modello di Licenza GPL per chiarire che il plugin viene rilasciato sotto la stessa licenza di WordPress.

 /* Copyright 2005 NOME_AUTORE_PLUGIN (email: EMAIL_AUTORE_PLUGIN)

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

Queste due sezioni possono essere seguite dalla definizione delle funzioni e dal codice del plugin.

Se il plugin è composto da più di un file, i commenti sopra indicati devono essere inseriti nel file che verrà posizionato nella directory wp-content/. Questo file di solito contiene le funzioni "principali" e si serve degli altri file usati dal plugin tramite le funzioni PHP include o require.

2.1  Plugin Metadata

Di recente gli sviluppatori di WordPress hanno chiesto agli autori dei plugin che intendono pubblicare i loro lavori su http://wp-plugins.org, di includere dei metadati riguardo il loro plugin, in un file readme.txt. Ad esempio in un file come questo. "Contributors" sono i nomi degli utenti dei forum. "Tags" rappresentano dei tag, appunto, e delle parole chiave.

Insieme ai metadati di un plugin, è possibile accludere delle immagini di anteprima del plugin. Per un esempio, si veda il file screenshot-1.gif su http://svn.wp-plugins.org/staticize-reloaded/trunk/. Chiamare ogni immagine di anteprima in questo modo screenshot-#.(png|jpg|jpeg|gif), e fornire una descrizione esauriente dell'immagine, usando un elenco numerato nella sezione Screenshots del file readme incluso nel plugin.

Questi file possono essere utili per lo sviluppo del plugin, ma non sono usati da WordPress. Sono richiesti solo se si ha intenzione di mettere a disposizione il proprio plugin sul sito http://wp-plugins.org.

3.  La API dei Plugin

WordPress definisce attraverso la sua API dei Plugin un certo numero di agganci che possono essere sfruttati dai plugin per interfacciarsi al codice di WordPress.

Ci sono due tipi di agganci:

  1. Agganci che producono Azioni. Gli Agganci-Azione permettono di aggiungere o rimuovere funzioni la cui esecuzione viene lanciata dalle azioni di WordPress. Questi agganci permettono ai plugin di eseguire delle funzioni ogni volta che si verifica un determinato evento all'interno di WordPress, ad esempio: la pubblicazione di un articolo.
  2. Agganci che producono Filtri. Gli Agganci-Filtri permettono di aggiungere o rimuovere dei filtri a vari tipi di contenuto (di solito si tratta di testo) e perciò permettono ai plugin di modificare il contenuto del testo "al volo".

A seconda del tipo di aggancio usato, le funzioni dei plugin possono essere distinte in due categorie:

Azioni
Le Azioni sono funzioni che specificano eventi aggiuntivi che possono essere causati dagli eventi interni di WordPress.
Filtri
I Filtri sono funzioni che modificano i contenuti di WordPress conservati nel database.

Per una descrizione completa dell'Interfaccia di Programmazione dell'Applicazione, si veda PluginAPI.

3.1  Evitare Funzioni Duplicate

E' possibile che qualcuno abbia creato un plugin contenente una funzione che ha lo stesso nome di un'altra funzione usata nel tuo plugin! Questo rappresenta un problema, dal momento che PHP non permette l'uso di più funzioni aventi lo stesso nome. Se due plugin contengono una funzione con lo stesso nome, oppure un plugin contiene una funzione che si chiama come una funzione interna di WordPress, il blog smette di funzionare. Ci sono due modi per evitare che questo accada.

La prima soluzione consiste nell'aggiungere ad ogni funzione, un prefisso, cioè un insieme unico di caratteri. Se ti chiami Pier Paolo Rossi, puoi dichiarare tutte le tue funzioni in questo modo function ppr_NomeFunzione() {...}. C'è sempre la possibilità che qualcuno abbia le tue stesse iniziali, e faccia la stessa cosa con il proprio plugin, ma le possibilità sono minori.

La seconda soluzione, più facile, è quella di includere le funzioni del tuo plugin in una classe e invocare i metodi della classe staticamente. Sembra più complicato di quanto non lo sia in realtà.

Prendiamo ad esempio questa classe, che estende l'esempio illustrato nella sezione Aggiungere Azioni dell'articolo PluginAPI:

 class avvisa {
  function invia_messaggio($post_ID) {
   $friends = 'bob@example.org,susie@example.org';
    mail($friends,"Il blog e' stato aggiornato",'Ho scritto un nuovo articolo sul mio blog: http://blog.example.com');
   return $post_ID;
  }
}

 add_action('publish_post', array('avvisa', 'invia_messaggio'));

Questa classe, chiamata avvisa possiede un metodo invia_messaggio che implementa la funzionalità del plugin.

La funzione add_action() all'esterno della classe, aggiunge l'azione a WordPress dicendogli di invocare il metodo invia_messaggio ogni volta che viene pubblicato un articolo. L'array usato come secondo parametro, segnala al plugin di invocare il metodo statico della classe avvisa, e cioè il metodo invia_messaggio.

La funzione invia_messaggio viene separata quindi dallo spazio di esecuzione globale grazie alla dichiarazione della classe. Infatti non è possibile richiamare direttamente la funzione invia_messaggio() e in questo modo si evitano collisioni con eventuali altre funzioni chiamate invia_messaggio(). Se tu avessi voluto richiamare la funzione invia_messaggio(), avresti dovuto usare un operatore (scope resolution operator), come questo: avvisa->invia_messaggio().

E' possibile anche associare gli agganci dei plugin alle istanze degli oggetti. Ad esempio:

 $avvisa = new avvisa();
 add_action('publish_post', array($avvisa, 'invia_messaggio'));

Ogni volta che lo stato di un post viene modificato a "Pubblicato", WordPress esegue il metodo invia_messaggio() dell'oggetto $avvisa. Usata in questo modo, la classe può usare dei riferimenti tipo $this->, dal momento che il metodo di richiamo statico illustrato sopra, può solo richiamare i metodi della classe in maniera statica, attraverso l'uso dell'operatore.

Mentre l'esempio sopraindicato è piuttosto semplice, le classi possono diventare un argomento abbastanza complicato. Per saperne di più, consulta la documentazione di PHP sulle classi.

4.  Salvare i Dati del Plugin nel Database

Certe volte si ha la necessità di salvare delle variabili, in modo da conservarne il valore anche dopo l'esecuzione del plugin. WordPress ti permette di definire, aggiornare e ottenere ogni sorta di variabile come 'opzione' dal proprio database. Tramite il salvataggio delle opzioni nel database e la creazione di un nuovo pannello di amministrazione per il tuo plugin, puoi consentire agli utenti di personalizzare i parametri del plugin. Le funzioni che permettono di manipolare queste opzioni, sono specificate più in basso.

NOTA: Come per le funzioni, assicurati che i nomi delle opzioni non siano uguali ad altre opzioni usate da WordPress. Un buon sistema è quello di aggiungere ad ogni opzione, un prefisso, cioè un insieme unico di caratteri come un acronimo del nome del plugin o le tue iniziali.

4.1  Aggiungere Opzioni

 add_option($name, $value, $description, $autoload);
$name
Obbligatorio (stringa). Nome dell'opzione da aggiungere.
$value
Facoltativo, valore predefinito: '' (stringa). La variabile associata al nome della variabile.
$description
Facoltativo, valore predefinito: '' (stringa).
$autoload
Facoltativo, valore predefinito: 'yes' (enum: 'yes' or 'no').

Se un'opzione con un certo nome esiste già, non viene effettuato nessun cambiamento al suo valore o all'intero database. add_option() può solo aggiungere opzioni, non modificarle.

$value può essere una stringa, un array o un oggetto. WordPress serializzerà automaticamente $value, prima di memorizzarlo nel database e de-serializzandolo quando verrà richiamato dal database. Se stai memorizzando più di un'opzione, è più efficiente passare alla funzione add_option() un'array contenente le impostazioni delle opzioni. In questo modo, tutte le impostazioni verranno memorizzate in una sola riga della tabella del database.

Tieni presente che update_option() invoca add_option() nel caso le opzioni non siano state ancora impostate. Perciò è sufficiente richiamare add_option in questo caso, se vuoi impostare le opzioni aggiuntive $description e $autoload con valori diversi da quelli predefiniti.

4.2  Ottenere le Opzioni

 get_option($option);
$option
Obbligatorio (stringa). Nome dell'opzione di cui si vuole ottenere il valore.

Questa funzione richiama i valori memorizzati nel database. Se le le opzioni sono contenute in un array o in un oggetto, WordPress effettuerà una de-serializzazione prima di restituirne i valori.

Parametri accettati
  • 'admin_email' - Indirizzo email dell'amministratore del blog.
  • 'blogname' - Titolo del blog; in Opzioni/Generale.
  • 'blogdescription' - Descrizione del blog; in Opzioni/Generale.
  • 'blog_charset' - La codifica di caratteri (Charset) per il blog; n Opzioni/Lettura.
  • 'date_format' - Formato predefinito della data; in Opzioni/Generale.
  • 'default_category' - Categoria di default per gli articoli; set in Writing Options.
  • 'home' - L'indirizzo web della home; in Opzioni/Generale.
  • 'siteurl' - L'indirizzo web del blog in Opzioni/Generale.
  • 'template' - Il nome del tema corrente; in Aspetto.
  • 'start_of_week' - Giorno della settimana con il quale partire; in Opzioni/Generale
  • 'upload_path' - percorso di upload; in Opzioni/Varie.
  • 'posts_per_page' - Numero massimo di articoli per pagina; in Opzioni/Lettura.
  • 'posts_per_rss' - Numero massimo di articoli recenti mostrati nel feed rss; in Opzioni/Lettura.

Ci sono molte altre opzioni disponibili, tutto dipende dai plugin installati nel vostro blog. Per visualizzare la lista completa di parametri visita la pagina /wp-admin/options.php del tuo blog.

4.3  Aggiornare le Opzioni

 update_option($option_name, $newvalue);
$option_name
Obbligatorio (stringa). Nome dell'opzione da aggiornare.
$newvalue
Obbligatorio (stringa). Il valore che si vuole cambiare per l'opzione specificata.

$value può essere una stringa, un array o un oggetto. WordPress serializzerà automaticamente $value, prima di memorizzarlo nel database e de-serializzandolo quando verrà richiamato dal database. Se stai memorizzando più di un'opzione, è più efficiente passare alla funzione add_option() un'array contenente le impostazioni delle opzioni. In questo modo, tutte le impostazioni verranno memorizzate in una sola riga della tabella del database.

Tieni presente che update_option() invoca add_option() nel caso le opzioni non siano state ancora impostate. Perciò è sufficiente richiamare add_option in questo caso, se vuoi impostare le opzioni aggiuntive $description e $autoload con valori diversi da quelli predefiniti.

5.  Interfacce Utente

5.1  Rendere le Funzioni Disponibili ai Template

Tutte le funzioni dei plugin sono disponibili di default e sono richiamabili dai Template. Anche le classi e gli oggetti dichiarati nei plugin sono disponibili nei template. Tuttavia le funzioni dei plugin contenuti in una classe, hanno bisogno dell'operatore (scope resolution) per funzionare. E' consigliabile fornire delle funzioni di tipo wrapper nello spazio di esecuzione globale per facilitare le cose agli utenti finali.

5.2  Aggiungere Pannelli di Amministrazione

Tramite l'aggiunta di nuovi pannelli di amministrazione, puoi permettere ai tuoi utenti di personalizzare ogni paramentro usato dal tuo plugin senza costringerli a modificare il codice sorgente del plugin. E' sufficiente creare un modulo HTML che restituisce e aggiorna le varie opzioni del tuo plugin.

Quando si vuole aggiungere un nuovo Sotto-Pannello all'interfaccia di amministrazione di WordPress, spesso si vuole aggiungere questo pannello a quello delle Opzioni. Ecco quindi alcune istruzioni di base per aggiungere un nuovo pannello al menu delle Opzioni. Per una panoramica completa si veda Aggiungere Menu di Amministrazione.

Da qualche parte, nel tuo plugin, aggiungi questa funzione:

 function your_function_name_here() {
 	if (function_exists('add_options_page')) {
 		add_options_page('subpaneltitle', 'submenuitemname', minuserlevel, basename(__FILE__), 'my_options_subpanel');
 		}
 	}

Alla fine del plugin, dove hai aggiunto tutti gli agganci, scrivi:

 add_action('admin_menu', 'your_function_name_here');

Questo aggiungerà un nuovo elemento 'subpaneltitle', al menu delle Opzioni. Il contenuto del pannello che viene visualizato quando il menu 'subpaneltitle' è attivo, viene generato dalla funzione my_options_subpanel(), che devi specificare da qualche parte nel plugin.

A volte, il tuo plugin ha bisogno di sapere se viene caricato come pagina di amministrazione. E' possibile ottenere questa informazione mediante la funzione booleana is_plugin_page(). Questo sistema è l'unico per aggiungere contenuti all'interfaccia di amministrazione. Ora, per evitare di usare troppi controlli di tipo if, è preferibile usare il summenzionato sistema, basato sulla funzione my_options_subpanel().

6.  Localizzazione dei Plugin

WordPress usa il framework di localizzazione GNU Gettext. Gettext è un framework stabile e molto diffuso che si usa per la traduzione modulare dei software. Di fatto è lo standard per la localizzazione dei software open source/free. Le informazioni riguardanti la traduzione di WordPress sono disponibili alle pagine WordPress in Italiano e Localizing WordPress sul Codex del progetto.

Il catalogo predefinito delle stringhe di WordPress da tradurre non comprende le stringhe contenute nel tuo plugin. Se desideri ablitare altre traduzioni del tuo plugin, devi fare in modo di caricare il suo catalogo della traduzione.

All'inizio del plugin, per includere un dominio di testo del plugin, scrivi questa riga di codice:

 load_plugin_textdomain('myplugin');

Questo carica un dominio di testo chiamato 'myplugin'. Devi cambiare questo nome di dominio per adattarlo al tuo plugin. WordPress cercherà di caricare un catalogo chiamato $domain-$locale.mo in base al dominio specificato nel plugin e alle impostazioni di localizzazione di WordPress definite da WPLANG nel file wp-settings.php. WordPress cercherà questo file nella stessa directory dove si trova il plugin.

Il tuo plugin potrà quindi usare le funzioni di localizzazione __() e _e(). Attenzione, devi fornire un parametro addizionale a queste funzioni, e cioè il dominio del plugin da usare per la traduzione:

__($message, $domain)
Cerca il modulo di localizzazione per la stringa $message, e passa la traduzione allo statement di PHP return. Se non viene trovata nessuna traduzione di $message, verrà restituita la stringa originale $message non tradotta.
_e($message, $domain)
Cerca il modulo di localizzazione per la stringa $message, e passa la traduzione allo statement di PHP echo. Se non viene trovata nessuna traduzione di $message, verrà stampata la stringa originale $message non tradotta.

__() si usa per passare la stringa come argomento ad un'altra funzione; _e() si usa per stampare la stringa direttamente nella pagina.

7.  Buone Pratiche di Programmazione

Ecco alcuni consigli da tener presente quando si scrive un plugin; alcuni riferimenti sulle cose da evitare e su come evitare tali cose.

  • Fai riferimento alle Linee Guida di Programmazione di WordPress, che si basano anche sulle Linee Guida di Programmazione PEAR. Lo stile di indentazione di WordPress è principalmente K&R, ma snello, e con le guste indentazioni, facile da leggere.
  • Non programmare facendo riferimento diretto, nei tuoi plugin, al prefisso delle tabelle (di solito è "wp_"), ma usa la variabile $table_prefix ogni volta che hai bisogno di fare riferimento alle tabelle.

8.  Consigli PHP

8.1  Script per l'invio di Moduli

Creare uno script che visualizza un modulo, controlla i dati inviati tramite modulo e invoca una funzione che gestisce i dati. Per verificare se una pagina viene richiesta come risultato dell'invio di un modulo, un modo è quello di usare un campo nascosto nel modulo (ad esempio chiamato 'inviato'). Nelle prime righe dello script PHP, si possono usare sia isset($foo) che restituisce TRUE se la variabile è stata inizializzata, sia is(empty($foo)), che quasi è simile. Per le differenze tra le due funzioni, si veda la documentazione di PHP.

Modulo con campo nascosto:

 <?php
echo '
 <form name="example" action="' . $_SERVER[PHP_SELF] . '" method="post">
 Text: <input type="text" name="text" /><br />
 <input type="hidden" name="submitted" />
 <input type="submit" value="Go!" />
 <form>
 '; ?>

Per controllare il valore del campo nascosto:

 isset($_POST['submitted']) /* Per method=POST */
 isset($_GET['submitted']) /* Per method=GET */
 isset($_REQUEST['submitted']) /* Per entrambi i tipi di modulo */

9.  Consigli per il Database

9.1  Leggere è facile, Scrivere è costoso

I database sono eccezionalmente buoni per restituire i dati in modo veloce. Fare dei cambiamenti al database tuttavia, è un processo complesso e molto costoso in termini di prestazioni. Per questo motivo bisogna minimizzare la quantità di dati scritti nel database attraverso un'adeguata progettazione del codice, in modo da scrivere solamente i dati di cui si ha bisogno.

9.2  SELEZIONA solo ciò di cui hai bisogno

Anche se i database restituiscono i dati in maniera abbastanza veloce, dovresti comunque cercare di ridurre il carico sul database selezionando solamente quei dati di cui hai veramente bisogno. Se vuoi contare il numero di righe di una tabella, non devi usare SELECT * FROM perché così saranno estratti tutti i dati, in tutte le righe, sprecando memoria. Allo stesso modo, se nel tuo plugin ti serve conoscere solo il post_id e post_author, allora definisci il SELECT per selezionare solo quegli elementi.

Ricorda: in uno stesso momento ci possono essere centinaia di altri processi che tengono occupato il database. Il database e il server hanno entrambi poche risorse ciascuno, che devono condividere con tutti gli altri processi. Imparare a minimizzare il carico sul database farà sì che il tuo plugin venga apprezzato ancora di più.

10.  Da Fare

Alright, so here we go...before you left, mother asked you to remember the following:

  1. general
  2. if(!function_exists('dys_function_al')....
  3. use your initials to ensure your functions don't clash with others
  4. adding options
  5. add_options_page($page_title, $menu_title, $access_level, $file)
  6. add_option() and delete_option()
  7. load_plugin_textdomain($domain) --Steal write-up from boren.nu without accredition ;)
  8. need a taxonomy of plugins
Ultima modifica il 21/09/2007 ore 14:04