Comment catcher les erreurs en PHP ?

Faire un rattrapage ou une sortie propre en cas de notice, warning ou fatal error.

15 mai 2014

Ce tutoriel a pour but d'expliquer comment attraper proprement les notices, warnings et erreurs qui peuvent survenir pendant vos programmes.

PHP dispose nativement de 15 types d'erreurs et du type E_ALL qui les regroupe tous. Nous allons faire en sorte d'envoyer une exception lorsque l'une de ces erreurs survient, ce qui nous permettra de l'intercepter, de la traiter et de continuer notre programme comme bon nous semble. Le tutoriel est divisé en deux parties car, comme toujours, il y a une exception : l'erreur fatale.

Catcher les notices, les warnings et les 12 autres types d'erreurs

PHP dispose d'une fonction qui permet d'enregistrer notre propre gestionnaire d'erreurs : set_error_handler. Il suffit donc de créer cette fonction et de l'enregistrer en tant que gestionnaire.

Générer une exception en cas d'erreur

/* Affichage de toutes les erreurs : pour les tests, à ne jamais mettre en production */
error_reporting(E_ALL);

/* Défini les exceptions qui devront être renvoyées */
class WarningException extends ErrorException{}
class ParseException extends ErrorException{}
class NoticeException extends ErrorException{}
class CoreErrorException extends ErrorException{}
class CoreWarningException extends ErrorException{}
class CompileErrorException extends ErrorException{}
class CompileWarningException extends ErrorException{}
class UserErrorException extends ErrorException{}
class UserWarningException extends ErrorException{}
class UserNoticeException extends ErrorException{}
class StrictException extends ErrorException{}
class RecoverableErrorException extends ErrorException{}
class DeprecatedException extends ErrorException{}
class UserDeprecatedException extends ErrorException{}

/* Gestionnaire d'erreurs PHP */
function informatixErrorHandler($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
        $errors = array(
                E_ERROR => 'Error',
                E_WARNING => 'Warning',
                E_PARSE => 'Parse',
                E_NOTICE => 'Notice',
                E_CORE_ERROR => 'CoreError',
                E_CORE_WARNING => 'CoreWarning',
                E_COMPILE_ERROR => 'CompileError',
                E_COMPILE_WARNING => 'CoreWarning',
                E_USER_ERROR => 'UserError',
                E_USER_WARNING => 'UserWarning',
                E_USER_NOTICE => 'UserNotice',
                E_STRICT => 'Strict',
                E_RECOVERABLE_ERROR => 'RecoverableError',
                E_DEPRECATED => 'Deprecated',
                E_USER_DEPRECATED => 'UserDeprecated'
        );

        /* Si une erreur inconnue survient (ne devrait jamais arriver) on renvoie une ErrorException */
        $errorName = (empty($errors[$err_severity])) ? 'Error' : $errors[$err_severity];

        $errorName .= 'Exception';

        /* Par exemple E_COMPILE_ERROR provoquera throw new CompileErrorException(...) */
        throw new $errorName($err_msg, 0, $err_severity, $err_file, $err_line);

        /* Aucun autre gestionnaire ne doit s'exécuter après celui là */
        return true;
}

/* Indique à PHP notre gestionnaire d'erreurs */
set_error_handler('informatixErrorHandler');

Maintenant que nous avons un gestionnaire enregistré, nous devons le tester. La fonction trigger_error permet de générer des erreurs de type E_USER_*, l'utilisation d'un attribut non-existant provoque une notice, une division par zéro provoque un warning et l'instanciation d'une classe non-existante provoque une fatal error. Lorsque l'on aura réussi à catcher une erreur, nous afficherons le nom de sa classe pour vérifier que nous avons renvoyé l'exception attendue.

Générer des notices, warnings et erreurs

/* Test E_USER_* */
$errors = array(
        E_USER_ERROR,
        E_USER_WARNING,
        E_USER_NOTICE,
        E_USER_DEPRECATED
);

foreach ($errors AS $errorCode)
{
        try
        {
                trigger_error("mon erreur", $errorCode);
        }
        catch (Exception $e)
        {
                var_dump(get_class($e));
        }
}

