Errori

1. Gestione degli Errori

Procediamo con la creazione di un Array globale, in cui andremo a mettere la traduzione delle stringhe d'errore MySQL che ci interessano per le nostre applicazioni.

Se avete scaricato la documentazione ufficiale di MySQL, andate all'appendice B.
Più esattamente a "Errors, Error Codes, and Common Problems".

Nei capitoli "B.2. Server Error Codes and Messages" e "B.3. Client Error Codes and Messages" trovate la lista completa degli errori, nel caso in cui non siate soddisfatti e vogliate aggiungerne altri al vostro Array per renderlo più completo.

Di seguito riporto il mio Array con gli errori più comuni e la rispettiva traduzione :

$MySQLErrors = array();

$MySQLErrors[1040] = "Troppe connessioni. Riprova più tardi grazie";
$MySQLErrors[1044] = "Accesso negato. Utente non autorizzato";
$MySQLErrors[1045] = "Accesso negato. Utente non autorizzato";
$MySQLErrors[1046] = "Nessun database è stato selezionato";
$MySQLErrors[1047] = "Comando sconosciuto";
$MySQLErrors[1049] = "Impossibile trovare il database specificato";
$MySQLErrors[1051] = "Impossibile trovare la tabella specificata";
$MySQLErrors[1054] = "Impossibile trovare il campo specificato";
$MySQLErrors[1062] = "Valore già presente";
$MySQLErrors[1064] = "Errore di sintassi";
$MySQLErrors[1065] = "Specificare una query";
$MySQLErrors[1105] = "Sconosciuto";
$MySQLErrors[1146] = "Impossibile trovare la tabella specificata";
$MySQLErrors[2003] = "Impossibile connettersi all'host specificato";
$MySQLErrors[2005] = "Impossibile trovare l'host specificato";

L'indice dell'array corrisponde al valore intero dell'errore in MySQL.
In questo modo, con una semplice funzione, potremo richiamare direttamente la stringa tradotta, usando la funzione delle librerie standard di PHP "mysql_errno()".

Di seguito la funzione per richiamare gli errori tradotti :

function getMySQLError($errno)
{
	global $MySQLErrors;
	$errno = intval($errno); // convertiamo in un valore intero per leggere correttamente l'array

	if (strlen($MySQLErrors[$errno]))
		return $MySQLErrors[$errno];
	else
		return "Errore imprevisto n." . $errno;
}

Ora non resta che salvare il nuovo file come MySQLErrors.php.

21. Gestire le Eccezioni

Vedremo in questo capitolo gli strumenti che ActionScript 3 ci mette a disposizione per gestire in modo trasparente le eccezioni, quindi tutti gli eventi considerati non previsti e gli errori.

Per fare un pò di chiarezza, potremmo considerare una eccezione come la ricezione di una password di 20 caratteri quando il massimo consentito per la nostra applicazione è ad esempio 15, mentre potremmo considerare un errore come la ricezione di un tipo stringa quando invece attendiamo ad esempio un intero.

Tratterò le due varianti come equivalenti dal momento che entrambe producono un risultato non desiderato.
Ora vediamo i costrutti che ActionScript 3 ci fornisce :

try
{
	// Codice in cui si potrebbe verificare un'eccezione
}
catch (errore:ErroreUno)
{
	// Codice per gestire un eventuale eccezione di tipo "ErroreUno"
}
catch (errore:ErroreDue)
{
	// Codice per gestire un eventuale eccezione di tipo "ErroreDue"
}
finally
{
	// Codice che verrà eseguito in ogni caso
}

Il blocco try dovrà contenere il codice che potrebbe generare una potenziale eccezione.
Per ogni blocco try ci deve essere almeno un blocco catch pronto a catturare e gestire l'eccezione.

Per questo motivo è possibile utilizzare più di un blocco catch per ogni blocco try, in modo da poter catturare e gestire in blocchi separati ogni tipo di eccezione prevista.

Inoltre abbiamo il blocco finally che è facoltativo, utile ad esempio per effettuare operazioni di pulizia.

E' possibile utilizzare un numero indeterminato di blocchi try, a patto che siano separati o annidati fra di loro e che ognuno abbia il rispettivo blocco catch per la cattura dell'eccezione.

