Wie fange ich einen schwerwiegenden PHP-Fehler (E_ERROR) ab?

557

Ich kann die set_error_handler()meisten PHP-Fehler abfangen, aber es funktioniert nicht bei schwerwiegenden ( E_ERROR) Fehlern, z. B. beim Aufrufen einer nicht vorhandenen Funktion. Gibt es eine andere Möglichkeit, diese Fehler abzufangen?

Ich versuche, mail()alle Fehler abzurufen und verwende PHP 5.2.3.

zu viel php
quelle
Ich habe ein Wiki-ähnliches Q & A mit einer vollständigen Lösung zum Abfangen aller Fehler in PHP geschrieben. die hier auf Stack Overflow eingesehen / gesammelt / gestohlen / kritisiert werden können . Die Lösung enthält fünf Methoden, die alle von PHP generierten Fehler umschließen und diese Fehler schließlich an ein Objekt vom Typ 'ErrorHandler' übergeben.
DigitalJedi805
Siehe auch: bugs.php.net/bug.php?id=41418
dreftymac
Siehe auch: stackoverflow.com/questions/7116995
dreftymac

Antworten:

635

Protokollieren Sie schwerwiegende Fehler mit dem register_shutdown_function, für das PHP 5.2+ erforderlich ist:

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));
    }
}

Sie müssen die error_mailund format_errorFunktionen definieren . Zum Beispiel:

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;
}

Verwenden Sie Swift Mailer , um die error_mailFunktion zu schreiben .

Siehe auch:

