Piero V.

Singleton in PHP + annesso bug

È da un po’ di tempo che sto programmando in C++ ma oggi sono tornato a programmare un po’ in PHP.

Devo dire che ormai mi sembra strano non dover dichiarare più variabili, non aver più gli header e poter implementare i metodi fuori dalla classe stessa.

Però mi ha fatto bene questa pausa di PHP perché ho imparato una tecnica molto interessante, quella delle “singleton”.

In pratica le singleton sono classi che possono avere una sola istanza.

Il loro metodo si riduce praticamente ai metodi statici, però sono più eleganti, secondo me.

Ci sono due principali metodi in C++ per fare i singleton: impostare il costruttore private, in modo da impedire la chiamata al di fuori della classe e dopo chiamarlo o da un metodo statico oppure tramite l’overriding dell’operatore new.

Implementare il primo metodo in PHP è facilissimo, mentre implementare il secondo dovrebbe essere fattibile tramite il metodo magico __call. Però non funziona, ma per farvi vedere come sarebbe lo metto lo stesso nel codice, che è il seguente:

<?php

class MiaClasse {

	/**
	 * L'istanza del singleton.
	 *
	 * @var MiaClasse object
	 * @access private
	 */
	private static $msSingleton = null;

	/**
	 * Un numero per fare una prova.
	 *
	 * @var integer
	 * @access public
	 */
	public $prova = 0;

	/**
	 * Il costruttore.
	 *
	 * È privato perché così si può creare una nuova istanza
	 * solamente da altri metodi di questa classe.
	 */
	private function __construct()
	{
		echo "Costruisco la classe\n";
	}

	/**
	 * Ritorno (o creo, se non esiste) il singleton.
	 */
	public static function getSingleton()
	{
		if(is_null(self::$msSingleton)) {
			self::$msSingleton = new MiaClasse();
		}

		return self::$msSingleton;
	}

	/**
	 * Fallback per le chiamate ai metodi privati.
	 *
	 * Se per caso si prova a creare una nuova istanza, tramite
	 * questa funzione chiamo sempre getSingleton.
	 * È un po' come l'overriding dell'operatore new in C++.
	 */
	public function __call($name, $arguments)
	{
		echo '__call called. $name = ' . $name;
		if($name == '__construct') {
			return self::getSingleton();
		}
	}

}

$a = MiaClasse::getSingleton();
$a->prova = 20;

$b = MiaClasse::getSingleton();
echo $b->prova . "\n";
$b->prova = 27;
echo $a->prova . "\n";

$c = $a->__construct();
$c->foo = 32;
echo $a->foo . "\n";

/*$d = new MyClass();
echo $d->foo . "\n";
$d->foo = 29;
echo $a->foo . "\n";*/

Come vedete se modifico $b modifico anche $a.

Magari questo esempio non rende l’efficacia delle singleton ma in altri contesti possono essere molto comode.

Se vedete, le ultime 4 righe sono commentate.

Se provate a decommentarle vi darà questo errore:

Fatal error: Call to private MiaClasse::__construct() from invalid context

In pratica non può chiamare la funzione __construct perché è fuori dal contesto.

Però la funzione __call non viene chiamata, mentre se provassimo a creare un altro metodo, renderlo privato e poi chiamarlo, verrebbe chiamata anche la funzione __call.

Perciò, per il momento può funzionare solo il metodo dei metodi statici (scusate il gioco di parole 😉 ).

Ho intenzione di tradurre in inglese il codice e poi segnalarlo come bug.

EDIT: Il mio primo bug di PHP segnalato 😊

2 commenti