Dynamische Webseiten mit PHP

Debugging

Debugging von PHP-Skripten

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.

Allgemeines

  • Seien Sie penibel!
    Achten Sie sehr sehr sorgfältig auf Groß- und Kleinschreibung, auf das Semikolon am Ende jeder Anweisungszeile, auf das Schließen der Klammern etc.
  • Arbeiten Sie sehr kleinschrittig!
    Testen sie immer und immer wieder sofort jedes veränderte Detail. Je mehr Sie verändern, desto mehr Code müssen Sie nach Fehlern durchsuchen und desto mehr Fehler können sich eingeschlichen haben.
  • Nutzen Sie die offizielle Dokumentation
    Unter der Adresse http://www.php.net/manual/de/ finden Sie die offizielle PHP Dokumentation, die vielerlei Hilfestellung geben kann (auch in Deutsch).

Fehlerausgabe

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.

Code formatieren!

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.

Exception-Handling

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();
}

In die Variablen gucken

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.

Auf logische Fehler testen

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.

Wohin verzweigt das Programm?

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.

Die Seite bleibt Weiss

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.

Diverses

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) ...