30. Il metodo Magico __set()


Overload di __set()

Prototipo della funzione :

void __set (string name, mixed value)

Questo metodo ci consente di effettuare delle operazioni di assegnazione tradizionali sugli attributi della classe.

Il primo parametro è il nome dell'attributo mentre il secondo parametro rappresenta il valore da assegnare a tale attributo.

Se ad esempio abbiamo un'istanza di una ipotetica classe "Oggetto" che ha definito il metodo __set(), questo sarà richiamato quando verrà effettuata un'operazione di assegnazione ad un attributo della classe non esistente o non accessibile (private o protected), passando alla funzione i soliti due parametri, il nome dell'attributo sotto forma di stringa e il valore da assegnargli.


Esempi di overloading

<?php

	class Oggetto
	{
		public function __set($attributo, $valore)
		{ /* Corpo funzione */ }
	}

	$obj = new Oggetto();
	$obj->pippo = 32;

?>

L'ultima riga di codice quindi ($obj->pippo = 32), corrisponde ad una chiamata al metodo __set() con questi parametri : __set("pippo", 32);
Questo accade perchè nella definizione della classe "Oggetto", non abbiamo dichiarato nessun attributo di nome "pippo".

Vediamo un esempio più complesso :

<?php

	class Stringa
	{
		private $stringa;
		const MAX_CHARS = 15;

		public function Stringa($valore)
		{
			if (strlen($valore) > self::MAX_CHARS)
			{ throw new Exception("Stringa troppo lunga (max " . self::MAX_CHARS . " car.)"); }
			else
			{ $this->stringa = $valore; }
		}

		public function __set($attributo, $valore)
		{
			if ($attributo == "stringa")
			{ self::Stringa($valore); }
			else
			{ throw new Exception("Attributo non esistente!"); }
		}

		public function __toString()
		{ return $this->stringa; }
	}

	try
	{
		$obj = new Stringa("Ciao ciao!!!");
		echo "$obj<br>\n"; // Stampa "Ciao ciao!!!"

		$obj->stringa = "Ancora ciao!";
		echo "$obj<br>\n"; // Stampa "Ancora ciao!"

		$obj->stringa = "Un ultimo saluto!";
		echo "$obj<br>\n"; // Solleva un'eccezione con il messaggio : "Stringa troppo lunga (max 15 car.)"

	}
	catch (Exception $e)
	{
		echo $e->getMessage();
	}

?>

Nell'esempio soprastante ho impostato l'attributo $stringa della classe Stringa come privato, attraverso il modificatore di accesso private.

Poi ho reso l'attributo accessibile dall'esterno attraverso l'overload del metodo __set(), che in questo caso ho usato per simulare un accesso all'attributo di tipo public.

Questo mi consente di effettuare dei controlli precisi sui valori assegnati all'attributo $stringa, senza comunque perdere la trasparenza e la comodità della classica operazione di assegnamento, tipica degli attributi public.

A seguire un ultimo esempio, dove gestirò gli attributi della classe "Coordinata", come se fossero degli attributi normali memorizzandoli invece in un unico attributo di tipo Array.

Il questo modo avremo la sicurezza che venga sempre effettuato un casting esplicito di tipo float durante l'assegnazione dei valori, e risparmieremo inoltre righe di codice durante la gestione degli pseudo-attributi potendo utilizzare una semplice foreach per scorrerli tutti, creando del codice rapido e più flessibile.

<?php

	// Inizio dichiarazione Classe
	class Coordinata
	{
		private $xyz;

		public function Coordinata($x, $y, $z)
		{
			$this->xyz = array();

			$this->xyz["x"] = (float) $x;
			$this->xyz["y"] = (float) $y;
			$this->xyz["z"] = (float) $z;
		}

		public function __set($attributo, $valore)
		{
			switch ($attributo)
			{
				case "x" :
				case "y" :
				case "z" :
					$this->xyz[$attributo] = (float) $valore;
					break;
				default :
					throw new Exception("Attributo non valido!");
					break;
			}
		}
		
		public function __toString()
		{
			$return = "";

			foreach ($this->xyz as $chiave => $valore)
				$return .= "$chiave = $valore<br />\n";

			$return .= "<br>\n";

			return $return;
		}
	}
	// Fine dichiarazione Classe

	try
	{
		$punto = new Coordinata(15, 17.3, 22.4);

		echo $punto;

		$punto->x = 20.3;
		$punto->z = 11.2;

		echo $punto;

		$punto->a = 22.1; // L'attributo "a" non esiste e sarà sollevata un'eccezione
	}
	catch (Exception $e)
	{ echo $e->getMessage(); }

?>

Il codice soprastante produrrà questo risultato.