602 Stimmen

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

Ich kann set_error_handler() um die meisten PHP-Fehler abzufangen, aber es funktioniert nicht bei fatalen ( E_ERROR ) Fehler, wie z.B. der Aufruf einer Funktion, die nicht existiert. Gibt es eine andere Möglichkeit, diese Fehler abzufangen?

Ich versuche zu telefonieren mail() für alle Fehler und ich verwende PHP 5.2.3.

10voto

algorhythm Punkte 8122

Schöne Lösung in Zend Framework 2 gefunden:

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

Diese Klasse ermöglicht es Ihnen, die spezifischen ErrorHandler manchmal, wenn Sie es brauchen. Und dann können Sie den Handler auch anhalten.

Verwenden Sie diese Klasse z.B. 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 diejenige von Monolog :

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

Es kann auch FATAL_ERRORS behandeln, indem es die register_shutdown_function Funktion. Gemäß dieser Klasse ist ein FATAL_ERROR einer der folgenden Fälle 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;
    }

    // [...]
}

10voto

Prof Punkte 2882

Ich brauche, um fatale Fehler für die Produktion zu behandeln, um stattdessen zeigen eine statische gestylt 503 Dienst nicht verfügbar HTML-Ausgabe. Dies ist sicherlich ein vernünftiger Ansatz, um "fatale Fehler abzufangen". So habe ich es gemacht:

Ich habe eine benutzerdefinierte Fehlerbehandlungsfunktion "error_handler", die bei jedem E_ERROR, E_USER_ERROR usw. meine HTML-Seite "503 service unavailable" anzeigt. Diese wird nun bei der Shutdown-Funktion aufgerufen und fängt meinen fatalen 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');

in meiner benutzerdefinierten error_handler-Funktion, wenn der Fehler E_ERROR, E_USER_ERROR usw. ist. Ich rufe auch @ob_end_clean(); um den Puffer zu leeren und damit die "fatale Fehlermeldung" von PHP zu entfernen.

Beachten Sie unbedingt die strenge isset()-Überprüfung und @ Silencing-Funktionen, da wir nicht wollen, dass unsere error_handler-Skripte irgendwelche Fehler erzeugen.

Ich stimme keparo zu, dass das Abfangen von schwerwiegenden Fehlern den Zweck von "FATAL error" untergräbt, so dass es nicht wirklich für eine weitere Verarbeitung gedacht ist. Führen Sie in diesem Shutdown-Prozess keine mail()-Funktionen aus, da Sie damit mit Sicherheit den Mailserver oder Ihren Posteingang sichern würden. Protokollieren Sie stattdessen diese Ereignisse in einer Datei und planen Sie einen cron Aufgabe, diese zu finden error.log Dateien und senden sie an die Administratoren.

7voto

None Punkte 5232

PHP hat abfangbare fatale Fehler. Sie sind als E_RECOVERABLE_ERROR definiert. Das PHP-Handbuch beschreibt einen E_RECOVERABLE_ERROR wie folgt:

Abfangbarer schwerwiegender Fehler. Er zeigt an, dass ein wahrscheinlich gefährlicher Fehler aufgetreten ist, der die Engine aber nicht in einen instabilen Zustand versetzt hat. Wenn der Fehler nicht durch ein benutzerdefiniertes Handle abgefangen wird (siehe auch set_error_handler() ), bricht die Anwendung ab, da es ein E_ERROR war.

Sie können diese "fatalen" Fehler abfangen, indem Sie set_error_handler() und Prüfung auf E_RECOVERABLE_ERROR. Ich finde es nützlich, eine Exception zu werfen, wenn dieser Fehler aufgefangen wird, dann können Sie try/catch verwenden.

Diese Frage und Antwort liefert ein nützliches Beispiel: Wie kann ich einen "catchable fatal error" bei PHP type hinting abfangen?

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

6voto

Mahn Punkte 15528

Da die meisten Antworten hier unnötig langatmig sind, hier meine nicht hässliche Version der meistgewählten 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");

4voto

troelskn Punkte 110542

Nicht wirklich. Fatale Fehler werden so genannt, weil sie fatal sind. Von ihnen kann man sich nicht erholen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X