Question Comment attraper une erreur PHP fatale


je peux utiliser set_error_handler() pour attraper la plupart des erreurs PHP, mais cela ne fonctionne pas pour fatal (E_ERROR), comme l'appel d'une fonction qui n'existe pas. Y a-t-il un autre moyen d'attraper ces erreurs?

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


493
2017-11-10 06:42


origine


Réponses:


Enregistrer les erreurs fatales en utilisant register_shutdown_function, ce 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 le error_mail et format_error les fonctions. 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;
}

Utilisation Swift Mailer écrire le error_mail fonction.

Voir également:


586
2018-01-27 10:48



Juste venu avec 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 définis à http://www.php.net/manual/en/errorfunc.constants.php


134
2017-08-02 15:04



PHP ne fournit pas de moyens conventionnels pour intercepter 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. La chaîne correspondant à un tampon de sortie (comme suggéré par le post original, la technique décrite sur PHP.net) est certainement peu judicieuse. C'est simplement peu fiable.

L'appel de la fonction mail () à partir d'une méthode de gestionnaire d'erreur s'avère également problématique. Si vous avez eu beaucoup d'erreurs, votre serveur de messagerie serait chargé de travail, et vous pourriez vous retrouver avec une boîte de réception gnarly. Pour éviter cela, vous pouvez envisager d'exécuter un cron pour analyser périodiquement les journaux d'erreur et envoyer les notifications en conséquence. Vous pourriez également vouloir regarder dans le logiciel de surveillance de système, tel que Nagios.


Pour parler au sujet de l'enregistrement d'une fonction d'arrêt:

C'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, en particulier pas en utilisant une expression régulière contre votre tampon de sortie. Je répondais à la réponse acceptée, qui est liée à une suggestion sur php.net qui a depuis été modifiée ou supprimée.

Cette suggestion consistait à utiliser une expression rationnelle contre le tampon de sortie lors du traitement des exceptions et, en cas d’erreur fatale (détectée par la correspondance avec le texte d’erreur configuré), essayez une récupération ou un traitement continu. Ce ne serait pas une pratique recommandée (je crois que c'est la raison pour laquelle je ne trouve pas la suggestion originale. Je ne suis pas d'accord ou la communauté php a été éliminée).

Il pourrait être intéressant de noter que les versions plus récentes de PHP (autour de 5.1) semblent appeler la fonction d’arrêt plus tôt, avant que le rappel de la mémoire tampon de sortie ne soit invoqué. Dans la version 5 et plus tôt, cet ordre était l'inverse (le rappel de mise en mémoire tampon de sortie était suivi par la fonction shutdown). De plus, environ 5.0.5 (ce qui est beaucoup plus tôt que la version 5.2.3 de l'interrogateur), les objets sont déchargés bien avant qu'une fonction d'arrêt enregistrée ne soit appelée, vous ne pourrez donc pas faire appel à vos objets en mémoire beaucoup de n'importe quoi.

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

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


111
2017-11-10 09:44



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;

}

31
2018-03-04 09:38



J'ai développé un moyen d'attraper tous les types d'erreurs en PHP (presque tous)! Je ne suis pas sûr de E_CORE_ERROR (je pense que ça ne marchera pas seulement pour cette erreur)! Mais, pour d'autres erreurs fatales (E_ERROR, E_PARSE, E_COMPILE ...) fonctionne correctement en utilisant une seule fonction de gestionnaire 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();

?>

J'espère que cela aide beaucoup de gens! Je cherchais cette solution trop longtemps et n'ai pas trouvé! Alors j'en ai développé un!


20
2018-05-11 04:51



Vous ne pouvez pas capturer / gérer les erreurs fatales, mais vous pouvez les enregistrer / les 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');

17
2018-04-15 05:31



Vous ne pouvez pas lancer d'exception dans la fonction d'arrêt enregistré comme ceci:

<?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();
       # raport the event, send email etc.
       header("Location: http://localhost/error-capture");
       # from /error-capture, you can use another redirect, to e.g. home page
    }
}
register_shutdown_function('shutdown');

$x = null;
$x->method()
?>

16
2017-09-25 20:15



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 ur error handle
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
}

?>

11
2017-07-24 15:11



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 ErrorHandler parfois si vous en avez besoin. Et puis, vous pouvez également arrêter le gestionnaire.

Utilisez cette classe, par exemple comme ça:

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 la classe:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


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

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

Il peut également gérer FATAL_ERRORS en utilisant le register_shutdown_function fonction. 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;
    }

    // [...]
}

8
2017-10-21 13:21