user259973
quelle
113
+1 Dies ist die tatsächlich richtige Antwort. Ich weiß nicht, warum die Leute hängen bleiben an "Sie können sich nicht von schwerwiegenden Fehlern erholen" - die Frage sagte nichts über die Wiederherstellung aus.
David Harkness
21
Danke, guter. Das Wiederherstellen von schwerwiegenden Fehlern (z. B. Speicherbeschränkungen) ist nicht das, was ich versuchen würde, aber das Aufdecken dieser Fehler (ohne dass der Kunde ein Support-Ticket einreicht) macht den Unterschied.
Ilija
2
Verwenden der Basispost:mail("[email protected]", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
Eric Muyser
4
@ScottNicol Slava V ist korrekt, da die Abschaltfunktion jedes Mal aufgerufen wird, wenn das Skript beendet ist. So wie der Code jetzt geschrieben ist, wird beim Laden jeder Seite eine E-Mail gesendet.
Nate
2
Hinweis: Dies ist keine 100% korrekte Antwort. Jeder Ort, an dem ein @ -Symbol zum Ignorieren von Fehlern verwendet wird, setzt weiterhin den letzten Fehler (damit Sie Fehler behandeln können). Ihr Skript wird also problemlos beendet, aber die Funktion register_shutdown_ glaubt immer noch, dass ein Fehler aufgetreten ist. Erst seit PHP 7 hatten sie eine Funktion error_clear_last ().
Rahly
150

Ich habe mir gerade diese Lösung ausgedacht (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');

Bei vordefinierten Konstanten werden verschiedene Fehlertypen definiert .

Periklis
quelle
25
Diese Lösung bietet mir viel mehr als nur eine erstklassige Antwort. Die am besten bewertete Antwort sendet Ihnen jedes Mal eine E-Mail, wenn das Skript ausgeführt wird, auch wenn kein Fehler vorliegt. Dieser läuft ausschließlich mit einem schwerwiegenden Fehler.
kmoney12
@periklis, wenn der letzte Fehler bereits behandelt wurde, würde error_get_last immer noch zurückgeben, nicht wahr?
Pacerier
@ Pacerier Ich bin nicht sicher, was Sie mit "behandelt" meinen, da Fehler keine Ausnahmen sind, aber ich nehme an, die Antwort ist "Ja"
Periklis
3
@ Pacerier Ich verstehe, das ist eine interessante Frage. Werfen Sie einen Blick auf php.net/error_get_last , einer der Kommentare erwähnt, dass " If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function."
periklis
1
Vielleicht ist dies offensichtlich. Der Aufruf register_shutdown_function()muss früher erfolgen als jeder schwerwiegende Fehler. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');funktioniert nicht wie erwartet.
Nobu
117

PHP bietet keine herkömmlichen Mittel zum Abfangen und Wiederherstellen schwerwiegender Fehler. Dies liegt daran, dass die Verarbeitung normalerweise nach einem schwerwiegenden Fehler nicht wiederhergestellt werden sollte. Ein String, der einem Ausgabepuffer entspricht (wie im ursprünglichen Beitrag der auf PHP.net beschriebenen Technik vorgeschlagen), ist definitiv nicht ratsam. Es ist einfach unzuverlässig.

Das Aufrufen der Funktion mail () innerhalb einer Fehlerbehandlungsmethode erweist sich ebenfalls als problematisch. Wenn Sie viele Fehler hätten, wäre Ihr Mailserver mit Arbeit geladen, und Sie könnten sich in einem knorrigen Posteingang befinden. Um dies zu vermeiden, können Sie einen Cron ausführen, um Fehlerprotokolle regelmäßig zu scannen und entsprechende Benachrichtigungen zu senden. Vielleicht möchten Sie sich auch mit Systemüberwachungssoftware wie Nagios befassen .


Um mit dem Bit über das Registrieren einer Abschaltfunktion zu sprechen:

Es ist wahr, dass Sie eine Abschaltfunktion registrieren können, und das ist eine gute Antwort.

Der Punkt hier ist, dass wir normalerweise nicht versuchen sollten, schwerwiegende Fehler zu beheben, insbesondere nicht, indem wir einen regulären Ausdruck für Ihren Ausgabepuffer verwenden. Ich antwortete auf die akzeptierte Antwort , die mit einem Vorschlag auf php.net verknüpft war, der inzwischen geändert oder entfernt wurde.

Dieser Vorschlag bestand darin, während der Ausnahmebehandlung einen regulären Ausdruck für den Ausgabepuffer zu verwenden und im Falle eines schwerwiegenden Fehlers (der durch den Abgleich mit dem möglicherweise erwarteten konfigurierten Fehlertext erkannt wird) eine Wiederherstellung oder eine fortgesetzte Verarbeitung durchzuführen. Das wäre keine empfohlene Vorgehensweise (ich glaube, deshalb kann ich auch den ursprünglichen Vorschlag nicht finden. Ich übersehen ihn entweder oder die PHP-Community hat ihn abgeschossen).

Es kann erwähnenswert sein, dass die neueren Versionen von PHP (um 5.1) die Abschaltfunktion früher aufzurufen scheinen, bevor der Rückruf für die Ausgabepufferung aufgerufen wird. In Version 5 und früheren Versionen war diese Reihenfolge umgekehrt (auf den Rückruf der Ausgabepufferung folgte die Funktion zum Herunterfahren). Seit ungefähr 5.0.5 (das ist viel früher als die Version 5.2.3 des Fragestellers) werden Objekte lange vor dem Aufruf einer registrierten Abschaltfunktion entladen, sodass Sie sich nicht auf Ihre In-Memory-Objekte verlassen können viel von allem.

Das Registrieren einer Abschaltfunktion ist also in Ordnung, aber die Art von Aufgaben, die von einer Abschaltfunktion ausgeführt werden sollten, sind wahrscheinlich auf eine Handvoll sanfter Abschaltvorgänge beschränkt.

Der Schlüssel zum Mitnehmen sind hier nur einige Worte der Weisheit für jeden, der über diese Frage stolpert und den Rat in der ursprünglich akzeptierten Antwort sieht. Regexen Sie Ihren Ausgabepuffer nicht neu.

Keparo
quelle
25
Pfff, ich erinnere mich an die über 650.000 E-Mails, die ich am nächsten Morgen erhalten habe. Seitdem ist mein ErrorHandler auf 100 E-Mails pro Webserver begrenzt.
Bob Fanger
14
Das ist nicht wahr. Mit register_shutdown_function können Sie schwerwiegende Fehler erfassen.
Hipertracker
56
Es gibt Anwendungsfälle, um schwerwiegende Fehler abzufangen. Testsuiten sollten beispielsweise nicht einfach anhalten, wenn eine fehlschlägt, sondern den schwerwiegenden Fehler melden und mit dem nächsten Test fortfahren. PHP macht einfach zu viele Dinge "fatale" Fehler.
Tschad
24
Ja zu sagen, dass sie "nicht gefangen werden sollten", ist sehr kurzsichtig. In einem Produktionssystem, Sie müssen wissen , wann etwas fehlschlägt (E - Mails einrichten oder Dinge , melden Sie sich in einer Datenbank - Standard - PHP - Fehlerbehandlung ist nicht sehr anspruchsvoll).
BT
8
Ich möchte einen kurzen Kommentar zu dem abgeben, was Sie alle über "Fehler müssen abgefangen werden, damit wir sie beheben können" ... Ini-Direktiven ini log_errors und error_log.
Kelly Elton
37

Nun, es scheint möglich zu sein, schwerwiegende Fehler auf andere Weise zu erkennen :)

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
quelle
3
Ich würde diese 10 Upvotes geben, wenn ich könnte. Es funktioniert perfekt für mich bei diesen seltsamen Fehlern, die manchmal auftreten, wenn eine Seite bombardiert wird und nichts protokolliert wird. Ich würde es nicht in Live-Produktionscode verwenden, aber es ist großartig, es einer Seite hinzuzufügen, wenn eine schnelle Antwort auf das, was fehlschlägt, benötigt wird. Vielen Dank!
Nachteule
Eine der besten Lösungen, die ich im Internet gefunden habe. Funktioniert wie Charme.
Bounce
Inwiefern? Eine Erklärung wäre angebracht, insbesondere wenn es sich um eine der besten Lösungen im Internet handelt (es könnte sogar noch besser werden).
Peter Mortensen
Wird zB der gesamte CSS-Inhalt benötigt? Könnte es nicht auf das Wesentliche reduziert werden? Antworten Sie, indem Sie Ihre Antwort bearbeiten, nicht hier in den Kommentaren (falls zutreffend).
Peter Mortensen
@ PeterMortensen Ich behaupte nicht das Beste. Es ist auch meine persönliche Lösung für Probleme, es gibt andere bessere Optionen, viele professionelle. Wie von jemandem vorgeschlagen, ist es nicht gut für die Produktion geeignet. CSS ist da, weil ich gerade meinen persönlichen Code ausgeschnitten habe
sakhunzai
36

Schwerwiegende Fehler oder behebbare schwerwiegende Fehler lösen jetzt Instanzen von Errorin PHP 7 oder höheren Versionen aus . Wie alle anderen Ausnahmen können ErrorObjekte mit a gefangen werdentry/catch Block .

Beispiel:

<?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

Oder Sie können die ThrowableSchnittstelle verwenden, um alle Ausnahmen abzufangen.

Beispiel:

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

https://3v4l.org/Br0MG

Für weitere Informationen: http://php.net/manual/en/language.errors.php7.php

LugiHaue
quelle
2
Gibt es eine Idee, wie man dies verwendet, um einen Fehler wie Fatal error: Trait 'FailedTrait' not found inbei der Verwendung abzufangen ReflectionClass?
TCB13
1
@ TCB13 versuchen, den inneren Inhalt des Versuchs in eine Datei und include "filename.php"stattdessen in den tryBlock zu packen , dann Throwablefunktioniert catch-Block zumindest für ParseError.
Niloct
24

Ich habe eine Möglichkeit entwickelt, alle Fehlertypen in PHP (fast alle) zu erfassen! Ich bin mir nicht sicher über E_CORE_ERROR (ich denke, es wird nicht nur für diesen Fehler funktionieren)! Bei anderen schwerwiegenden Fehlern (E_ERROR, E_PARSE, E_COMPILE ...) funktioniert dies jedoch mit nur einer Fehlerbehandlungsfunktion! Da geht meine Lösung:

Fügen Sie den folgenden Code in Ihre Hauptdatei (index.php) ein:

<?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
quelle
2
Was macht die Zeile @include 'content.php'?
Marco
22

Sie können schwerwiegende Fehler nicht abfangen / behandeln, aber Sie können sie protokollieren / melden. Zum schnellen Debuggen habe ich eine Antwort auf diesen einfachen Code geändert

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
quelle
aber wohin würde dieser Code gehen?
TKoL
@TKoL erste Zeile. Grundsätzlich Eingabedatei Ihres Skripts / Programms, so dass es zuerst ausgeführt wird, wenn dies nicht möglich ist, legen Sie es in eine gemeinsame Datei
zainengineer
17

Sie können innerhalb einer registrierten Abschaltfunktion wie folgt keine Ausnahme auslösen:

<?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
    }