/* Test E_NOTICE */
try
{
        $o->toto;
}
catch (Exception $e)
{
        var_dump(get_class($e));
}

/* Test E_WARNING */
try
{
        1 / 0;
}
catch (Exception $e)
{
        var_dump(get_class($e));
}

/* Test E_ERROR */
try
{
        $o = new NonExistingClass();

}
catch (Exception $e)
{
        var_dump(get_class($e));
}

/* Affichage :
 * string(18) "UserErrorException"
 * string(20) "UserWarningException"
 * string(19) "UserNoticeException"
 * string(23) "UserDeprecatedException"
 * string(15) "NoticeException"
 * string(16) "WarningException"
 *
 * Fatal error: Class 'NonExistingClass' not found in ... on line ...
*/

Nous constatons que des exceptions sont bien lancées pour toutes les erreurs qui ont été attrapées, ce qui n'est pas le cas de l'erreur fatale.

Catcher les erreurs fatales

Malheureusement il n'est possible de catcher une fatal error que lors de l'extinction du script. Vous pourrez à ce moment lancer un traitement pour faire une jolie sortie applicative, relancer une application etc... Attention toutefois à ne pas lancer de boucle infini selon l'endroit où est situé l'erreur.

Intercepter une fatal error

function catchFatalError()
{
        /* retourne la dernière erreur qui est survenue */
        $last_error =  error_get_last();

        // Vérifie si la dernière erreur est une fatal error
        if(isset($last_error['type']) AND $last_error['type'] == E_ERROR)
        {
                /* traitement à réaliser, pour l'exemple nous ne ferons qu'afficher l'erreur */
                echo "L'erreur est : " . $last_error['message'] . PHP_EOL;
        }
}

register_shutdown_function('catchFatalError');

/* Affichage avec les mêmes tests que précédemment :
 * string(18) "UserErrorException"
 * string(20) "UserWarningException"
 * string(19) "UserNoticeException"
 * string(23) "UserDeprecatedException"
 * string(15) "NoticeException"
 * string(16) "WarningException"
 *
 * Fatal error: Class 'NonExistingClass' not found in ... on line ...
 * L'erreur est : Class 'NonExistingClass' not found
*/

Il n'est, à ma connaissance, pas possible de complètement catcher une erreur fatale : PHP continuera d'envoyer le message d'erreur quoi qu'il arrive. En revanche, si vous cachez toutes vos erreurs, il n'apparaîtra évidemment plus.

Ne pas afficher les erreurs PHP

error_reporting(~E_ALL);
ini_set('display_errors', false);

A bientôt !

Par
Créateur et administrateur.

Dans la même catégorie

Formater un tableau pour CURLOPT_POSTFIELDS
Email avec pièce jointe en PHP
PHP : modifier les attributs privés d'un objet
Tester l'existence d'un fichier dans l'include path
Convertir récursivement un objet PHP en tableau
PHP : formater un tableau en CSV
Comment envoyer un mail en ligne de commande ?
RSYNC : Comment synchroniser des fichiers à travers une connexion ssh ?
Exécuter un code PHP en ligne de commande
Doctrine 2 : générer les classes PHP depuis la base de données
Doctrine 2 : comment afficher la requête SQL ?
Comment construire une URL sans caractères spéciaux en PHP ?
Comment lister les fichiers PHP inclus sur ma page ?
Les fonctions anonymes récursives en PHP
Requête HTTP asynchrone en PHP
La résolution statique à la volée ou Late Static Bindings
Trouver les jours fériés français en PHP
Comment allumer son ordinateur à distance en PHP ?
Comment utiliser la balise meta viewport ?
Une version mobile de mon site avec le Zend Framework
Doctrine et le Zend Framework : Présentation, intégration et utilisation
Créer son flux RSS simplement avec Zend_Feed
Appliquer un layout sur un mail avec le Zend Framework
Comment lancer une requête multi-bases avec les fonctions MySQL ?
Implode / Explode : Du tableau à la chaine de caractères, de la chaine de caractères au tableau

Commentaire(s)