Comment attraper une erreur PHP fatale (`E_ERROR`)?

557

Je peux utiliser set_error_handler()pour attraper la plupart des erreurs PHP, mais cela ne fonctionne pas pour les E_ERRORerreurs fatales ( ), telles que l'appel d'une fonction qui n'existe pas. Existe-t-il un autre moyen de détecter ces erreurs?

J'essaie d'appeler mail()pour toutes les erreurs et j'exécute PHP 5.2.3.

trop de php
la source
J'ai rédigé un Q&A de style wiki avec une solution complète pour intercepter toutes les erreurs en PHP; qui peuvent être consultés / glanés / volés / critiqués ici sur Stack Overflow . La solution comprend cinq méthodes qui encapsulent toutes les erreurs que PHP peut générer et qui transmettront éventuellement ces erreurs à un objet typé «ErrorHandler».
DigitalJedi805
Voir aussi: bugs.php.net/bug.php?id=41418
dreftymac
Voir aussi: stackoverflow.com/questions/7116995
dreftymac

Réponses:

635

Consigner les erreurs fatales en utilisant le register_shutdown_function, qui nécessite PHP 5.2+:

register_shutdown_function( "fatal_handler" );

function fatal_handler() {
    $errfile = "unknown file";
    $errstr  = "shutdown";
    $errno   = E_CORE_ERROR;
    $errline = 0;

    $error = error_get_last();

    if($error !== NULL) {
        $errno   = $error["type"];
        $errfile = $error["file"];
        $errline = $error["line"];
        $errstr  = $error["message"];

        error_mail(format_error( $errno, $errstr, $errfile, $errline));
    }
}

Vous devrez définir les fonctions error_mailet format_error. Par exemple:

function format_error( $errno, $errstr, $errfile, $errline ) {
    $trace = print_r( debug_backtrace( false ), true );

    $content = "
    <table>
        <thead><th>Item</th><th>Description</th></thead>
        <tbody>
            <tr>
                <th>Error</th>
                <td><pre>$errstr</pre></td>
            </tr>
            <tr>
                <th>Errno</th>
                <td><pre>$errno</pre></td>
            </tr>
            <tr>
                <th>File</th>
                <td>$errfile</td>
            </tr>
            <tr>
                <th>Line</th>
                <td>$errline</td>
            </tr>
            <tr>
                <th>Trace</th>
                <td><pre>$trace</pre></td>
            </tr>
        </tbody>
    </table>";
    return $content;
}

Utilisez Swift Mailer pour écrire la error_mailfonction.

Voir également:

user259973
la source
113
+1 Ceci est la vraie réponse correcte. Je ne sais pas pourquoi les gens sont accrochés à "vous ne pouvez pas récupérer d'erreurs fatales" - la question n'a rien dit sur la récupération.
David Harkness
21
Merci bien. La récupération à partir d'erreurs fatales (limites de mémoire par exemple) n'est pas quelque chose que j'essaierais de faire, mais rendre ces erreurs détectables (sans que le client soumette un ticket de support) fait toute la différence.
Ilija
2
Utilisation du courrier de base:mail("[email protected]", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
Eric Muyser
4
@ScottNicol Slava V est correct, car la fonction d'arrêt est appelée à chaque fois que le script se termine. Avec la façon dont le code est écrit maintenant, un email sera envoyé à chaque chargement de page.
Nate
2
Remarque: ce n'est pas une réponse correcte à 100%. N'importe quel endroit qui utilise un symbole @ pour ignorer les erreurs définira toujours la dernière erreur (afin que vous puissiez gérer les erreurs). Votre script se termine donc sans problème, mais la fonction register_shutdown_function pense toujours qu'une erreur s'est produite. Ce n'est que depuis PHP 7 qu'ils ont une fonction error_clear_last ().
Rahly
150

Je viens de proposer cette solution (PHP 5.2.0+):

function shutDownFunction() {
    $error = error_get_last();
     // Fatal error, E_ERROR === 1
    if ($error['type'] === E_ERROR) {
         // Do your stuff
    }
}
register_shutdown_function('shutDownFunction');

Différents types d'erreur sont définis dans Constantes prédéfinies .

periklis
la source
25
Cette solution fait beaucoup plus pour moi que la réponse la mieux notée. La réponse la mieux notée vous enverra un e-mail à chaque exécution du script, même en l'absence d'erreur. Celui-ci fonctionne strictement sur une erreur fatale.
kmoney12
@periklis, si la dernière erreur était déjà gérée, error_get_last retournerait quand même ce n'est-ce pas?
Pacerier
@Pacerier Je ne sais pas trop ce que vous voulez dire par "manipulé", car les erreurs ne sont pas des exceptions, mais je suppose que la réponse est "oui"
periklis
3
@Pacerier je vois, c'est une question intéressante. Jetez un oeil à php.net/error_get_last , l'un des commentaires mentionne que " If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function."
periklis
1
C'est peut-être évident, l'appel register_shutdown_function()doit être antérieur à toute erreur fatale. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');ne fonctionnera pas comme prévu.
Nobu
117

PHP ne fournit pas de moyens conventionnels pour détecter et récupérer des erreurs fatales. En effet, le traitement ne doit généralement pas être récupéré après une erreur fatale. Une chaîne correspondant à un tampon de sortie (comme suggéré par le post original, la technique décrite sur PHP.net) est définitivement mal avisée. Ce n'est tout simplement pas fiable.

L'appel de la fonction mail () à partir d'une méthode de gestion d'erreurs s'avère également problématique. Si vous aviez beaucoup d'erreurs, votre serveur de messagerie serait chargé de travail et vous pourriez vous retrouver avec une boîte de réception noueuse. Pour éviter cela, vous pouvez envisager d'exécuter un cron pour analyser régulièrement les journaux d'erreurs et envoyer des notifications en conséquence. Vous voudrez peut-être également examiner les logiciels de surveillance du système, tels que Nagios .


Pour parler un peu de l'enregistrement d'une fonction d'arrêt:

Il est vrai que vous pouvez enregistrer une fonction d'arrêt, et c'est une bonne réponse.

Le point ici est que nous ne devrions généralement pas essayer de récupérer des erreurs fatales, surtout pas en utilisant une expression régulière contre votre tampon de sortie. Je répondais à la réponse acceptée , qui était liée à une suggestion sur php.net qui a depuis été modifiée ou supprimée.

Cette suggestion était d'utiliser une expression régulière contre le tampon de sortie lors de la gestion des exceptions, et dans le cas d'une erreur fatale (détectée par la correspondance avec le texte d'erreur configuré que vous attendez), essayez de faire une sorte de récupération ou de traitement continu. Ce ne serait pas une pratique recommandée (je crois que c'est pourquoi je ne trouve pas la suggestion originale aussi. Je l'ignore ou la communauté php l'a abattue).

Il convient de noter que les versions les plus récentes de PHP (environ 5.1) semblent appeler la fonction d'arrêt plus tôt, avant que le rappel de la mise en mémoire tampon de sortie ne soit invoqué. Dans la version 5 et les versions antérieures, cet ordre était l'inverse (le rappel de mise en mémoire tampon de sortie était suivi de la fonction d'arrêt). De plus, depuis environ 5.0.5 (qui est beaucoup plus tôt que la version 5.2.3 de l'interrogateur), les objets sont déchargés bien avant l'appel d'une fonction d'arrêt enregistrée, vous ne pourrez donc pas compter sur vos objets en mémoire pour le faire. beaucoup de tout.

L'enregistrement d'une fonction d'arrêt est donc correct, mais le type de tâches qui devraient être effectuées par une fonction d'arrêt est probablement limité à une poignée de procédures d'arrêt en douceur.

La clé à retenir ici n'est que quelques mots de sagesse pour quiconque bute sur cette question et voit les conseils dans la réponse initialement acceptée. Ne pas regex votre tampon de sortie.

keparo
la source
25
Pfff, je me souviens de ces 650 000 e-mails reçus le lendemain matin. Depuis lors, mon ErrorHandler est plafonné à 100 e-mails par serveur Web.
Bob Fanger
14
Ce n'est pas vrai. Vous pouvez capturer des erreurs fatales avec register_shutdown_function.
hipertracker
56
Il existe des cas d'utilisation pour vouloir attraper des erreurs fatales. Les suites de tests, par exemple, ne doivent pas simplement s'arrêter en cas d'échec, elles doivent signaler l'erreur fatale et passer au test suivant. PHP fait juste trop de choses des erreurs "fatales".
Chad
24
Ouais dire qu'ils "ne devraient pas être pris" est très myope. Dans un système de production, vous devez savoir quand quelque chose échoue (configurer des e-mails ou enregistrer des choses dans une base de données - la gestion des erreurs php par défaut n'est pas très sophistiquée).
BT
8
Je veux faire un bref commentaire sur ce que vous dites tous sur "Les erreurs doivent être détectées, afin que nous puissions les corriger" ... Directives Ini ini log_errors et error_log.
Kelly Elton,
37

Eh bien, il semble possible d'attraper des erreurs fatales d'une autre manière :)

ob_start('fatal_error_handler');

function fatal_error_handler($buffer){
    $error = error_get_last();
    if($error['type'] == 1){
        // Type, message, file, line
        $newBuffer='<html><header><title>Fatal Error </title></header>
                      <style>
                    .error_content{
                        background: ghostwhite;
                        vertical-align: middle;
                        margin:0 auto;
                        padding: 10px;
                        width: 50%;
                     }
                     .error_content label{color: red;font-family: Georgia;font-size: 16pt;font-style: italic;}
                     .error_content ul li{ background: none repeat scroll 0 0 FloralWhite;
                                border: 1px solid AliceBlue;
                                display: block;
                                font-family: monospace;
                                padding: 2%;
                                text-align: left;
                      }
                      </style>
                      <body style="text-align: center;">
                        <div class="error_content">
                             <label >Fatal Error </label>
                             <ul>
                               <li><b>Line</b> ' . $error['line'] . '</li>
                               <li><b>Message</b> ' . $error['message'] . '</li>
                               <li><b>File</b> ' . $error['file'] . '</li>
                             </ul>

                             <a href="javascript:history.back()"> Back </a>
                        </div>
                      </body></html>';

        return $newBuffer;
    }
    return $buffer;
}
sakhunzai
la source
3
Je donnerais ces 10 votes positifs si je le pouvais. Cela fonctionne parfaitement pour moi sur ces erreurs étranges qui se produisent parfois lorsqu'une page se bombe et que rien n'est enregistré. Je ne l'utiliserais pas dans le code de production en direct, mais c'est génial d'ajouter à une page lorsqu'une réponse rapide à ce qui échoue est nécessaire. Je vous remercie!
Night Owl
L'une des meilleures solutions que j'ai trouvées sur Internet. Fonctionne comme un charme.
Bounce
De quelle manière? Une explication serait de mise, surtout si c'est l'une des meilleures solutions sur Internet (elle pourrait devenir encore meilleure).
Peter Mortensen
Par exemple, tout le contenu CSS est-il nécessaire? Ne pourrait-il pas être réduit à l'essentiel? Répondez en modifiant votre réponse, pas ici dans les commentaires (le cas échéant).
Peter Mortensen
@PeterMortensen Je ne revendique pas son meilleur. C'est aussi ma solution personnelle au problème, il existe d'autres meilleures options, beaucoup plus professionnelles. Comme l'a suggéré quelqu'un, ce n'est pas bon pour la production. Css is there bcz Je viens de couper mon code personnel
sakhunzai
36

Les erreurs fatales ou des erreurs fatales récupérables jettent maintenant les instances de Erroren PHP 7 ou versions supérieures . Comme toutes les autres exceptions, les Errorobjets peuvent être interceptés à l'aide d'un try/catchbloc.

Exemple:

<?php
$variable = 'not an object';

try {
    $variable->method(); // Throws an Error object in PHP 7 or higger.
} catch (Error $e) {
    // Handle error
    echo $e->getMessage(); // Call to a member function method() on string
}

https://3v4l.org/67vbk