?>

Sie können Anforderungen jedoch erfassen und auf eine andere Seite umleiten.

<?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
quelle
11

Wenn Sie PHP> = 5.1.0 verwenden, machen Sie einfach so etwas mit der ErrorException-Klasse:

<?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
quelle
9

Gute Lösung in 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);
    }
}

Mit dieser Klasse können Sie die spezifische ErrorHandlermanchmal starten, wenn Sie es brauchen. Und dann können Sie auch den Handler stoppen.

Verwenden Sie diese Klasse zB wie folgt:

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;

Link zum vollständigen Klassencode:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


Eine vielleicht bessere Lösung ist die von Monolog :

Link zum vollständigen Klassencode:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

Es kann auch FATAL_ERRORS mit der register_shutdown_functionFunktion verarbeiten. Gemäß dieser Klasse ist ein FATAL_ERROR eine der folgenden 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;
    }

    // [...]
}
Algorithmus
quelle
9

Ich muss schwerwiegende Fehler für die Produktion behandeln, um stattdessen eine statisch gestaltete 503 Service Unavailable HTML-Ausgabe anzuzeigen . Dies ist sicherlich ein vernünftiger Ansatz, um "schwerwiegende Fehler zu erkennen". Folgendes habe ich getan:

Ich habe eine benutzerdefinierte Fehlerbehandlungsfunktion "error_handler", die meine HTML-Seite "503 service notavailable" auf jedem E_ERROR, E_USER_ERROR usw. anzeigt. Diese wird jetzt in der Shutdown-Funktion aufgerufen und fängt meinen schwerwiegenden Fehler ab.

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');

Wenn der Fehler in meiner benutzerdefinierten error_handler-Funktion E_ERROR, E_USER_ERROR usw. ist, rufe ich ebenfalls auf @ob_end_clean(); um den Puffer zu leeren und so die PHP-Meldung "Schwerwiegender Fehler" zu entfernen.

Beachten Sie die strengen isset () - Prüfungen und @ Stummschalten da unsere error_handler-Skripte keine Fehler generieren sollen.

Wenn Sie Keparo weiterhin zustimmen, wird das Abfangen schwerwiegender Fehler den Zweck des "FATAL-Fehlers" zunichte machen, sodass Sie nicht wirklich für die weitere Verarbeitung vorgesehen sind. Führen Sie bei diesem Herunterfahren keine mail () -Funktionen aus, da Sie mit Sicherheit den Mailserver oder Ihren Posteingang sichern werden. Protokollieren Sie diese Vorkommen lieber in einer Datei und planen Sie einen Cron- Job, um diese error.log- Dateien zu finden und an Administratoren zu senden .

Prof83
quelle
7

PHP hat schwerwiegende Fehler. Sie sind als E_RECOVERABLE_ERROR definiert. Das PHP-Handbuch beschreibt einen E_RECOVERABLE_ERROR als:

Fangender schwerwiegender Fehler. Es zeigt an, dass ein wahrscheinlich gefährlicher Fehler aufgetreten ist, der Motor jedoch nicht in einem instabilen Zustand belassen wurde. Wenn der Fehler nicht von einem benutzerdefinierten Handle abgefangen wird (siehe auch set_error_handler () ), wird die Anwendung abgebrochen, da es sich um ein E_ERROR handelt.

