Gestione errori in PHP

Gestione errori

La programmazione è una tra le attività più complesse che esistano e come tante altre è soggetta ad errori. Di conseguenza è fondamentale imparare a gestire gli errori nel modo corretto.

In questa guida vedremo quali sono le tipologie di errori di PHP e impareremo ad utilizzare in modo efficiente le funzioni di gestione degli errori del linguaggio.

Cause errori

Esistono diversi motivi per cui un'applicazione genera errori, ad esempio:

  • Un file che si sta tentando di aprire è inesistente.
  • La variabile utilizzata non è stata ancora definita e/o inizializzata.
  • La funzione utilizzata dal programma non esiste
  • L'applicazione non dispone delle autorizzazioni necessarie per scrivere un file su disco
  • Un servizio a cui l'applicazione deve accedere risulta temporaneamente non disponibile

Questi tipi di errori sono noti come errori a runtime, poiché si verificano nel momento in cui viene eseguito lo script. Gli errori di sintassi invece, come ad esempio l'omissione di un punto e virgola (;) per indicare la fine di un'istruzione PHP, devono essere corretti prima che lo script possa essere eseguito.

Un'applicazione professionale deve avere le capacità per gestire tali errori, in modo tale da informare l'utente del problema nel modo più chiaro e preciso possibile.

Tipi di errore

PHP genera diverse tipologie di errore che possiamo suddividere in:

  • errori fatali: errori critici, interrompono l'esecuzione del programma
  • parse error: errori di sintassi, interrompono l'esecuzione del programma
  • warning error: errori generati a runtime, non interrompono l'esecuzione del programma
  • notice: simili ai warning, non interrompono l'esecuzione del programma

Gli errori fatali sono gli errori più gravi perché bloccano l'esecuzione dell'applicazione. Un tipico caso di errore fatale viene rilevato quando tentiamo di chiamare una funzione che non è stata definita:

<?php
$string = 'test';

echo test($string);

// Fatal error: Uncaught Error: Call to undefined function test()

I parse error, come gli errori fatali, bloccano l'esecuzione dello script e riguardano nello specifico un errore di sintassi. Riprendendo l'esempio precedente:

<?php
$string = 'test';

echo test($string)

// Parse error: syntax error, unexpected end of file, expecting ';' or ',' 

In questo caso manca il punto e virgola alla riga 4 che indica all'interprete PHP la fine dell'istruzione.

I warning error non bloccano l'esecuzione dello script. Questo tipo di errore può verificarsi ad esempio quando si cerca di includere un file inesistente:

<?php
include 'functions.php';

// Warning: include(functions.php): failed to open stream: No such file or directory

Di seguito è riportato l'elenco di tutte le tipologie di errori che possiamo riscontrare in PHP, a ciascuna costante corrisponde un valore numerico

Costante Valore numerico Descrizione
E_ERROR 1 Errore fatale a runtime. L'esecuzione dello script viene interrotta.
E_WARNING 2 Warning a runtime. L'esecuzione dello script non viene interrotta.
E_PARSE 4 Errore di sintassi. L'esecuzione dello script viene interrotta.
E_NOTICE 8 Messaggio di errore a runtime, indica che l'interprete ha rilevato qualcosa che potrebbe essere un errore. Non blocca l'esecuzione dello script.
E_CORE_ERROR 16 Errore fatale che si verifica durante l'avvio iniziale del motore PHP.
E_CORE_WARNING 32 Warning che si verifica durante l'avvio iniziale del motore PHP.
E_COMPILE_ERROR 64 Errore fatale generato dallo Zend Scripting Engine che si verifica durante la compilazione dello script.
E_COMPILE_WARNING 128 Warning generato dallo Zend Scripting Engine che si verifica durante la compilazione dello script.
E_USER_ERROR 256 Messaggio di errore generato dall'utente tramite la funzione trigger_error() piuttosto che dal motore PHP.
E_USER_WARNING 512 Warning generato dall'utente tramite la funzione trigger_error() piuttosto che dal motore PHP.
E_USER_NOTICE 1024 Notice generato dall'utente tramite la funzione trigger_error() piuttosto che dal motore PHP.
E_STRICT 2048 Generato ogni volta che PHP incontra del codice che potrebbe causare problemi o incompatibilità
E_RECOVERABLE_ERROR 4096 Un errore fatale recuperabile, ossia che può essere gestito a livello utente tramite la funzione set_error_handler()
E_DEPRECATED 8192 Warning a runtime che indica l'utilizzo di una funzione deprecata, ossia che non funzionerà più nelle versioni future di PHP
E_USER_DEPRECATED 16384 Warning generato a livello utente tramite trigger_error() per indicare l'utilizzo di funzioni deprecate
E_ALL 32767 Tutti gli errori e gli avvisi di PHP

La funzione error_reporting()