Ou vous pouvez utiliser l' Throwableinterface pour intercepter toutes les exceptions.

Exemple:

<?php
    try {
        undefinedFunctionCall();
    } catch (Throwable $e) {
        // Handle error
        echo $e->getMessage(); // Call to undefined function undefinedFunctionCall()
    }

https://3v4l.org/Br0MG

Pour plus d'informations: http://php.net/manual/en/language.errors.php7.php

LugiHaue
la source
2
Une idée sur la façon de l'utiliser pour détecter une erreur comme Fatal error: Trait 'FailedTrait' not found inlors de l'utilisation ReflectionClass?
TCB13
1
@ TCB13 essayez d'envelopper le contenu interne de l'essai dans un fichier et à la include "filename.php"place dans le trybloc, puis Throwableattraper le bloc fonctionne au moins pour ParseError.
Niloct
24

J'ai développé un moyen d'attraper tous les types d'erreur en PHP (presque tous)! Je n'ai aucune certitude sur E_CORE_ERROR (je pense que cela ne fonctionnera pas uniquement pour cette erreur)! Mais, pour d'autres erreurs fatales (E_ERROR, E_PARSE, E_COMPILE ...) fonctionne très bien en utilisant une seule fonction de gestion d'erreur! Voilà ma solution:

Mettez ce code suivant sur votre fichier principal (index.php):

<?php
    define('E_FATAL',  E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR |
            E_COMPILE_ERROR | E_RECOVERABLE_ERROR);

    define('ENV', 'dev');

    // Custom error handling vars
    define('DISPLAY_ERRORS', TRUE);
    define('ERROR_REPORTING', E_ALL | E_STRICT);
    define('LOG_ERRORS', TRUE);

    register_shutdown_function('shut');

    set_error_handler('handler');

    // Function to catch no user error handler function errors...
    function shut(){

        $error = error_get_last();

        if($error && ($error['type'] & E_FATAL)){
            handler($error['type'], $error['message'], $error['file'], $error['line']);
        }

    }

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

        switch ($errno){

            case E_ERROR: // 1 //
                $typestr = 'E_ERROR'; break;
            case E_WARNING: // 2 //
                $typestr = 'E_WARNING'; break;
            case E_PARSE: // 4 //
                $typestr = 'E_PARSE'; break;
            case E_NOTICE: // 8 //
                $typestr = 'E_NOTICE'; break;
            case E_CORE_ERROR: // 16 //
                $typestr = 'E_CORE_ERROR'; break;
            case E_CORE_WARNING: // 32 //
                $typestr = 'E_CORE_WARNING'; break;
            case E_COMPILE_ERROR: // 64 //
                $typestr = 'E_COMPILE_ERROR'; break;
            case E_CORE_WARNING: // 128 //
                $typestr = 'E_COMPILE_WARNING'; break;
            case E_USER_ERROR: // 256 //
                $typestr = 'E_USER_ERROR'; break;
            case E_USER_WARNING: // 512 //
                $typestr = 'E_USER_WARNING'; break;
            case E_USER_NOTICE: // 1024 //
                $typestr = 'E_USER_NOTICE'; break;
            case E_STRICT: // 2048 //
                $typestr = 'E_STRICT'; break;
            case E_RECOVERABLE_ERROR: // 4096 //
                $typestr = 'E_RECOVERABLE_ERROR'; break;
            case E_DEPRECATED: // 8192 //
                $typestr = 'E_DEPRECATED'; break;
            case E_USER_DEPRECATED: // 16384 //
                $typestr = 'E_USER_DEPRECATED'; break;
        }

        $message =
            '<b>' . $typestr .
            ': </b>' . $errstr .
            ' in <b>' . $errfile .
            '</b> on line <b>' . $errline .
            '</b><br/>';

        if(($errno & E_FATAL) && ENV === 'production'){

            header('Location: 500.html');
            header('Status: 500 Internal Server Error');

        }

        if(!($errno & ERROR_REPORTING))
            return;

        if(DISPLAY_ERRORS)
            printf('%s', $message);

        //Logging error on php file error log...
        if(LOG_ERRORS)
            error_log(strip_tags($message), 0);
    }

    ob_start();

    @include 'content.php';

    ob_end_flush();
