Datenbank-Fehlerbehandlung in PHP - verschiedene Wege und Möglichkeiten

Flattr this
Tweet this: Datenbank-Fehlerbehandlung in PHP - verschiedene Wege und Möglichkeiten
Dent this: Datenbank-Fehlerbehandlung in PHP - verschiedene Wege und Möglichkeiten
Datum: 16.07.2009 12:00:00

Dieser kleine Artikel soll sich mit Thema der Fehlerbehandlung in PHP, speziell beim Arbeiten mit Datenbanken auseinandersetzen, dabei werden verschiedene Wege und Möglichkeiten aufgezeigt und verglichen. Er zeigt dabei auch etwas meine eigene Weiterentwicklung über die Jahre die ich mich schon mit PHP und anderen Programmiersprachen beschäftige.

1) Fehlerbehandlung via die()

Der wohl am meisten verbreitete und bekannteste Weg zur "Fehlerbehandlung" in PHP bei der Benutzung von Datenbanken dürfte wohl die benutzung von die() sein. Man findet sie in Foren, Code-Beispielen in vielen Blogs und Büchern und Tutorials.

Einfaches Beispiele:

<?php

$connection = mysql_connect('localhost', 'baa', 'foo')  or die(mysql_error());
$selected   = mysql_select_db('baa', $connection)       or die(mysql_error());
$query      = mysql_query('SELECT baa FROM foo')        or die(mysql_error());

?>

Einen wirklichen Vorteil dieser Methode ausser der Simplizität mit der sie sich im Code einbinden lässt gibt es eigentlich nicht. Aber dafür einige Nachteile: Das Script bricht an dieser Stelle komplett ab und der Kunde bzw. User der Seite bekommt einerseits eine unschöne Fehlermeldung undabhängig vom Fehler und der Meldung auch Informationen darüber was in dem Script gerade passiert / abläuft (Sicherheit) Desweiteren ist es nicht wirklich gleich ersichtlich WO genau der Fehler aufgetreten ist, da keinerlei Angaben über die betroffene Datei und Zeilennummer gemacht wird und keine Informationen zum Query und verwendeten Parametern bekannt sind. Ein weiteres Problem bei der Verwendung von die() ist, dass wenn der Fehler nicht gerade im Entwicklungsbetrieb einem Entwickler auffällt er sehr wahrscheinlich nie gemeldet wird.

Wenn auch am weitesten verbreitet sollte man von dieser Methode möglichst schnell Abstand nehmen, weil sie keine wirkliche Fehlerbehandlung ermöglicht, sondern nur eine möglicherweise sicherheitsbedenkliche Fehlermeldung ausgibt und dann den kompletten Script-Ablauf stoppt. Gerade mit der dann meistens nicht vorhandenen Trennung von Code und Layout ist eine zerhackte und unschöne Ausgabe schon vorprogrammiert.

2) Erweiterte Fehlerbehandlung mit Logging/Mailing via einfacher Wrapper-Funktion

Einen einfachen Weg stellt die Verwendung von Wrapper-Funktionen für die gängigen Befehle: Datenbank-Verbindung aufbauen, Datenbank auswählen und die Datenbankabfragen selbst dar. Hier kann man sehr einfach und projektübergreifend zumindest ein Logging (z.b. mit PEAR Log oder Zend_Log) oder Email versenden (z.b. mit Swiftmailer, phpmailer, PEAR Mail oder Zend_Mail) für Fehler implementieren und neben dem Query auch Informationen z.b. Über die Url ($_SERVER), in welcher Datei + Zeile der Fehler aufgetreten ist (debug_backtrace()) und weitere Informationen die den Request betreffen ($_POST, $_GET, $_COOKIE, $_SESSION ... etc) mitgespeichert werden können.

Einfaches Beispiele:

<?php

function logError($message)
{
    $error = PHP_EOL . $message . PHP_EOL .
             PHP_EOL . print_r(debug_backtrace(), true);
    file_put_contents('error.log', $error);
    mail('admin@project.tld', 'Fehler in Project XYZ', $error);
}

function connectToDatabase($host, $username, $password, $database)
{
    $connection = mysql_connect($host, $username, $password);
    if ($connection === false) {
        echo 'Es ist ein unerwarteter Fehler aufgetreten, der Administrator wurde informiert';
        logError("Fehler beim Aufbau der Verbindung zur Datenbank: " . mysql_error());
        exit();
    }
    $selected = mysql_select_db($database);
    if ($selected === false) {
        echo 'Es ist ein unerwarteter Fehler aufgetreten, der Administrator wurde informiert';
        logError("Fehler bei der Auswahl der Datenbank" . mysql_error());
        exit();
    }
}

