web-dev-qa-db-fra.com

Gérer les erreurs fatales dans PHP en utilisant register_shutdown_function ()

Selon le commentaire sur cette réponse il est possible d'attraper les erreurs fatales via une fonction d'arrêt qui ne peut pas être interceptée en utilisant set_error_handler().

Cependant, je n'ai pas pu savoir comment déterminer si l'arrêt s'est produit en raison d'une erreur fatale ou de la fin du script.

De plus, les fonctions de débogage de la trace arrière semblent avoir disparu de la fonction d'arrêt, ce qui la rend pratiquement inutile pour la journalisation de la trace de la pile où l'erreur fatale s'est produite.

Ma question est donc la suivante: quelle est la meilleure façon de réagir aux erreurs fatales (en particulier les appels de fonction non définis) tout en gardant la possibilité de créer une trace appropriée?

36
ThiefMaster

Cela fonctionne pour moi:

function shutdown() {
    $error = error_get_last();
    if ($error['type'] === E_ERROR) {
        // fatal error has occured
    }
}

register_shutdown_function('shutdown');

spl_autoload_register('foo'); 
// throws a LogicException which is not caught, so triggers a E_ERROR

Cependant, vous le savez probablement déjà, mais juste pour être sûr: vous ne pouvez pas récupérer d'une E_ERROR en aucune façon.

En ce qui concerne la trace, vous ne pouvez pas ... :( Dans la plupart des cas d'erreur fatale, en particulier les erreurs fonction non définie, vous n'en avez pas vraiment besoin. Identifier le fichier/la ligne où elle s'est produite est suffisant. Le retour en arrière n'est pas pertinent dans ce cas.

65
netcoder

Une façon de distinguer l'erreur fatale et l'arrêt correct de l'application avec la fonction register_shutdown_function consiste à définir une constante comme dernière ligne de votre programme, puis à vérifier si la constante est définie:

function fatal_error() {
if ( ! defined(PROGRAM_EXECUTION_SUCCESSFUL)) {
        // fatal error has occurred
    }
}

register_shutdown_function('fatal_error');

define('PROGRAM_EXECUTION_SUCCESSFUL', true);

Si le programme arrive à sa fin, il ne pourrait pas avoir rencontré d'erreur fatale, nous savons donc ne pas exécuter la fonction si la constante est définie.

error_get_last () est un tableau avec toutes les informations concernant l'erreur fatale que vous devriez avoir besoin de déboguer, bien qu'il n'ait pas de trace arrière, comme cela a été mentionné.

Généralement, si votre programme php a rencontré une erreur fatale (par opposition à une exception), vous voulez que le programme explose afin que vous puissiez trouver et résoudre le problème. J'ai trouvé register_shutdown_function utile pour les environnements de production où vous souhaitez que le rapport d'erreurs soit désactivé, mais voulez un moyen de consigner l'erreur en arrière-plan afin de pouvoir y répondre. Vous pouvez également utiliser la fonction pour diriger l'utilisateur vers une page html conviviale en cas d'une telle erreur afin de ne pas simplement servir une page vierge.

10
Gabriel

Juste une bonne astuce pour obtenir la méthode error_handler actuelle =)

<?php
register_shutdown_function('__fatalHandler');
function __fatalHandler()
{
    $error      = error_get_last();

    //check if it's a core/fatal error, otherwise it's a normal shutdown
    if($error !== NULL && $error['type'] === E_ERROR) {
        //Bit hackish, but the set_exception_handler will return the old handler
        function fakeHandler() { }
        $handler = set_exception_handler('fakeHandler');
        restore_exception_handler();
        if($handler !== null) { 
            call_user_func($handler, new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
        }
        exit;
    }
}
?>

Je ne veux pas non plus noter que si vous appelez

<?php
ini_set('display_errors', false);
?>

Php arrête d'afficher l'erreur, sinon le texte d'erreur sera envoyé au client avant votre gestionnaire d'erreurs

6
Sander Visser