?>
Lucas Batistussi
la source
2
Que fait la ligne @include 'content.php'?
Marco
22

Vous ne pouvez pas attraper / gérer les erreurs fatales, mais vous pouvez les enregistrer / signaler. Pour un débogage rapide, j'ai modifié une réponse à ce code simple

function __fatalHandler()
{
    $error = error_get_last();

    // Check if it's a core/fatal error, otherwise it's a normal shutdown
    if ($error !== NULL && in_array($error['type'],
        array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING,
              E_COMPILE_ERROR, E_COMPILE_WARNING,E_RECOVERABLE_ERROR))) {

        echo "<pre>fatal error:\n";
        print_r($error);
        echo "</pre>";
        die;
    }
}

register_shutdown_function('__fatalHandler');
zainengineer
la source
mais où irait ce code?
TKoL
@TKoL première ligne. Fondamentalement, le fichier d'entrée de votre script / programme, donc il s'exécute en premier, si ce n'est pas possible, mettez-le dans un fichier commun
zainengineer
17

Vous ne pouvez pas lancer d'exception dans une fonction d'arrêt enregistrée comme celle-ci:

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           throw new Exception("fatal error");
        }
    }

    try {
        $x = null;
        $x->method()
    } catch(Exception $e) {
        # This won't work
    }
?>

Mais vous pouvez capturer et rediriger la demande vers une autre page.

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           # Report the event, send email, etc.
           header("Location: http://localhost/error-capture");
           # From /error-capture. You can use another
           # redirect, to e.g. the home page
        }
    }
    register_shutdown_function('shutdown');

    $x = null;
    $x->method()
?>
hipertracker
la source
11

Si vous utilisez PHP> = 5.1.0 Faites simplement quelque chose comme ça avec la classe ErrorException:

<?php
    // Define an error handler
    function exception_error_handler($errno, $errstr, $errfile, $errline ) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }

    // Set your error handler
    set_error_handler("exception_error_handler");

    /* Trigger exception */
    try
    {
        // Try to do something like finding the end of the internet
    }
    catch(ErrorException $e)
    {
        // Anything you want to do with $e
    }
?>
Cyril Tata
la source
9

Belle solution trouvée dans Zend Framework 2:

/**
 * ErrorHandler that can be used to catch internal PHP errors
 * and convert to an ErrorException instance.
 */
abstract class ErrorHandler
{
    /**
     * Active stack
     *
     * @var array
     */
    protected static $stack = array();

    /**
     * Check if this error handler is active
     *
     * @return bool
     */
    public static function started()
    {
        return (bool) static::getNestedLevel();
    }

    /**
     * Get the current nested level
     *
     * @return int
     */
    public static function getNestedLevel()
    {
        return count(static::$stack);
    }

    /**
     * Starting the error handler
     *
     * @param int $errorLevel
     */
    public static function start($errorLevel = \E_WARNING)
    {
        if (!static::$stack) {
            set_error_handler(array(get_called_class(), 'addError'), $errorLevel);
        }

        static::$stack[] = null;
    }

    /**
     * Stopping the error handler
     *
     * @param  bool $throw Throw the ErrorException if any
     * @return null|ErrorException
     * @throws ErrorException If an error has been catched and $throw is true
     */
    public static function stop($throw = false)
    {
        $errorException = null;

        if (static::$stack) {
            $errorException = array_pop(static::$stack);

            if (!static::$stack) {
                restore_error_handler();
            }

            if ($errorException && $throw) {
                throw $errorException;
            }
        }

        return $errorException;
    }

    /**
     * Stop all active handler
     *
     * @return void
     */
    public static function clean()
    {
        if (static::$stack) {
            restore_error_handler();
        }

        static::$stack = array();
    }

    /**
     * Add an error to the stack
     *
     * @param int    $errno
     * @param string $errstr
     * @param string $errfile
     * @param int    $errline
     * @return void
     */
    public static function addError($errno, $errstr = '', $errfile = '', $errline = 0)
    {
        $stack = & static::$stack[count(static::$stack) - 1];
        $stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack);
    }
}