function queryDatabase($query)
{
    $query = mysql_query($query);
    if ($query === false) {
        echo 'Es ist ein unerwarteter Fehler aufgetreten, der Administrator wurde informiert';
        logError("Fehler im Query: " . $query . PHP_EOL . PHP_EOL . "Fehler: " . mysql_error());
        exit();
    }
}

?>

Dieses Methode erlaubt schon eine deutlich bessere Behandlung von Fehlern, der User bekommt keine kryptische sondern eine möglichst neutrale Fehlermeldung, der Verantwortliche des System wird informatiert und kann sich um das Problem kümmern. Nachteil ist auch hier weiterhin, dass es zu einem kompletten Abbruch des Scriptes kommt und der Fehler in keiner Form behandelt wird.

3) Richtige Fehlerbehandlung mit Exceptions

Eine weitere Methode der Behandlung von Fehler (nicht nur beim arbeiten mit Datenbanken) ist das Arbeiten mit Exceptions. Hier wird in einem try-Block der eigentliche Code geschrieben und in Catch-Blöcken kann man die auftretenden Exceptions auffangen und dann passend behandeln. Auch die Fehlerbehandlung über mehrere Ebenen ist möglich indem man verschachtelten try/catch-Blöcke nutzt und z.b. in einem catch-Block nach der hier möglichen Behandlung die Exceptions "weiterwirft" um sie weiter oben weiter zu behandeln

Im Bezug auf Datenbanken kan man zu diesem Zwecke entweder das Rad neu erfinden, wenn man darauf steht und sich einen eigenen Wrapper um die mysql_*, mysqli_* Funktionen oder pg_sql (oder auch die Funktionen einer anderen Datenbank) basteln oder man setzt auf eine der schon existierenden Datenbank-(Abstraktions)Klasse die von sich aus Exceptions bei Fehlern werfen. Möglichkeiten gibt es hier viele, die seit PHP 5.1 standardmäßig vorhandene Extensions PDO (vor 5.1 als PECL-Extensions) oder auch komplexere (teilweise auf PDO aufbauende) Systeme und Komponenten von Frameworks die zum Teil noch um einiges mehr bieten als nur einen einfachen Wrapper um eine Datenbank-API (z.b. ORM-Systeme):

Weitere Möglichkeiten findet man via Google oder auch bei PHP Classes

Einfaches Beispiele:

<?php

function PrintErrorMessage($message)
{
    /* Ausgabe einer Fehlerseite */
}

function LogException($e)
{
    /* Logging der Exception zusammen mit Umgebungsvariablen und co */
    /* Senden einer Email */
}

/* ... */

$dir = BASE_DIR . '/foo';

try {

    mkdir($dir);
    touch($dir . 'File1');
    touch($dir . 'File2');
    $db->query("INSERT INTO table (id, field1, field2) VALUES (4, 'Baa', 'Foo')");

} catch (DBException $e) {
    // Gemachte Dinge rückgängig machen
    unlink($dir . 'File1');
    unlink($dir . 'File2');
    rmdir($dir);

    LogException($e);
    PrintErrorMessage('Beim anlegen eines Datensatzes ist ein Problem aufgetreten');    
}

?>

Zusammenfassung / persönlicher Fazit

Der Weg der mir mittlerweile am besten zusagt ist die Benutzung von Exceptions, da ich hier in Kombination mit strikter Trennung von Code und Layout und Ausgabenpufferung nicht nur die Möglichkeit habe Fehler zu loggen und dem User eine ordentliche Fehlerseite auszugeben, sondern auch die Möglichkeit habe im catch-Block direkt auf Fehler zu reagieren, Dinge rückgängig zu machen wie erstellte Datensätze, Ordner, Dateien (Stichwort Transaktionen).

Methode Vorteile Nachteile
Methode 1
  • Einfach und schnell in der Anwendung
  • Keinerlei Fehlerbehandlung
  • Keinerlei Logging
  • Abbruch des Scripts
Methode 2
  • Einfacher in der Anwendung als Methode 1
  • Möglichkeit des Loggings
  • Abbruch des Scripts
Methode 3
  • Möglichkeit des Loggings
  • Möglichkeit der Fehlerbehandlung
  • Höhere Komplexität und teilweise mehr Aufwand

Trackbacks (0)

Trackbackurl: http://www.robo47.net/trackback/text/13

Es sind keine Trackbacks vorhanden.


Kommentare (2)

Die Kommentare zu diesem Beitrag sind gesperrt.

You liked it ? Link it on your homepage or blog: