PHP Beitrag

Fehlerbehandlung richtig einsetzen

error_reporting(E_ALL);

Wer kennt es nicht?

Aber ist das wirklich die optimale Einstellung in der Entwicklungsumgebung?

Der nachfolgende Codeauszug zeigt dir wie du zumindest während der Entwicklung optimal die Fehlerbehandlung nutzen kannst, denn mit den Einstellungen wird dir jeder noch so kleine Fehler direkt angezeigt.

error_reporting(-1); // wirklich alle Fehlermeldungen anzeigen
    
    // Error Reporting komplett abschalten
    error_reporting(0);
    
    // Nur einfache Fehler melden
    error_reporting(E_ERROR | E_WARNING | E_PARSE);
    
    // E_NOTICE ist sinnvoll um uninitialisierte oder
    // falsch geschriebene Variablen zu entdecken
    error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
    
    // Melde alle Fehler außer E_NOTICE
    error_reporting(E_ALL & ~E_NOTICE);
    
    // Melde alle PHP Fehler (siehe Changelog @ php.net)
    error_reporting(E_ALL);

ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
ini_set('html_errors', '1');
ini_set('log_errors', '1');
ini_set('error_log', getcwd().'/php-'.date('d.m.Y').'-error.log');
ini_set('ignore_repeated_errors', '0');

Und im Live-Betrieb?

Im Live-Betrieb sollte die Fehlerbehandlung zwar nicht deaktiviert werden, aber so ablaufen, das der Anwender im besten Fall eine Fehlerseite sieht. Natürlich ohne PHP Warnungen in denen im schlimmsten Fall Serverpfade und Co. stehen.

Also gilt folgendes:

  1. Den Fehler abfangen
  2. Eine eigene Fehlerseite anzeigen (zum Beispiel: Sorry, unsere Technik arbeitet bereits daran)
  3. Den Fehler behandeln bzw. im Hintergrund verarbeiten

Und das geht so

error_reporting(0);
    
ini_set('display_errors', '0');
ini_set('display_startup_errors', '0');
ini_set('html_errors', '0');
ini_set('log_errors', '1'); // dateibasierter log bleibt aktiv
ini_set('error_log', getcwd().'/php-'.date('d.m.Y').'-error.log');
ini_set('ignore_repeated_errors', '0');

// eigene Handler registrieren
set_error_handler('MyError');
set_exception_handler('MyException');
register_shutdown_function('MyShutdown');

Eigene Handler-was?

PHP bringt von Haus aus eine Exception Klasse mit. Diese können wir erweitern um uns individuelle Fehlerberichte zu erstellen.

Doch als erstes lassen wir wirklich alles gleich aussehen und wandeln Fehler (trigger_error) in Ausnahmen (Exception) um.

function MyError($no, $str, $file, $line) {

    throw new ErrorException($str, 0, $no, $file, $line);
}

Jetzt müssen wir unsere Ausnahmen nur noch auffangen und irgendetwas damit anstellen.

Da es im Live-Betrieb nicht wirklich sinnvoll ist detaillierte Fehlermeldungen anzuzeigen, lassen wir uns den Bericht am Besten per E-Mail schicken.

Damit wir nicht eine Million E-Mails für den selben Fehler bekommen, erstellen wir von dem gesamten Fehler einen MD5-Hash und speichern diesen in einem Cookie, sollte der Fehlerbericht erfolgreich versendet werden können. Nur wenn es den MD5-Hash noch nicht gibt, wird der Fehlerbericht per E-Mail versendet.

