Jeder kennt das Problem, es gibt nicht nur Syntaxfehler, sondern auch Fehler,
die durch Fehleingaben und technische Unzulänglichkeiten (Verbindungsabbrüche usw.)
entstehen und in den meisten Fällen zur Laufzeit des Programmes auftreten.
Für Anwender und Entwickler ist dies eine Störquelle, welche beim einen für
Unverständnis des Auftretens und beim anderen für Schwierigkeiten bei der Behebung
sorgt, da bestimmte Zustände wieder hergestellt werden müssen um diesen Fehler
zu replizieren.
Als erstes ist es wichtig, dass der Anwender keine Fehlermeldungen zu Gesicht
bekommt bzw. nur in einer "hübschen" Form.
Ein einfaches @ vor der jeweiligen Funktion oder Befehl. Die Fehlerausgabe kann
unterdrückt werden damit.
Ein einfaches Beispiel:
Eine Variable deren Inhalt eigentlich ein Array sein sollte wird benutzt, jedoch
beinhaltet sie aufgrund eines Fehlers ein String.
$value="x";
Der Aufruf mit
current($value);
erzeugt eine Fehlerausgabe:
Warning: Variable passed to current() is not an array or object in ...
Wir unterdrücken diese Ausgabe mit einem @.
@current($value);
Das Problem ist aber jetzt, das es schwer wird den Fehler zu finden.
Nun ist es möglich das Auftreten eines Fehlers mit or die() abzufangen und
somit sichtbar zu machen.
@current($value) or die("Ein Fehler ist aufgetreten.");
Die nun erfolgende Textausgabe ist zwar hübscher, bringt uns aber nicht sehr
viel weiter. Um mehr Flexibilität zu haben schreiben wir uns am besten eine
Funktion, die das die() ersetzt und uns mehr Informationen liefert und nicht
gleich das Skript abbricht. Wir nennen sie mal debug();
Selbst wenn die Funktion ein "reguläres" false zurückliefert
(z.B. is_array($values)) kann die() oder unsere spätere Funktion genutzt werden.
Wir müssen dieser Funktion natürlich einige Informationen übergeben damit diese
dann entsprechend reagieren kann und wir müssen uns auch ein paar Parameter
einfallen lassen, denn im Betrieb soll diese Funktion etwas anders reagieren als
während der Entwicklungszeit.
$print_error=TRUE; // wird auf TRUE gesetzt, wenn der Fehler direkt auf dem
// Bildschirm ausgegeben werden soll (für Entwickler)
// andernfalls auf FALSE
$debug_level=3; // dient zur Steuerung ab welchen Level die Fehler ausgegeben
// werden sollen (dazu später mehr)
Jetzt müssen wir nur noch wissen wo und wann dieser Fehler auftrat.
Zum Einsatz kommen zwei Konstanten, welche einige bestimmt aus der C-Welt kennen,
und im PHP Manual dokumentiert sind.
__LINE__ ist gesetzt auf die aktuelle Zeile
__FILE__ ist gesetzt auf den aktuellen Pfad der Datei
Da Fehler ungleich Fehler ist und ein Auftreten nicht unbedingt gleich den
Abbruch der Skriptes erfordert ist es ratsam eine Art Fehlerlevel festzulegen.
Hier unterscheiden wir nur zwischen Ausgaben die als Programmierhilfe dienen
auch ohne das Fehler auftreten (Level 3). Fehler, die wenn sie auftreten zwar
bestimmte Funktionen beeinträchtigen, aber den Ablauf des Gesamtskripts nicht
gefährden (Level 2) und Fehler, die sehr schwer wiegen und möglicherweise
Sicherheit oder Ablauf des Skriptes ganz unmöglich gestalten (Level 1).
Dies ist nur eine grobe Einteilung
Jetzt bauen wir mal die Funktion debug.
Der Aufruf der Funktion kann so gestaltet werden:
(der Fehler ist jetzt mal leichter Natur)
is_array($value) or debug(2,__LINE__,__FILE__,"Variable value : Das Array ist Fehlerhaft");
und normale Ausgaben als Programmierhilfe:
debug(3,__LINE__,__FILE__,"Elemente im Array".count($value));
Wenn jemand den print_error auf FALSE setzt muss je ein Logfile erzeugt werden:
(Hinweis: Das Logfile sollte bereits existieren und der Webserver muss
Schreibzugriff haben
(z.B. (unter unix/linux)
touch debuglog
chown nobody.nogroup # der Webserver läuft meist als nobody )
function debug_log($debugstring){
if (file_exists("debuglog")){
$fp=fopen("debuglog","a");
fwrite($fp,$debugstring);
fclose($fp);
}
}
function debug($errorlevel,$line,$file,$textstring){
// wir holen die Einstellungen für die Ausgabe in die Funktion
global $print_error;
// je nach einstellung werden alle ausgaben oder erst ab einem bestimmten
// errorlevel ausgegeben
global $debug_level;
// damit der Zeitpunkt des Auftretens festgehalten wird
$errortime=date("d.m.y G:i:s", time());
// jetzt wird der Fehlerstring zusammengesetzt
$errorstring="\n".$errorlevel." ".$errortime." ".$file." ".$line." ".$textstring;
// unterschiedliche Farben machen die Ausgabe besser deutlich
if($errorlevel==1 && $debug_level>=1){
if ($print_error){
echo ">P<>FONT SIZE=-1 COLOR=\"#FFOO00\"<".$errorstring.">/FONT<</P<";
} else {
debug_log($errorstring);
}
die(); // fehler ist ja schwerwiegend
} elseif ($errorlevel==2 && $debug_level>=2){
if ($print_error){
echo ">P<>FONT SIZE=-1 COLOR=\"#0000FF\"<".$errorstring.">/FONT<>/P<";
} else {
debug_log($errorstring);
}
} else {
if ($print_error){
echo ">P<<FONT SIZE=-1 COLOR=\"#00FF00\"<".$errorstring.">/FONT<>/P<";
} else {
debug_log($errorstring);
}
}
}
Datenbank Fehler treten sehr häufig auf. Damit wir eine gute Ausgabe erzeugen
verwenden wir ebenfalls eine kleine Funktion. Hier Oracle:
$type beinhaltet den Typ entweder ein Statement, eine Verbindung usw. oder
wenn sie leergelassen wird, wird der zuletzt auftretende Fehler genommen.
function database_debug($type,$query="Connection"){
$error=@OCIError($type);
$errorstring=sprintf("OCIError: %s %s Query: %s",
$error["code"],
$error["message"],
$query);
return $errorstring;
}
Der Aufruf ist dann in der Art:
$query="INSERT INTO test (id,text) VALUES ('55','Testtext')";
$stmt=@ociparse($conn,$query)
or debug(1,__LINE__,__FILE__,database_debug($stmt,$query);
Diese Funktion kann nun verwendet werden. Dabei ist auf eine sinnige Verwendung zu achten,
jeden Befehl damit zu Bestücken bläht den Quellcode auf.
Wichtig ist, dass der Text, der übergeben wird als Hilfsmittel zu gebrauchen ist. Texte
wie "Alles ok" sind also zu vermeiden.
Diese Funktion kann beliebig erweitert werden, z.B. verwendet man Sessions, koennen in
diesen zu jedem User "einzeln" bestimmte Einstellungen gespeichert werden. Es kann der
Pfad zum Logfile in der Session gespeichert werden, die Einstellungen bis zu welchem Level
gelogt wird oder wohin die Mitteilungen geschrieben werden sollen und somit separat
gelogt werden.
Wird neben dem Text noch die entsprechende Userid oder spezielle Kennung für die
jeweilige Webseite, kann man einzelne Benutzer "isolieren" und durch das System
verfolgen. (Usertracking)
Ist die Zeit knapp Logfiles zu studieren, so können auch durch die Verwendung
von der allseits beliebten mail() Funktion schwerwiegende Fehler direkt versand werden.
Ich hoffe dieser Artikel gibt dem einen oder anderen etwas Inspriration und hilft
bei der Verwirklichung grösserer Projekte.
Ich bedanke mich für das Interesse.
Über den Autor:
Thomas Fromm ist Student an der Universität Potsdam und bewegt sich seit seit 1999
verstärkt in der PHP Scene. Neben einigen anderen Projekten gilt seine Aufmerksamkeit
besonders der Firma InUBIT(www.inubit.com) in Berlin, wo
er als Mitarbeiter an der Entwicklung anspruchsvoller B2B-Marktplatzlösungen auf der
Basis von PHP und anderen Technologien mitwirkt. |