Sie können diese "schwerwiegenden" Fehler "abfangen", indem Sie set_error_handler () verwenden und nach E_RECOVERABLE_ERROR suchen. Ich finde es nützlich, eine Ausnahme auszulösen, wenn dieser Fehler abgefangen wird. Dann können Sie try / catch verwenden.

Diese Frage und Antwort bietet ein nützliches Beispiel: Wie kann ich einen "abfangbaren schwerwiegenden Fehler" bei Hinweisen auf PHP-Typen abfangen?

E_ERROR-Fehler können jedoch behandelt, aber nicht behoben werden, da sich die Engine in einem instabilen Zustand befindet.

Keiner
quelle
6

Hier ist nur ein netter Trick, um die aktuelle error_handler-Methode zu erhalten =)

<?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;
        }
    }
?>

Auch das möchte ich beachten, wenn Sie anrufen

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

PHP zeigt den Fehler nicht mehr an. Andernfalls wird der Fehlertext vor Ihrem Fehlerbehandler an den Client gesendet.

Sander Visser
quelle
1
Dies wurde aufgrund der Zeile ini_set ('display_errors', false) verbessert.
Sahib Khan
Wenn dieses Bit aus irgendeinem Grund aktiviert ist, werden immer noch PHP-Fehler angezeigt, auch wenn Sie anders damit umgehen
Sahib Khan,
5

Da die meisten Antworten hier unnötig ausführlich sind, ist hier meine nicht hässliche Version der am besten bewerteten Antwort:

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
quelle
4

Nicht wirklich. Schwerwiegende Fehler werden so genannt, weil sie schwerwiegend sind. Sie können sich nicht von ihnen erholen.

troelskn
quelle
12
Fangen und Erholen sind zwei sehr unterschiedliche Dinge.
Simon Forsberg
3

Ich habe diese Funktion entwickelt, um Code zu "sandboxen", der einen schwerwiegenden Fehler verursachen kann. Da Ausnahmen, die vom Abschluss ausgelöst werden register_shutdown_function, nicht vom vor schwerwiegenden Fehleraufrufstapel ausgegeben werden, muss ich diese Funktion beenden, um eine einheitliche Verwendung zu ermöglichen.

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
quelle
3

Es gibt bestimmte Umstände, unter denen sogar schwerwiegende Fehler auftreten sollten (möglicherweise müssen Sie vor dem ordnungsgemäßen Beenden einige Aufräumarbeiten durchführen und sterben nicht einfach.).

Ich habe einen pre_system-Hook in meinem CodeIgniter implementiert Anwendungen damit ich meine schwerwiegenden Fehler per E- kann. Dies hat mir geholfen, Fehler zu finden, die nicht gemeldet wurden (oder gemeldet wurden, nachdem sie behoben wurden, da ich bereits davon wusste :)).

Sendemail prüft, ob der Fehler bereits gemeldet wurde, damit Sie nicht mehrmals mit bekannten Fehlern als Spam versendet werden.

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
quelle
Was ist "Sendemail" ? Meinen Sie Sendmail (antworten Sie, indem Sie Ihre Antwort bearbeiten , nicht hier in den Kommentaren)?
Peter Mortensen