PHP Exception Handling
§ 1. PHP Exception Handling
Le eccezioni sono usate per modificare il normale flusso di uno script quando si verifica un errore.
§ 1.1 Che cosa è una eccezione
Con PHP arriva un nuovo modo object-oriented di trattare gli errori.
La gestione delle eccezioni è usata per modificare il normale flusso di esecuzione del codice al verificarsi di una specifica condizione di errore. Questa condizione è chiamata eccezione.
Ecco quello che normalmente succede quando si innesca una eccezione:
- Lo stato corrente del codice viene salvato;
- L'esecuzione passa ad una predefinita funzione di gestione della eccezione;
- A seconda dei casi, l'handler può riesumare l'esecuzione dallo stato salvato, terminare lo script, o continuare da una differente locazione.
Mostreremo diversi metodi di gestione delle eccezioni:
- Basic;
- Custom;
- Multiple;
- Rilanciate;
- Top level;
Nota: Le eccezioni devono essere usate esclusivamente per la gestione delle condizioni di errore, e non per far saltare a piacere il flusso di esecuzione.
§ 1.2 Uso base
Quando una eccezione viene lanciata, il codice successivo al punto in cui l'eccezione è lanciata non sarà più eseguito, e PHP proverà a trovare il corrispondente blocco di cattura per quella specifica eccezione.
Se una eccezione non viene catturata, verrà generato un errore fatale con un messaggio di "Uncaught Exception".
Proviamo a lanciare una eccezione senza catturarla:
<?php
//create function with an exception
function checkNum($number) {
if($number>1) {
throw new Exception("Value must be 1 or below");
}
return true;
}
//trigger exception
checkNum(2);
?>
Lo script precedente provocherà un errore come questo:
Fatal error: Uncaught exception 'Exception'
with message 'Value must be 1 or below" in c:\webfolder\test.php:6
Stack trace: #0 c:\webfolder\test.php(12):
checkNum(28)#1 {main} thrown in c:\webfolder\test.php on line 6
§ 1.2.1 Try, throw and catch
Per evitare gli errori dell'esempio precedente, abbiamo bisogno dell'appropriato codice per gestire una eccezione. Questo dovrebbe includere:
- Try - una funzione che usa un eccezione dovrebbe essere in un blocco try. Se l'eccezione non viene innescata il codice continuerà normalmente. Se una eccezione viene innescata essa viene "lanciata";
- Throw - è come inneschi l'eccezione. Ogni "throw" deve avere almeno un "catch";
- Catch - un blocco "catch" cattura l'eccezione e crea un oggetto contenente le informazioni sull'eccezione.
Inneschiamo una eccezione in modo corretto:
<?php
//create function with an exception
function checkNum($number) {
if($number>1) {
throw new Exception("Value must be 1 or below");
}
return true;
}
//trigger exception in a "try" block
try {
checkNum(2);
//If the exception is thrown, this text will not be shown
echo 'If you see this, the number is 1 or below';
}
//catch exception
catch(Exception $e) {
echo 'Message: ' .$e->getMessage();
}
?>
Lo script precedente provocherà un errore come questo:
Message: Value must be 1 or below
Spiegazione dell'esempio
Lo scrip appena illustrato lancia e cattura una eccezione:
- La funzione checkNum() controlla se un numero è più grande di 1. Se così lancia un eccezione;
- La funzione checkNum() è chiamata in un blocco try;
- L'eccezione all'interno della funzione checkNum() viene lanciata;
- Il blocco catch cattura l'eccezione e crea l'oggetto $e contenente le informazioni sulla eccezione.
- Il messaggio di errore viene visualizzato con il metodo $e->gettMessage() dell'oggetto eccezione.
Esiste un metodo per aggirare la regola, "ogni eccezione deve avere un catch". E' quello di creare un top level exception handler. Lo vedremo nel prossimo paragrafo.
§ 1.3 Creare una classe personalizzata per la gestione delle eccezioni
Occorre estendere la classe exception:
<?php
class customException1 extends Exception {
public function errorMessage() {
//error message
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
$email = "someone@example...com";
try {
//check if
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
//throw exception if email is not valid
throw new customException1($email);
}
}
catch (customException1 $e) {
//display custom message
echo $e->errorMessage();
}
?>
La nuova classe è una copia della vecchia classe exception con in più la funzione errorMessage(). Eredita le proprietà ed i metodi della vecchia classe così che possiamo usare metodi come getLine(), getFile() e getMessage().
Spiegazione dell'esempio
Lo scrip appena illustrato lancia e cattura una eccezione con una classe exception personalizzata:
- La customException1 estende la class Exception ereditandone metodi e proprietà;
- La funzione errorMessage() restituisce un messaggio di errore se un indirizzo email non è valido;
- La variabile $email viene settata con un indirizzo email non valido;
- Il blocco try viene eseguito e lancia una eccezione dal momento che l'indirizzo email non è valido
- Il blocco catch cattura l'errore ed emette il messaggio di errore.
§ 1.4 Eccezioni multiple
<?php
class customException2 extends Exception {
public function errorMessage() {
//error message
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
$email = "someone@example.com";
try {
//check if
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
//throw exception if email is not valid
throw new customException2($email);
}
//check for "example" in mail address
if(strpos($email, "example") !== FALSE) {
throw new Exception("$email is an example e-mail");
}
}
catch (customException2 $e) {
echo $e->errorMessage();
}
catch(Exception $e) {
echo $e->getMessage();
}
?>
Spiegazione dell'esempio
Lo scrip appena illustrato lancia e cattura una eccezione con una classe exception personalizzata:
- La customException2 estende la class Exception ereditandone metodi e proprietà;
- La funzione errorMessage() restituisce un messaggio di errore se un indirizzo email non è valido;
- La variabile $email viene settata con un indirizzo email valido ma che contiene la stringa "example" ;
- Il blocco try viene eseguito e non lancia una eccezione sulla prima condizione.
- La seconda condizione testata nel blocco try lancia una eccezione sulla stringa "example"
- Il blocco catch cattura l'errore ed emette il messaggio di errore.
§ 1.5 Rilancio di eccezioni
A volte, quando una eccezione viene catturata, può essere necessario lanciare una nuova eccezione dal blocco catch.
<?php
class customException extends Exception {
public function errorMessage() {
//error message
$errorMsg = $this->getMessage().' is not a valid E-Mail address.';
return $errorMsg;
}
}
$email = "someone@example.com";
try {
try {
//check for "example" in mail address
if(strpos($email, "example") !== FALSE) {
//throw exception if email is not valid
throw new Exception($email);
}
}
catch(Exception $e) {
//re-throw exception
throw new customException($email);
}
}
catch (customException $e) {
//display custom message
echo $e->errorMessage();
}
?>
Spiegazione dell'esempio
Questa struttura mi è decisamente nuova:
- La customException estende la class Exception ereditandone metodi e proprietà;
- La funzione errorMessage() restituisce un messaggio di errore se un indirizzo email non è valido;
- La variabile $email viene settata con un indirizzo email valido ma che contiene la stringa "example" ;
- Il blocco try contiene un altro blocco try per rendere possibile il rilancio di una eccezione..
- Il blocco try lancia una eccezione sulla stringa "example".
- Il blocco catch cattura l'eccezione e rilancia una customException.
- La customException viene catturata ed emette il messaggio di errore.
§ 1.6 Impostare una top level exception handler
La funzione set_exception_handler() setta una funzione utente per gestire tutte le eccezioni non catturate.
<?php
function myException($exception) {
echo "<b>Exception:</b> " . $exception->getMessage();
}
set_exception_handler('myException');
throw new Exception('Uncaught Exception occurred');
?>
Lo script precedente emetterà un output come questo:
Exception: Uncaught Exception occurred
§ 1.7 Regole per le eccezioni
- Il codice può essere racchiuso in un blocco try, per aiutare a catturare potenziali eccezioni
- Ogni blocco try o "thrown" deve avere almeno un corrispondente blocco catch;
- Blocchi catch multipli possono essere usati per catturare differenti classi di eccezioni;
- Eccezioni possono essere lanciate o rilanciate in un blocco catch all'interno di un blocco try