Debugging
Nur selten gelingt es auf Anhieb ein korrekt lauffähiges PHP Programm zu schreiben. Das Suchen und die Beseitigung von Fehlern (Debugging) ist also im Programmierprozess eher die Regel als die Ausnahme.
Hier sollen ein paar Techniken, die sich in der Praxis bewährt haben, beschrieben werden.
PHP ist in der Lage Fehlermeldungen auszugeben die einen Hinweis auf das Problem geben. Diese Fehlermeldungen sollten aber unbedingt im Produktivbetrieb ausgeschaltet werden, um Hackern keine Informationen zu geben.
Die Fehlermeldungen sind nach Schwere gestaffelt: NOTICE, WARNING, ERROR
Wenn Sie Zugriff auf den Server und die PHP.ini Datei haben können Sie darin folgende Zeile finden:
error_reporting = E_ALL & ~E_NOTICE
E_ALL schaltet alle Fehlermeldungen ein und ~E_NOTICE nimmt die NOTICEs davon aus. Hinweise auf weitere Möglichkeiten finden sich in der Datei.
Desweiteren müssen Sie die Ausgabe mit display_errors=On in der PHP.ini anschalten. Auf dem Produktivserver muss das natürlich ausgeschaltet sein!
Wenn Sie keinen Zugriff auf den Server und die PHP.ini haben, können Sie die Servereinstellungen im Skript mit ini_set() überschreiben.
ini_set("error_reporting", E_ALL);
ini_set("display_errors", 1);
Umgekehrt können Sie die Ausgabe von Fehlermeldungen für einzelne Anweisungszeilen (nicht bei Kontrollstrukturen) mit einem @ davor explizit unterdrücken. Überlegen Sie sich aber genau ob Sie das tatsächlich wollen.
Gewöhnen Sie sich unbedingt eine saubere Formatierung ihres Quellcodes an. Mit guten Einrückungen sehen Sie sofort nicht geschlossene Klammern und Anderes. Solche Fehler in einem schlecht formatierten Text zu finden gleicht einem Sehtest und endet in der Verzweiflung.
Ein schlechtes Beispiel:
$a = 5;
if ($a >10)
$a++;
else
$a = 0;
echo "A ist zu klein";
echo $a;
"A ist zu klein" wird hier natürlich immer ausgegeben. Die Einrückung suggeriert etwas anderes.
Nebenbei: Gewöhnen Sie sich auch an bei bedingten Verzweigungen immer die geschweiften Klammern zu verwenden, auch wenn nach dem if nur eine Anweisungszeile kommt.
Seit PHP 5 ist es möglich einen Teil des Programmcodes in einen try-Block einzuklammern. Sollte in diesem Block ein Fehler auftreten, wird ein zweiter Block, des catch-Block abgearbeitet.
function dividiere($a,$b) {
if ($b == 0) throw new Exception("Division durch 0");
return $a/$b;
}
try {
echo dividiere(2,0);
}
catch (Exception $fehler) {
echo "Fehler: ".$fehler->getMessage();
}
echo $var eignet sich immer gut um den Inhalt einer Variablen an einer bestimmten Stelle auszugeben. Indem der echo Befehl schrittweise versetzt wird, kann man die Veränderungen einer Variablen nachverfolgen.
Wenn es sich im Arrays oder komplexere Objekte handelt, eignet sich print_r($var) besser. In Verbindung mit dem HTML <pre> Tag wird die Ausgabe auch recht übersichtlich.
Es gibt Stellen im Programm die immer wieder Probleme bereiten. Daher lohnt es sich manchmal kleine Debug-Skripten einzubauen, die nach belieben ein- und ausgeschaltet werden können.
if (true) {
echo '<pre>';
print_r($var);
echo '</pre>';
}
false in der If-Bedingung schaltet die Ausgabe ab.
Interessant ist auch die Funktion assert(). Dieser Funktion wird eine Annahme übergeben, die einen boolschen Wert liefern muss. Generiert assert() dann false, wird eine PHP Warning ausgegeben.
ini_set("error_reporting", E_ALL);
ini_set("display_errors",1);
$x = "foo";
assert($x != "foo");
Ergebnis:
Warning: assert() [function.assert]: Assertion failed in ...test.php on line 9
Noch besser ist, die Parameter für assert() als String zu schreiben.
$x = "foo";
assert("$x != 'foo'");
Dann können Sie im Ergebnis gleich die Bedingung und die Werte der Variablen sehen:
Warning: assert() [function.assert]: Assertion "$x != "foo"" failed in ...test.php on line 8
Damit lässt sich auch gut prüfen, ob Angaben aus Formularen übermittelt wurden.
assert($_SESSION['key']);
Mit den assert_options() können Sie das Verhalten von assert() steuern. Damit wird es flexibler als If-Abfragen.
Ein- und Ausschalten von assert() | ASSERT_ACTIVE | 1 (oder 0) |
Soll eine Warnung ausgegeben werden? | ASSERT_WARNING | 1 (oder 0) |
Unterdrückt Fehlermeldungen wenn 1 | ASSERT_QUIET_EVAL | (1 oder) 0 |
Beendet das Programm wenn eine Fehlermeldung generiert wird | ASSERT_BAIL | (1 oder) 0 |
Name der Callback Funktion, wenn ein Fehler auftritt | ASSERT_CALLBACK | null (oder name) |
Z.B.: assert_options(ASSERT_ACTIVE, 1);
Die Callback Funktion bekommt automatisch verschiedene Informationen mit:
assert_options(ASSERT_CALLBACK, "fehlerbehandlung");
function fehlerbehandlung ($datei, $zeile, $text) {
....
}
Mit
$debug = debug_backtrace();
var_dump($debug);
können die letzten Funktionsaufrufe ausgelesen, bzw. mit debug_print_backtrace() direkt ausgegeben werden.
Um in sehr komplex verzweigten Programmen herauszufinden, was abgearbeitet wird, lassen sich an allen möglichen Stellen echo "A"; echo "B"; echo "C"; u.s.w. einbauen. Anhand der ausgegebenen Buchstaben kann dann der Programmfluss nachvollzogen werden.
Zeigt die Seite gar nichts mehr an, ist ein Syntaxfehler sehr wahrscheinlich. Besonders oft sind es vergessene Semikolon am Ende einer Anweisungszeile oder vergessene Klammern, die Blöcke schliessen.
Um die fehlerhafte Stelle zu finden, kann man größere Bereiche des Quelltextes mit /*....*/ auskommentieren. Wenn die Seite dann wieder angezeigt wird, kommentiert man Zeile für Zeile wieder ein.
1) Vergleiche
Wenn eine Funktion einen Wert zurückgibt und Sie diesen Rückgabewert testen wollen sollten Sie aufpassen. Da false in PHP auch mit 0 und true mit 1 assoziiert wird, können sie mit == nicht unterscheiden ob die Funktion 0 oder false (Variable wurde durch die Funktion gar nicht nicht gesetzt) zurückliefert. Sie müssen in diesem Fall mit === auf Identität prüfen.
$erg = foo();
if (false === $erg) ...