Cette semaine, nous allons nous pencher sur la problématique de la gestion d'erreurs en PHP.

Que celui, ou celle, qui n'a jamais eut à se poser la question de la manière dont il devait gérer les erreurs dans une application lève le doigt.

PHP propose en natif, un système intéressant de gestion d'erreur utilisateur.

1 - Principes :

- PHP propose un gestionnaire d'erreur natif permettant de gérer les modalités de traitement lorsqu'une erreur est rencontrée.
- Il est possible de gérer différents niveaux de retours d'erreurs en plus des niveaux classiques de PHP. En pratique, trois niveaux spécifiques sont dédiés pour les utilisateurs.
- La limite du système est de ne pas pouvoir intervenir si une erreur est rencontrée avant la phase d'execution du code (i.e Parse Error, ou Fatal Error par exemple), par contre les Notices et Warnings sont très bien traités.


2 - Intérêts de la chose :

- Il est possible via ce système de gérer facilement des systèmes de logs et/ou d'alertes mails, dès lors qu'une erreur, ou un certains type d'erreur est rencontré.
- L'on peut également proposer un affichage graphique uniforme pour tout ou partie des erreurs rencontrées lors de l'utilisation de l'application par les utilisateurs finaux.
- Enfin l'on peut également s'en servir pour faciliter le débuggage en cours de développement, en gérant des affichages de contextes d'execution du scripts (imbrication de fonctions, état des variables définies...).

3 - Exemple simple de mise en oeuvre :

<?php

set_error_handler('my_error_handler');

function my_error_handler($errno, $errstr, $errfile, $errline){

    // définition des type d'erreurs disponibles
    $error_msg = array(
        1 => 'E_ERROR',
        2 => 'E_WARNING',
        4 => 'E_PARSE',
        8 => 'E_NOTICE',
        16 => 'E_CORE_ERROR',
        32 => 'E_CORE_WARNING',
        64 => 'E_COMPILE_ERROR',
        128 => 'E_COMPILE_WARNING',
        256 => 'E_USER_ERROR',
        512 => 'E_USER_WARNING',
        1024 => 'E_USER_NOTICE'
    );

    // pour une sortie HTML décommenter la ligne suivante 
    // echo '<pre>';

    // affichage classique d'une ligne d'erreur
    printf("[%s] %s in : %s, line : %s.\n", $error_msg[$errno], $errstr, $errfile, $errline);

    // pour une sortie HTML décommenter la ligne suivante 
    // echo '</pre>';

   // Permet de stopper l'execution pour certains types d'erreurs
   if($errno & 245) die("Abort\n"); // 245 = (1+4+16+32+64+128)

}

user_error("Test d'une erreur utilisateur", E_USER_ERROR);

?>
On voit avec cet exemple qu'il suffit de déclarer en tête de script le nom de la fonction servant de gestionnaire d'erreur, via set_error_handler. Puis de définir cette fonction, à l'intérieur de laquelle on va traiter les différentes erreurs, dans cet exemple, on se contente de générer un affichage du type d'erreur, avec la ligne et le fichier associé.

Il ne faut pas non plus oublier de stopper l'éxecution pour les types d'erreurs normallement bloquant en PHP (E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING). Dans la pratique seuls les deux premiers sont les plus récurents, et en environnement de production ils ne devraient bien sûr plus être présents.
Enfin, il suffit d'appeler via user_error le gestionnaire d'erreur quelque part dans un script pour pouvoir l'utiliser.

4 - Quelques subtilités du système :

Il est possible de récupérer en dernier argument du gestionnaire d'erreur une variable contenant le contexte (variables définies). Le prototype du gestionnaire d'erreur devient :

function my_error_handler($errno, $errstr, $errfile, $errline, $context){
Et via le code suivant à insérer dans le gestionnaire lui-même, il est par exemple possible d'afficher l'ensemble des paramètres d'entrés du script :


 // Affichage des paramètres d'entrées du script :
    $report = array('_FILES', '_REQUEST', '_COOKIE', '_GET', '_POST');
    foreach($report as $report_item){
        if(!empty($context[$report_item])){
            echo "$report_item --> ";
            print_r($context[$report_item]);
            echo "\n";
        }
    }

Il est également intérressant de savoir que d'une manière générale, le système d'error reporting est inopérant dès lors que l'on utilise un gestionnaire d'erreur. Ainsi toutes les erreurs (même les Notices) sont transmises au gestionnaires.
Si vous ne voulez pas faire remonter certaines erreurs il vous suffit de placer un traitement conditionnel dans le gestionnaire faisant office de filtre.

5 - Pour aller plus loin :

Le chapitre de la documentation officielle PHP sur la gestion d'erreurs
Pour approfondir les mécanismes de gestion d'erreur et notament les notions de logs et d'alertes mails
Exemple d'utilisation du gestionnaire d'erreur couplé avec Xdebug pour une utilisation lors des phases de développement (pdf)