27. Gestione Avanzata delle Eccezioni

Abbiamo visto nei capitoli precedenti, che l'istruzione throw quando solleva un'eccezione solleva in realtà un oggetto della classe Exception.

PHP ci consente inoltre di sollevare delle eccezioni proprie, attraverso la creazione di sottoclassi specifiche che devono obbligatoriamente essere derivate dalla built-in Exception.

A questo proposito vedremo come sarà possibile annidare più blocchi catch ad un blocco try, similmente ad una struttura di controllo classica come la IF - ELSE IF, che ci consentirà di controllare che tipo di eccezione è stata sollevata e agire di conseguenza.

Illustrerò ora un esempio analogo a quello spiegato nel capitolo Gestione degli Errori, inserendo però una classe aggiuntiva per gestire le eccezioni sugli input utente, lasciando la classe Exception per le altre eccezioni non previste.


Esempio eccezioni personalizzate

Per questo esempio ho preferito creare una classe Utente, che controlla automaticamente la correttezza dei dati, e se necessario solleva un'eccezione del tipo personalizzato ErroriUtente.

Quest'ultima eredita come abbiamo detto prima, dalla classe Exception, fornendo un metodo aggiuntivo per l'inserimento opzionale di un link che invita l'utente a tornare indietro per reinserire i dati.

La funzione "registraUtente()" invece è rimasta invariata, quindi in caso di errore solleva un'eccezione normale che sarà catturata dal secondo blocco catch.

<?php

	class Utente
	{
		public $nome_utente;
		public $pass_utente;

		public function Utente($n, $p)
		{
			if (strlen($n) > ErroriUtente::MAX_LUNGHEZZA)
			    throw new ErroriUtente($n);

			if (strlen($p) > ErroriUtente::MAX_LUNGHEZZA)
			    throw new ErroriUtente($p);

			$this->nome_utente = $n;
			$this->pass_utente = $p;
		}
	}

	class ErroriUtente extends Exception
	{
		const MAX_LUNGHEZZA = 20;

		private $errore;

		public function ErroriUtente($stringa)
		{
			$this->errore = "Errore : La lunghezza massima consentita è " . self::MAX_LUNGHEZZA;
			$this->errore .= "\n<br />\"$stringa\" è di " . strlen($stringa) . " caratteri!";
		}

		private function stampaLink($pagina = "registrazione.php")
		{
			echo "<a href=\"$pagina\">Torna indietro</a> per reinserire i dati.";
		}

		public function stampaMessaggio($link = false)
		{
			echo $this->errore . "<br /><br />\n\n";

			if ($link) $this->stampaLink();
		}
	}
	
	function registraUtente(Utente $utente)
	{
		// ... codice per registrare l'utente nel database

		$registrato = true; // Simuliamo una registrazione avvenuta con successo

		if ($registrato)
			echo "Utente registrato con successo!";
		else
			throw new Exception("Impossibile registrare l'utente, riprova più tardi!");
	}

	try
	{
		$utente = new Utente("Amircare", "supercalifragilistichespiralidoso");

		registraUtente($utente);
	}
	catch (ErroriUtente $e)
	{
		$e->stampaMessaggio(true);
	}
	catch (Exception $e)
	{
		echo $e->getMessage();
	}

?>

Il codice produce questo risultato.


Analisi del codice

Nello script viene sollevata un'eccezione ErroriUtente dal costruttore di Utente nella prima riga del blocco try, che viene catturata dal primo blocco catch.

Se impostate una stringa più corta di "supercalifragilistichespiralidoso" (mannaggia Mary Poppins), e assegnate $registrato a false dentro registraUtente(), allora otterrete il seguente messaggio senza link :
Impossibile registrare l'utente, riprova più tardi!

Quest'ultima eccezione viene invece gestita dal secondo blocco catch.


Suggerimenti sulle eccezioni

Ricordatevi che non sempre è necessario definire un costruttore per la vostra sottoclasse di Exception, se PHP non lo troverà effettuerà automaticamente una chiamata al costruttore della classe madre, ossia Exception.

Inoltre usate questo sistema solo per gestire le eccezioni, per risolvere quindi quei problemi imprevisti che si presume non accadano poi così spesso.

Non utilizzate assolutamente la gestione delle eccezioni come una sruttura di controllo, non solo per un discorso di performance (le eccezioni sono più lente), ma anche per mantenere il codice il più robusto e leggibile possibile, evitando problemi di manutenzione futuri.

Infine prestate attenzione al tipo di eccezioni che gestite in determinati blocchi try / catch, poichè con questo sistema se catturate un eccezione anche molto grave, non interromperete lo script, e il codice successivo sarà quindi eseguito :

<?php

	function sollevaEccezione()
	{ throw new Exception("Eccezione grave!"); }

	function registraUtente()
	{ /* Codice per registrare un'utente */ }

	try
	{
		sollevaEccezione();
	}
	catch (exception $e)
	{
		echo $e->getMessage() . "<br />\n";
	}

	try
	{
		echo "Codice d'esempio che non andrebbe eseguito in caso di eccezioni gravi!";
		registraUtente();
	}
	catch (exception $e)
	{
		echo $e->getMessage();
	}

?>

L'esempio produrrà in output :

Eccezione grave!
Codice d'esempio che non andrebbe eseguito in caso di eccezioni gravi!

E' buona norma quindi minimizzare la quantità di blocchi try / catch e se necessario annidateli.