function MyException($e) {
    
    $args = array(
        'message' => $e->getMessage(),
        'code' => $e->getCode(),
        'severity' => $e->getSeverity(),
        'file' => $e->getFile(),
        'line' => $e->getLine()
    );

    switch ($args['severity']) {
        
        case E_NOTICE:
        case E_USER_NOTICE:
            $type = 'NOTICE';
            break;

        case E_WARNING:
        case E_USER_WARNING:
        case E_COMPILE_WARNING:
            $type = 'WARNING';
            break;

        case E_ERROR:
        case E_USER_ERROR:
            $type = 'ERROR';
            break;

        case E_DEPRECATED:
        case E_USER_DEPRECATED:
            $type = 'DEPRECATED';
            break;

        case E_PARSE:
            $type = 'PARSE ERROR';
            break;

        case E_CORE_ERROR:
            $type = 'CORE ERROR';
            break;

        case E_COMPILE_ERROR:
            $type = 'COMPILE ERROR';
            break;

        case E_STRICT:
            $type = 'STRICT';
            break;

        default:
            $type = 'UNDEFINED';
            break;
    }

    $args['type'] = $type.' ('.$args['severity'].')';

    $hash = hash('md5', implode('.', $args));

    $args['time'] = date('d.m.Y - H:i:s');

    $err_msg  = PHP_EOL.$args['message'].PHP_EOL.PHP_EOL;
    $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
    $err_msg .= $args['type'].' [Code: '.$args['code'].']'.PHP_EOL.PHP_EOL;
    $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
    $err_msg .= 'Server: '.preg_replace('/www./i', '', filter_input(INPUT_SERVER, 'HTTP_HOST', FILTER_SANITIZE_STRING)).PHP_EOL.PHP_EOL;
    $err_msg .= 'File: '.$args['file'].PHP_EOL.PHP_EOL;
    $err_msg .= 'Line: '.$args['line'].PHP_EOL.PHP_EOL;
    $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
    $err_msg .= 'PHP Version: '.phpversion().PHP_EOL.PHP_EOL;
    $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
    $err_msg .= 'Remote Address: '.filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_SANITIZE_STRING).PHP_EOL.PHP_EOL;
    $err_msg .= 'Request URI: '.filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_STRING).PHP_EOL.PHP_EOL;
    $err_msg .= 'User Agent: '.filter_input(INPUT_SERVER, 'HTTP_USER_AGENT', FILTER_SANITIZE_STRING).PHP_EOL.PHP_EOL;
    $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
    $err_msg .= $e->getTraceAsString().PHP_EOL.PHP_EOL;
    $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
    $err_msg .= 'End of report - '.$args['time'];

    $excHash = array();

    if (isset($_COOKIE['MY_EXC_HASH']) && !is_null($_COOKIE['MY_EXC_HASH'])) {
        
        $excHash = unserialize(base64_decode($_COOKIE['MY_EXC_HASH']));
    }

    if (!in_array($hash, $excHash)) {

        $to = 'webmaster@deine-domain';

        $server = preg_replace('/www./i', '', filter_input(INPUT_SERVER, 'HTTP_HOST', FILTER_SANITIZE_STRING));
        $from = $server.' <exception@'.$server.'>';
        $subject = $args['type'].' - '.$server;

        $header  = "From: ".$from.PHP_EOL;
        $header .= "Message-Id: <".sha1(microtime())."@".$server.">".PHP_EOL;
        $header .= "X-Mailer: PHP ".phpversion().PHP_EOL;
        $header .= "MIME-Version: 1.0".PHP_EOL;
        $header .= "X-Priority: 1".PHP_EOL;
        $header .= "Importance: High".PHP_EOL;
        $header .= "Content-Type: text/plain; charset=utf-8".PHP_EOL;
        $header .= "Content-Transfer-Encoding: 8bit".PHP_EOL;

        if (mail($to, $subject, $err_msg, $header)) {
            
            $cookie = array_merge(array($hash), $excHash);
            setcookie('MY_EXC_HASH', base64_encode(serialize($cookie)), 0, '/');
        }
    }
    
    // Füge hier Code ein um deine benutzerdefinierte Fehlerseite anzuzeigen
    // Zum Beispiel print filge_get_contents('meine-error-seite.html');
}

Shutdown?

