La corretta gestione degli errori è un aspetto tanto fondamentale quanto trascurato dello sviluppo software che fin troppo spesso viene affrontato troppo tardi o senza la dovuta attenzione. In questo articolo vedremo come gestire gli errori sfruttando i meccanismi delle Exception (eccezioni) di PHP.
Exception in PHP
PHP con la versione 5 ha acquisito la nozione di Exception prendendo spunto da altri linguaggi di programmazione object-oriented come Java o C#. Il meccanismo di base è il seguente:
try {
// (1) codice che lancia un'eccezione
throw new Exception('Errore di tipo bla bla bla', CODICE_ERRORE);
}
catch(Exception $e) {
// (2) gestione dell'eccezione
}
Quando una funzione PHP fallisce viene lanciata un’eccezione, ovvero un’istanza della classe Exception o una sua sotto-classe. Il lancio dell’eccezione può avvenire durante una chiamata ad una funzione di libreria o nel nostro stesso codice, utilizzando il comando throw come nella sezione (1) dell’esempio qui sopra.
Strutturando il codice in questo modo, è facile gestire molteplici situazioni di errore in un modo molto semplice e facilmente estendibile. È possibile infatti definire le proprie eccezioni estendendo la classe Exception ed utilizzando più di un blocco catch. Ipotizziamo ad esempio di dover gestire l’upload di file, per distinguere i vari errori potremmo definire queste eccezioni:
- ConfigurationException nel caso il server o l’applicazione non sia stata configurata correttamente per gestire l’upload;
- FileTooBigException nel caso l’utente abbia tentato di inviare un file troppo grande;
- WriteException nel caso non sia possibile scrivere sul filesystem, per problemi di permessi o altro.
Supponiamo inoltre che l’upload vero e proprio sia gestito da una funzione upload_file(); il codice che chiama la funzione avrà dunque questa forma:
try {
upload_file();
}
catch(ConfigurationException $e) {
echo 'Errore di configurazione';
}
catch(FileTooBigException $e) {
echo 'File troppo grande';
}
catch(WriteException $e) {
echo 'Impossibile scrivere su filesystem';
}
catch(Exception $e) {
echo 'Errore inaspettato: ' . $e->getMessage();
}
Nell’ultimo catch, come potete notare, catturiamo le eccezioni che non rientrano nella casistica sopra esposta; aggiungere un blocco catch(Exception $e) generico rappresenta quindi una “rete di protezione” efficace contro gli errori inaspettati.
Qualche consiglio utile
Saper catturare le eccezioni non basta, ma è necessario che l’applicazione stessa sia strutturata correttamente. In caso contrario, la gestione stessa degli errori potrebbe provocarne altri! Una situazione davvero pericolosa, soprattutto se la nostra applicazione è un gestionale.
Uno degli errori che si commettono più di frequente è il seguente:
try {
// codice che fallisce
$risultati = funzione_che_genera_eccezione();
}
catch(Exception $ex) {
// mostra messaggio d'errore
$errorMessage = "Si è verificato un errore: " . $ex->getMessage();
}
// SBAGLIATO: continuo come se non fosse successo nulla!
echo 'Risultato: ' . $risultati;
Qui lo sbaglio è che, nonostante l’eccezione sia stata catturata e l’errore sia stato notificato, l’applicazione agisce come se non ci sia stato alcun problema. A seconda dell’applicazione, questo potrebbe portare a:
- ulteriori eccezioni lanciate durante l’esecuzione della pagina, per variabili non inizializzate o con valori non corretti;
- l’utente potrebbe non accorgersi dell’errore e continuare a combinare danni.
Il pattern che vedremo ora è perfettamente lecito e si riferisce alla classica pagina PHP “tutto in uno”, solitamente presente in applicazioni molto semplici o siti web; questo tipo di pagina contiene sia il codice PHP per la logica di business che l’HTML per presentare i contenuti. Un modo molto efficace per struttura una pagina simile è di suddividere la logica dalla presentazione, in questo modo:
<?php
try {
/* (1) logica di business dell'applicazione:
* - lettura dei parametri;
* - esecuzione di funzioni;
* - lettura / scrittura da database;
* - ecc.
*/
}
catch(Exception $ex) {
// (2) gestione errori
$errorMessage = "Si è verificato un errore (" . $ex->getCode() . "):\n" . $ex->getMessage();
$errorMessage = nl2br(htmlspecialchars($errorMessage, ENT_NOQUOTES, 'UTF-8'));
}
?>
<html>
<head>
<title>Esempio gestione eccezioni</title>
</head>
<body>
<?php
if (isset($errorMessage)) {
?>
<div class="errorMessage">
<?php echo $errorMessage; ?>
</div>
<?php
}
else {
?>
<h3>Risultato dell'elaborazione</h3>
[...]
<?php
}
?>
</body>
In questo modo il codice PHP che si occupa della presentazione verrà eseguito solo se non si sono verificati errori; in caso contrario verrà mostrato un messaggio con i dettagli dell’eccezione verificasi.