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.