Il design pattern Singleton è uno dei pattern fondamentali dell’ingegneria del software. Se ne era già accennato negli articoli riguardanti la programmazione ad oggetti in PHP, precisamente qui, e qui, rimandando però sempre la questione.
Do per scontato che chi sta leggendo conosca il pattern. In ogni caso Wikipedia nè dà, come sempre, una spiegazione perfetta. Ho scelto di scrivere un articolo apposta, piuttosto che parlarne negli articoli sulla programmazione ad oggetti, non tanto perchè il singleton sia un pattern complicato da implementare (anzi, è il più banale), ma perchè in PHP il singleton ha un comportamento "anomalo".
In pratica un singleton in PHP è singolo solo all’interno della stessa esecuzione dello script. Ovvero è valido solo per la singola richiesta. Per chiarire meglio basta eseguire questo codice: è una classe singleton più alcune istruzioni che creano un oggetto singolo e ne richiamano il metodo getVar() due volte di seguito.
<?php
class Single {
private static $singleton = null;
private $var;
static function getInstance(){
if (Single::$singleton == null){
Single::$singleton = new Single();
}
return Single::$singleton;
}
function __construct(){
$this->var = rand();
}
function getVar(){
return $this->var;
}
}
$single = Single::getInstance();
echo "Primo get: ".$single->getVar()."<br />";
$single = Single::getInstance();
echo "Secondo get: ".$single->getVar()."<br />";
?>
Nel costruttore dell’oggetto viene generato un numero random, memorizzato nella variabile $var. Il metodo getVar() ritorna semplicemente questa variabile. Se provate ad eseguire lo script, vi verrà stampato:
Primo get: xxxxx
Secondo get: xxxxx
dove xxxxx è il numero casuale generato. In pratica le due chiamate del metodo getVar() restituiscono, ovviamente, lo stesso valore. Fin qui tutto ok.
Provate però ora a fare un refresh della pagina. Lo script viene eseguito nuovamente, e, a sorpresa, è stato generato un altro numero. Notare che la richiesta viene sempre dallo stesso utente (stesso client, stesso browser, stessa finestra del browser) e ancora in un momento in cui la sessione precedente è ancora valida. Questa è l’anomalia.
In una normale applicazione il singleton rimane unico dall’inizio al termine del processo. Il problema in PHP è che non esiste il concetto di applicazione. Ovvero, ogni script è come se un’applicazione a sè stante. Per questo ad ogni nuova esecuzione il singleton precedente è sparito. Esso è stato creato, e poi eliminato al termine dello script. Per questo all’esecuzione successiva è sparito.
Se provaste ad implementare un singleton ad esempio tramite dei servlet Java, notereste che questo inconveniente non esiste. Perchè il deploy di un servlet Java avviene in un application server, che provvede a crearle un "ambiente" che gli permette di comportarsi come una applicazione vera e propria. Ecco che quindi il singleton continuerebbe a vivere anche dopo l’esecuzione del servlet, indipendentemente che le richieste arrivino da browser diversi, client diversi, o in momenti diversi.
Per questo motivo il singleton in PHP non è utile come in molti altri linguaggi di programmazione. Ad esempio non è possibile utilizzarlo per implementare una cache, perchè essa verrebbe persa alla fine dello script. Ovviamente esistono dei rimedi. Eccone alcuni, con pro e contro, ma ognuno può pensare alla soluzione che più gli piace:
- Memorizzare il singleton, ad esempio in un database al momento della creazione, e poi recuperarlo ad ogni richiesta. Valutata la capacità di caching dei DBMS, potrebbe essere una soluzione da non scartare, soprattutto se è possibile utilizzare MySQL con engine Memory, che non richiede accessi al disco.
- Se il singleton deve essere relativo ad un utente, può essere in qualche modo serializzato e memorizzato in cookies sul client. Ma ovviamente non è un metodo da usare per dati sensibili, come login, e soprattutto password.
- Se il singleton deve esistere per una sessione, può essere memorizzato tra i dati della sessione dell’utente.
- qualsiasi altra cosa vi viene in mente…
Ora che abbiamo fatto chiarezza sul problema del singleton in PHP, è possibile capire che questo problema si presenta anche nel caso di attributi statici delle classi, per il medesimo motivo. Quindi, pensare di usare una classe con variabili statiche al posto di un singleton, non cambierebbe il risultato. Anche queste classi sono allocate e deallocate ad ogni richiesta.