Le costanti o i rispettivi valori numerici appena visti possono essere utilizzati come argomento per la funzione error_reporting() del linguaggio. Questa funzione consente di configurare PHP per fare generare o meno una determinata tipologia di errori in base al valore passato. Un valore basso corrisponde ad un errore più critico.

Vediamo subito un esempio di utilizzo della funzione error_reporting():

<?php
error_reporting(E_NOTICE);

$number1 = 10;
echo $number2; //Notice: Undefined variable: number2
echo sum($number1, $number2);

L'esecuzione dello script genererà solamente un warning per la riga 5 poiché abbiamo abilitato la visualizzazione degli errori di tipo E_NOTICE o superiori.

È possibile indicare una combinazione di costanti tramite l'and (&) oppure l'or (|), possiamo quindi scrivere

<?php
error_reporting(E_ERROR | E_WARNING);

per comunicare a PHP di abilitare solo errori fatali e warning.

Nota: la funzione error_reporting() non consente di disabilitare gli errori di tipo E_PARSE, poiché sono errori che vengono rilevati prima dell'esecuzione dello script (es. omissione del punto e virgola alla fine di un'istruzione).

Disabilitare visualizzazione errori

Per disabilitare la visualizzazione degli errori all'utente è possibile impostare la variabile di configurazione display_errors a false oppure a 0 tramite la funzione ini_set():

ini_set("display_errors", false); // equivalente a ini_set("display_errors", 0);

Con questo codice gli errori non verranno mostrati nella pagina HTML. Ciò è molto importante in fase di produzione poiché evitiamo di mostrare all'utente informazioni sensibili, mentre in fase di sviluppo o testing lasciare abilitata la visualizzazione degli errori è utile per la fase di debugging.

La funzione exit()

Introduciamo adesso una delle funzioni PHP più utilizzate nelle applicazioni web, la funzione exit(). Il suo utilizzo più comune è quello di mostrare un messaggio a video e terminare immediatamente l'esecuzione di dello script PHP, questo significa che qualsiasi ulteriore istruzione successiva non verrà eseguita.

exit($status)

Più precisamente si tratta di un costrutto del linguaggio, quindi può essere utilizzato anche senza parantesi se non viene specificato il parametro opzionale $status.

Consideriamo adesso questo esempio di codice

<?php
// Proviamo ad aprire il file file.txt
$file = fopen("file.txt", "r");

Stiamo semplicemente tentando di aprire il file file.txt in lettura. Se tale file non esiste, lo script genera il seguente avviso

Warning: fopen(file.txt): failed to open stream: No such file or directory in ... on line 3

Oltre a non fornire una buona esperienza d'uso all'utente, questa tipologia di errore mostra il percorso dove è memorizzato lo script, rivelando quindi un'informazione sensibile che rappresenta un potenziale problema dal punto di vista della sicurezza.

Vediamo adesso come la funzione exit() può migliorare un pochino l'esperienza utente, partendo dall'esempio precedente possiamo scrivere

<?php
// Proviamo ad aprire il file file.txt
$file_path = "file.txt";

if (!file_exists($file_path)) {
    exit("Il file $file_path non esiste");
}
$file = fopen("file.txt", "r");

In questo caso la funzione file_exists() ci consente di verificare l'esistenza del file ritornando true in caso di successo, false altrimenti. L'utente quindi visualizza uno specifico errore impostato dal programmatore piuttosto che un errore di sistema contenente informazioni sensibili.

Nota: la funzione exit() dovrebbe essere utilizzata dal programmatore prevalentemente per fare debugging in fase di sviluppo o testing.

Un altro utilizzo comune della funzione exit() è insieme alla funzione header(), in particolare nel redirezionare l'utente da una pagina all'altra.

Vediamo un esempio per capire meglio

<?php
session_start();

if (!isset($_SESSION['is_logged'])) {
  header("Location: login.php");
  exit;
}

Lo script verifica semplicemente se la variabile di sessione $_SESSION['is_logged'] è inizializzata, in caso contrario l'utente viene redirezionato alla pagina login.php.

L'istruzione alla riga 6 consente di interrompere immediatamente l'esecuzione dello script, eventuali righe di codice successive alla riga 7 non vengono eseguite.

La funzione set_error_handler()

È possibile creare la propria funzione per gestire gli errori generati in fase di esecuzione dal motore PHP. Questo gestore di errori personalizzato offre una maggiore flessibilità e un migliore controllo sugli errori, è possibile ispezionare l'errore e decidere se mostrare un messaggio all'utente, registrare l'errore in un file o database o inviarlo tramite email. È inoltre possibile aggiungere ulteriori informazioni come la data e l'ora dell'evento. 

La funzione di gestione degli errori definita dall'utente accetta diversi parametri tra cui due obbligatori, $errno ed $errstr e due opzionali, $errfile e $errline che vengono descritti di seguito:

Parametro Descrizione
$errno Specifica il livello dell'errore, come numero intero corrispondente alla relativa costante (E_ERROR, E_WARNING, etc...)
$errstr Specifica il messaggio di errore come stringa
$errfile Specifica il nome file del file di script in cui si è verificato l'errore, sotto forma di stringa
$errline Specifica il numero di riga in cui si è verificato l'errore, sotto forma di stringa

Vediamo adesso un esempio di funzione di gestione degli errori personalizzata tramite l'utilizzo di set_error_handler(), una funzione che consente di specificare un codice PHP alternativo per la gestione degli errori consentendo di personalizzare la risposta visualizzata dall'utente:

<?php
function my_custom_error($errno, $errstr, $errfile, $errline)
{
    printf(
        "%s<br>Errore [%s]: %s<br>Percorso file: %s<br>Riga n. %s",
        date("d/m/Y H:i:s"),
        $errno,
        $errstr,
        $errfile,
        $errline
    );
}
 
set_error_handler("my_custom_error");
 
echo $test;

Il codice precedente, salvato dentro un file error-handler.php, genera l'errore

03/01/2023 13:44:14
Errore [8]: Undefined variable: test
Percorso file: C:\xampp\htdocs\error-handler.php
Riga n. 16

Log degli errori

PHP può essere configurato in modo tale da memorizzare gli errori dentro un file di log specificato dal programmatore. Tale file è indicato nella direttiva error_log del file php.ini, che di default risulta vuota.

Anche in questo caso possiamo utilizzare la funzione ini_set() per impostare un file di log per la nostra applicazione.

Ad esempio, tramite il seguente codice:

<?php
ini_set('error_log', 'logs/error.log');

impostiamo logs/error.log come percorso dove verranno salvati gli errori.

Ogni eventuale messaggio di errore, che sia un warning o un errore fatale, verrà riportato dentro il file error.log, a patto che la directory logs sia presente.

Eseguendo il seguente codice:

<?php
ini_set('error_log', 'logs/error.log');

echo $test;

dovremmo ottenere un notice dentro il file error.log che recita più o meno così

[02-Jan-2023 23:29:09 Europe/Rome] PHP Notice:  Undefined variable: test in ... on line 4

Ulteriori errori generati dall'applicazione vengono accodati ai precedenti. Come possiamo notare, oltre alla a data e ora dell'evento abbiamo la timezone (Europe/Rome) che determina l'impostazione del fuso orario del server dove gira l'applicazione PHP.

Il file di log può essere utilizzato anche per memorizzare errori o messaggi generati dall'applicazione mediante l'uso della funzione error_log(), la cui sintassi è la seguente:

error_log($message, $type, $dest, $headers)

$message è il messaggio di errore, $type è un numero intero che indica il canale dove inviare l'errore. I parametri $dest e $headers sono opzionali e il primo va usato nel caso in cui $type = 1 oppure 3, il secondo nel caso in cui $type = 1.

La seguente tabella elenca i possibili valori per il parametro $type

Valore Descrizione
0 Il messaggio di errore viene salvato nel file di log di default. Nei sistemi operativi Unix si trova dentro /var/log/apache2/error.log
1 Il messaggio di errore viene inviato per email. Il parametro $dest contiene l'indirizzo email di destinazione e $headers eventuali intestazioni da aggiungere all'email
2 Non più utilizzabile
3 Il messaggio di errore viene salvato nel file specificato nel parametro $dest. Ulteriori messaggi vengono accodati ai precedenti.
4 Il messaggio di errore viene inviato al modulo SAPI del web server

La funzione error_log() ritorna true in caso di successo, false altrimenti.

Nota: $message non può contenere il carattere null perché il messaggio verrà troncato, essendo questo carattere interpretato da PHP come terminatore. Per evitare questo inconveniente, prima di chiamare la funzione error_log(), è possibile convertire il messaggio in base64 con la funzione base64_encode() o in URL-encode tramite la funzione rawurlencode() oppure utilizzare addlashes() per effettuare un'escape dei seguenti caratteri: ', ", \ e carattere null.

Oltre alla funzione error_log(), PHP mette a disposizione la funzione trigger_error() che genera un messaggio di errore/avviso a livello utente.

La sintassi della funzione trigger_error() è la seguente:

trigger_error($message, $error_level)

dove $message è il messaggio di errore da generare e $error_level è un intero che indica la tipologia dell'errore. Di default viene generato un errore di tipo utente E_USER_NOTICE.

Nella prossima guida introdurremo le eccezioni, un modo per intercettare e gestire a livello di codice un possibile comportamento inatteso di un'applicazione.

Guida successiva: Eccezioni PHP

Sviluppatore PHP Freelance

Stai cercando uno sviluppatore PHP? Ho oltre 10 anni di esperienza nello sviluppo web con questo linguaggio.

Contattami adesso!