Cette classe vous permet de démarrer le spécifique ErrorHandlerparfois si vous en avez besoin. Et puis, vous pouvez également arrêter le gestionnaire.

Utilisez cette classe par exemple comme ceci:

ErrorHandler::start(E_WARNING);
$return = call_function_raises_E_WARNING();

if ($innerException = ErrorHandler::stop()) {
    throw new Exception('Special Exception Text', 0, $innerException);
}

// or
ErrorHandler::stop(true); // directly throws an Exception;

Lien vers le code complet de classe:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


Une solution peut-être meilleure est celle de Monolog :

Lien vers le code complet de la classe:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

Il peut également gérer FATAL_ERRORS à l'aide de la register_shutdown_functionfonction. Selon cette classe, FATAL_ERROR est l'un des suivants array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR).

class ErrorHandler
{
    // [...]

    public function registerExceptionHandler($level = null, $callPrevious = true)
    {
        $prev = set_exception_handler(array($this, 'handleException'));
        $this->uncaughtExceptionLevel = $level;
        if ($callPrevious && $prev) {
            $this->previousExceptionHandler = $prev;
        }
    }

    public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1)
    {
        $prev = set_error_handler(array($this, 'handleError'), $errorTypes);
        $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
        if ($callPrevious) {
            $this->previousErrorHandler = $prev ?: true;
        }
    }

    public function registerFatalHandler($level = null, $reservedMemorySize = 20)
    {
        register_shutdown_function(array($this, 'handleFatalError'));

        $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
        $this->fatalLevel = $level;
    }

    // [...]
}
algorhythm
la source
9

J'ai besoin de gérer les erreurs fatales pour la production afin d'afficher à la place une sortie HTML 503 de style statique non disponible . C'est sûrement une approche raisonnable pour "détecter les erreurs fatales". Voici ce que j'ai fait:

J'ai une fonction personnalisée de gestion des erreurs "error_handler" qui affichera ma page HTML "503 service non disponible" sur n'importe quelle E_ERROR, E_USER_ERROR, etc.

function fatal_error_handler() {

    if (@is_array($e = @error_get_last())) {
        $code = isset($e['type']) ? $e['type'] : 0;
        $msg = isset($e['message']) ? $e['message'] : '';
        $file = isset($e['file']) ? $e['file'] : '';
        $line = isset($e['line']) ? $e['line'] : '';
        if ($code>0)
            error_handler($code, $msg, $file, $line);
    }
}
set_error_handler("error_handler");
register_shutdown_function('fatal_error_handler');

dans ma fonction personnalisée error_handler, si l'erreur est E_ERROR, E_USER_ERROR, etc. J'appelle également @ob_end_clean();pour vider le tampon, supprimant ainsi le message "erreur fatale" de PHP.

Prenez note des fonctions strictes de vérification et de désactivation de isset () @car nous ne voulons pas que nos scripts error_handler génèrent des erreurs.

En étant toujours d'accord avec keparo, la capture d'erreurs fatales va à l'encontre du but de "l'erreur fatale", donc il n'est pas vraiment destiné à vous de poursuivre le traitement. N'exécutez aucune fonction mail () dans ce processus d'arrêt car vous sauvegarderez certainement le serveur de messagerie ou votre boîte de réception. Consignez plutôt ces occurrences dans un fichier et planifiez un travail cron pour trouver ces fichiers error.log et les envoyer par courrier aux administrateurs.

Prof83
la source
7

PHP a des erreurs fatales capturables. Ils sont définis comme E_RECOVERABLE_ERROR. Le manuel PHP décrit un E_RECOVERABLE_ERROR comme:

Erreur fatale capturable. Il indique qu'une erreur probablement dangereuse s'est produite, mais n'a pas laissé le moteur dans un état instable. Si l'erreur n'est pas détectée par un descripteur défini par l'utilisateur (voir aussi set_error_handler () ), l'application s'interrompt car il s'agissait d'un E_ERROR.

Vous pouvez "intercepter" ces erreurs "fatales" en utilisant set_error_handler () et en recherchant E_RECOVERABLE_ERROR. Je trouve utile de lever une exception lorsque cette erreur est détectée, vous pouvez alors utiliser try / catch.

Cette question et réponse fournit un exemple utile: Comment puis-je détecter une "erreur fatale capturable" sur les indications de type PHP?

Cependant, les erreurs E_ERROR peuvent être gérées, mais pas récupérées car le moteur est dans un état instable.

Aucun
la source
6

Voici 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) {

            // It is a 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 veux aussi noter que si vous appelez

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

PHP cesse d'afficher l'erreur. Sinon, le texte d'erreur sera envoyé au client avant votre gestionnaire d'erreurs.

Sander Visser
la source
1
A voté cela en raison de la ligne ini_set ('display_errors', false);
Sahib Khan
Si pour une raison quelconque, ce bit est activé, il affichera toujours des erreurs php même si vous le gérez différemment
Sahib Khan
5

Puisque la plupart des réponses ici sont inutilement verbeuses, voici ma version non laide de la réponse la plus votée:

function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array()) {
    //Do stuff: mail, log, etc
}

function fatalHandler() {
    $error = error_get_last();
    if($error) errorHandler($error["type"], $error["message"], $error["file"], $error["line"]);
}

set_error_handler("errorHandler")
register_shutdown_function("fatalHandler");
Mahn
la source
4

Pas vraiment. Les erreurs fatales sont appelées ainsi, car elles sont fatales. Vous ne pouvez pas vous remettre d'eux.

troelskn
la source
12
attraper et récupérer sont deux choses très différentes.
Simon Forsberg
3

J'ai développé cette fonction pour rendre possible le code "sandbox" qui pourrait provoquer une erreur fatale. Depuis les exceptions levées de la fermetureregister_shutdown_function ne sont pas émises à partir de la pile d'appels d'erreur pré-fatale, je suis obligé de quitter après cette fonction pour fournir un moyen uniforme de l'utiliser.

function superTryCatchFinallyAndExit( Closure $try, Closure $catch = NULL, Closure $finally )
{
    $finished = FALSE;
    register_shutdown_function( function() use ( &$finished, $catch, $finally ) {
        if( ! $finished ) {
            $finished = TRUE;
            print "EXPLODE!".PHP_EOL;
            if( $catch ) {
                superTryCatchFinallyAndExit( function() use ( $catch ) {
                    $catch( new Exception( "Fatal Error!!!" ) );
                }, NULL, $finally );                
            } else {
                $finally();                
            }
        }
    } );
    try {
        $try();
    } catch( Exception $e ) {
        if( $catch ) {
            try {
                $catch( $e );
            } catch( Exception $e ) {}
        }
    }
    $finished = TRUE;
    $finally();
    exit();
}
Kendall Hopkins
la source
3

Il y a certaines circonstances dans lesquelles même des erreurs fatales devraient être détectées (vous devrez peut-être faire un peu de nettoyage avant de sortir gracieusement et ne pas simplement mourir ..).

J'ai implémenté un hook pre_system dans mon CodeIgniter applications afin que je puisse obtenir mes erreurs fatales par e-mails, ce qui m'a aidé à trouver des bogues qui n'ont pas été signalés (ou qui ont été signalés après leur correction, car je les connaissais déjà :)).

Sendemail vérifie si l'erreur a déjà été signalée afin de ne pas vous spammer plusieurs fois avec des erreurs connues.

class PHPFatalError {

    public function setHandler() {
        register_shutdown_function('handleShutdown');
    }
}

function handleShutdown() {
    if (($error = error_get_last())) {
        ob_start();
        echo "<pre>";
        var_dump($error);
        echo "</pre>";
        $message = ob_get_clean();
        sendEmail($message);
        ob_start();
        echo '{"status":"error","message":"Internal application error!"}';
        ob_flush();
        exit();
    }
}
tix3
la source
Qu'est-ce que "Sendemail" ? Voulez-vous dire Sendmail (répondez en modifiant votre réponse , pas ici dans les commentaires)?
Peter Mortensen