L'esempio seguente mostra come è possibile utilizzare dei blocchi annidati :

try
{
	try
	{
		// ...
	}
	catch (errore:*) // Cattura tutti i tipi di eccezione
	{
		// ...
	}
	finally
	{
		// ...
	}
}
catch (errore:ErroreUno)
{
	// Codice per gestire un eventuale eccezione di tipo "ErroreUno"

}
catch (errore:ErroreDue)
{
	// Codice per gestire un eventuale eccezione di tipo "ErroreDue"
}
finally
{
	// Codice che verrà eseguito in ogni caso
}

Questo tipo di gestione può tornare utile qualora si verificasse un'eccezione nel primo blocco catch o nel primo finally, per fare un esempio durante la chiusura di una connessione in risposta ad una eccezione catturata nel secondo try e così via.


Sollevare le eccezioni con throw

ActionScript 3 ci mette a disposizione un altro utilissimo strumento, la direttiva throw, che ci consente di sollevare delle eccezioni personalizzate.

Prima di procedere è necessario sapere che tutte le eccezioni sollevate, sia da noi che dalle API di Flash Player, sono in realtà istanze di una determinata classe, derivata dalla classe Error o dalla classe ErrorEvent.

Tutte le altre classi definite per la gestione di eccezioni più specifiche, derivano dalle due classi sopracitate, così se noi volessimo definire una nuova classe per gestire delle eccezioni personalizzate per la nostra applicazione, dovremmo derivare tale classe da una delle due classi madre Error o ErrorEvent.

Attraverso throw possiamo richiamare il costruttore delle classe desiderata, creando una nuova istanza che sarà poi catturata dal blocco catch adeguato, che prenderà come parametro un oggetto dello stesso tipo sollevato o derivato da esso.

try
{
	throw Error("Si è verificato un errore irreversibile!");
}
catch (errore:Error)
{
	trace(errore.message);
}
finally
{
	trace("Applicazione terminata!");
}

Il codice soprastante genera il seguente output :
Si è verificato un errore irreversibile!
Applicazione terminata!

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.

26. La Classe Exception

La classe Exception è una classe built-in, ossia nativa del linguaggio PHP.

E' possibile estendere questa classe, creandone delle proprie derivate da essa, ma prima di creare le nostre Sottoclassi personalizzate è necessario conoscere Exception e sapere che opportunità ci offre.

Vediamo subito la dichiarazione della classe :

<?php

	class Exception
	{
		protected $message = 'Unknown exception'; // exception message
		protected $code = 0; // user defined exception code
		protected $file; // source filename of exception
		protected $line; // source line of exception

		function __construct($message = null, $code = 0);

		final function getMessage(); // message of exception 
		final function getCode(); // code of exception
		final function getFile(); // source filename
		final function getLine(); // source line
		final function getTrace(); // an array of the backtrace()
		final function getTraceAsString(); // formated string of trace

		/* Overrideable */
		function __toString(); // formated string for display
	}

?>

Diamo un occhiata più da vicino alle proprietà della suddetta classe :

Attributi protetti

  • $message - Il messaggio dell'eccezione
  • $code - Il codice errore definito dall'utente
  • $file - Il file sorgente dove è stata sollevata l'eccezione
  • $line - La riga del sorgente dove è stata sollevata l'eccezione

Costruttore

  • __construct() - Costruttore della classe

Metodi finali

  • getMessage() - Restituisce $message
  • getCode() - Restituisce $code
  • getFile() - Restituisce $file
  • getLine() - Restituisce $line
  • getTrace() - Restituisce un array di backtrace()
  • getTraceAsString() - Restituisce una stringa formattata del trace

Metodi ridefinibili

  • __toString() - Restituisce una stringa formattata dell'oggetto

Nel prossimo capitolo approfondiremo l'argomento creando delle sottoclassi di Exception, per una gestione delle eccezioni maggiormente personalizzata.

25. Gestione degli Errori


Teoria sulle Eccezioni

Come abbiamo visto nel capitolo Strutture di controllo, al verificarsi di una situazione inaspettata, quindi di un errore dal momento che l'applicazione non è stata programmata per produrre quello stato, è possibile interrompere lo script con le istruzioni exit e die.

Così facendo però non si ha una gestione dell'errore intelligente ma una drastica terminazione dello script.

Per essere precisi, il termine esatto per definire questi eventi inaspettati che si verificano durante l'esecuzione dello script, è Eccezioni.

Un evento imprevisto, un baco nello script, la perdita di una connessione al Database, sono tutte considerate eccezioni che PHP ci consente di gestire attraverso tre comandi molto semplici :

  • throw - Solleva un'eccezione
  • try - Se si verifica un'eccezione nel codice del blocco try, quest ultimo fa saltare l'esecuzione del codice al blocco catch
  • catch - Contiene il codice alternativo che viene eseguito al verificarsi dell'eccezione

Il sistema è molto semplice ed è paragonabile ad un normale blocco IF ELSE.

Il codice per cui intendete gestire le eccezioni va all'interno del blocco try, che obbligatoriamente dovrà avere un blocco antagonista catch che conterrà il codice alternativo da eseguire nel caso in cui si verificasse un'eccezione.

Se all'interno del blocco try si verifica una condizione per cui viene chiamato il comando throw, allora l'esecuzione salta al blocco catch.

Non è necessario che l'istruzione throw si trovi esattamente nel blocco try, ma può essere richiamata anche da una funzione o da un metodo di un oggetto all'interno di try.


Esempio di gestione errori

Nell'esempio che segue vedremo del codice che ha come compito il controllo dei dati di un utente.
Se i dati vengono ritenuti adeguati si procede alla registrazione nel database, altrimenti si notifica l'errore all'utente, mettendogli a disposizione un link per tornare indietro a reinserire i dati.

Il codice essendo solo dimostrativo, considera i dati non validi se superano una certa lunghezza, e rimanda l'utente ad una ipotetica pagina di registrazione chiamata registrazione.php :

<?php

	define("MAX_LUNGHEZZA", 20);

	function registraUtente()
	{
		// ... 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!");
	}

	/* --- Inizio dati Utente --- */

	$nome_utente = "Agamennone";
	$pass_utente = "supercalifragilistichespiralidoso";

	/* --- Fine dati Utente --- */

	try
	{
		if (strlen($nome_utente) > MAX_LUNGHEZZA)
		    throw new Exception("Nome utente troppo lungo!");

		if (strlen($pass_utente) > MAX_LUNGHEZZA)
		    throw new Exception("Password troppo lunga!");

		registraUtente();
	}
	catch (Exception $exc)
	{
		echo "Errore : " . $exc->getMessage() . "<br />\n\n";
		echo "<br /><a href=\"registrazione.php\">Torna indietro</a> per reinserire i dati.";
	}

?>

Il codice soprastante produce questo risultato.

Se provate a modificare la variabile $nome_utente con una stringa più lunga di 20 caratteri, otterrete invece il seguente messaggio :
Errore : Nome utente troppo lungo!

Impostando la variabile $registrato col valore false, all'interno della funzione "registraUtente()", simuliamo un fallimento nella registrazione e viene sollevata una nuova eccezione ottenendo il messaggio :
Errore : Impossibile registrare l'utente!

Ovviamente per far verificare l'ultimo caso descritto, è necessario dare alle due variabili $nome_utente e $pass_utente, una lunghezza minore o uguale a 20, perchè vengono controllate prima dell'esecuzione della funzione "registraUtente()."


Osservazioni sulle eccezioni

Come avrete notato dall'ultimo esempio, quando throw solleva un'eccezione, manda al dovuto blocco catch un'istanza della classe Exception, nativa di PHP.

Throw quindi, utilizza new per creare una nuova istanza, e a seguire il costruttore di Exception con un messaggio opzionale per inizializzarne l'istanza.

La suddetta istanza viene poi catturata da catch, che si serve del metodo "getMessage()" per leggere il messaggio inviatogli da throw e gestire l'eccezione come programmato.

Nota : Se viene sollevata un'eccezione al di fuori di un blocco try / catch, questa interromperà lo script con un errore fatale :
Fatal error: Uncaught exception ...

Condividi contenuti