Es gibt in PHP eine ganze Reihe an Fehlern, dazu gehören auch die sog. COMPILE_ERROR und CORE_ERROR. Das sind allesamt Fehler die zwar zur Laufzeit passieren aber von der Zend Engine geworfen werfen, kurz bevor das gesamte Skript noch vor der ersten Ausführung abgebrochen wird.

Um von so einem Fehler überhaupt Wind zu bekommen, hilft der nachfolgende Code…

function MyShutdown()
{
    $err = error_get_last();
    
    switch ($err['type'])
    {
        case E_ERROR:
        case E_CORE_ERROR:
        case E_COMPILE_ERROR:
        case E_USER_ERROR:
        case E_RECOVERABLE_ERROR:
        case E_CORE_WARNING:
        case E_COMPILE_WARNING:
        case E_PARSE:

            $to = 'webmaster@deine-domain';

            if (!is_null($to))
            {
                $err_msg  = 'REGISTER SHUTDOWN FUNCTION - FATAL ERROR'.PHP_EOL.PHP_EOL;
                $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
                $err_msg .= $err['message'].PHP_EOL.PHP_EOL;
                $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
                $err_msg .= $err['type'].PHP_EOL.PHP_EOL;
                $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
                $err_msg .= 'Server: '.preg_replace('/www./i', '', filter_input(INPUT_SERVER, 'HTTP_HOST')).PHP_EOL.PHP_EOL;
                $err_msg .= 'File: '.$err['file'].PHP_EOL.PHP_EOL;
                $err_msg .= 'Line: '.$err['line'].PHP_EOL.PHP_EOL;
                $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
                $err_msg .= 'PHP Version: '.phpversion().PHP_EOL.PHP_EOL;
                $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
                $err_msg .= 'Remote Address: '.filter_input(INPUT_SERVER, 'REMOTE_ADDR').PHP_EOL.PHP_EOL;
                $err_msg .= 'Request URI: '.filter_input(INPUT_SERVER, 'REQUEST_URI').PHP_EOL.PHP_EOL;
                $err_msg .= 'User Agent: '.filter_input(INPUT_SERVER, 'HTTP_USER_AGENT').PHP_EOL.PHP_EOL;
                $err_msg .= '- - - - - - - - - - - - - - - - - - - -'.PHP_EOL.PHP_EOL;
                $err_msg .= 'End of report - '.date('d.m.Y - H:i:s');

                $server = preg_replace('/www./i', '', filter_input(INPUT_SERVER, 'HTTP_HOST'));
                $from = $server.' <exception@'.$server.'>';
                $subject = 'SHUTDOWN - '.$err['type'].' - '.$server;

                $header  = "From: ".$from.PHP_EOL;
                $header .= "Message-Id: <".sha1(microtime())."@".$server.">".PHP_EOL;
                $header .= "X-Mailer: PHP ".phpversion().PHP_EOL;
                $header .= "MIME-Version: 1.0".PHP_EOL;
                $header .= "X-Priority: 1".PHP_EOL;
                $header .= "Importance: High".PHP_EOL;
                $header .= "Content-Type: text/plain; charset=UTF-8".PHP_EOL;
                $header .= "Content-Transfer-Encoding: 8bit".PHP_EOL;

                mail($to, $subject, $err_msg, $header);
            }

            $site  = 'System shutdown'.PHP_EOL.PHP_EOL;
            $site .= 'FATAL ERROR'.PHP_EOL.PHP_EOL;
            $site .= 'File: '.basename($err['file']). ' on line '.$err['line'].PHP_EOL.PHP_EOL;
            $site .= 'Error: '.$err['message'];

            ob_clean();
            header('Content-Type: text/html; charset=iso-8859-1');
            header('Content-Length: '.strlen($site));
            header('Content-Transfer-Encoding: binary');
            header('Expires: 0');
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header('Pragma: no-cache');
            exit($site);
    }        
}
Fehlerbehandlung richtig einsetzen
© 2017 Gino Dola
PHP & Web Developer

Schreibe einen Kommentar