Für viele von uns klingt Debugging beängstigend oder wie etwas, das wir vermeiden möchten. Tatsächlich kann man es aber in ein spannendes Jagdspiel verwandeln – es kommt nur darauf an, welche „Waffe“ man wählt. Man kann sich seinen Code unter dem Mikroskop ansehen oder blind mit einem Stock darauf einschlagen, aber im 21. Jahrhundert gibt es bessere Methoden.
Die beste Lösung wäre die Verwendung eines speziellen PHP-Debuggers, wie zum Beispiel XDebug. Hier sind einige Anleitungen zur Verwendung von XDebug mit leistungsstarken IDEs:
In diesem Tutorial bleiben wir jedoch bei der klassischen Methode, d. h. wir verwenden ausschließlich PHP und Cotonti zur Fehlersuche, ohne spezielle Debugging-Tools. Warum? Weil XDebug eine PHP-Erweiterung ist, die nicht auf jedem Server verfügbar ist und deren Installation auf einem lokalen Server etwas Aufwand erfordert. Außerdem gibt es Entwickler, die es gewohnt sind, Fehler manuell zu finden. Cotonti bietet hierfür integrierte Debugging-Funktionen, die wir uns zunutze machen.
Um die integrierten Debugging-Tools zu verwenden, muss der Debugging-Modus in der Datei datas/config.php aktiviert werden:
$cfg['debug_mode'] = true;
Alternativ kann das Debugging-Modul auch manuell eingebunden werden:
require_once $cfg['system_dir'].'/debug.php';
Die einfachste Möglichkeit, den Wert einer Variablen zu überprüfen, ist die Ausgabe auf dem Bildschirm. In Cotonti gibt es jedoch eine Ausgabe-Pufferung und eine Template-Engine, sodass man nicht überall direkt etwas ausgeben kann. Um Werte auf einem leeren Bildschirm zu drucken, kann die Funktion cot_print() verwendet werden:
$qa_abuse = ($usr['id'] == 0 || $row['fp_posterid'] == $usr['id'] ? '' : cot_rc_link(cot_url('plug', 'e=modcp&m=report&post='.$row['fp_id']), $L['qa_report_abuse']); // cot_print($usr['id'], $row['fp_posterid'], $qa_question); // Variablen ausgeben // $qa_best = ($qa_question['q_state'] == QA_QUESTION_OPEN && $usr['id'] == $ft_firstposterid) ? // ...
Es können beliebig viele Variablen als Parameter übergeben werden. Die Ausgabe sieht dann so aus:
Das Problem bei cot_print() ist, dass die Skriptausführung unterbrochen wird. Daher weiß man nach dem Aufruf nicht, was als Nächstes passieren würde. Um zu sehen, wie sich Variablen im Laufe der Ausführung ändern, kann man sie beobachten.
Cotonti verfügt über „manuelle“ Beobachtungsfunktionen. Diese verfolgen Variablen nicht automatisch, sondern protokollieren sie in einer Debugging-Logdatei.
Um cot_watch() und andere Debugging-Funktionen nutzen zu können, muss der Pfad zur Logdatei in datas/config.php angegeben werden:
/** * Pfad zur Debugging-Logdatei. * Dieser Pfad darf nicht für Fremde (z. B. über HTTP) zugänglich sein. * Schützen Sie ihn mit einer .htaccess-Datei oder wählen Sie ein Verzeichnis, * das nur über FTP erreichbar ist. */ $cfg['debug_logpath'] = '/tmp';
Der Datumsanteil im Dateinamen sorgt dafür, dass für jede Debugging-Sitzung eine eigene Logdatei erstellt wird.
Um Variablen zur späteren Analyse zu speichern, kann die Funktion cot_watch() aufgerufen werden:
cot_watch($my_var, $my_array, $obj->id, $arr['item']);
Die Funktion cot_watch() kann beliebig oft aufgerufen werden. Sie protokolliert lediglich die Werte und den Speicherort in der Logdatei. Nach der Skriptausführung kann die Logdatei geöffnet werden, um zu sehen, wie sich die Werte während der Ausführung geändert haben.
Ein Backtrace zeigt die gesamte Sequenz von inkludierten Dateien und aufgerufenen Funktionen (mit ihren Argumentwerten) bis zu dem Zeitpunkt an, an dem das Backtrace erstellt wird.
Der häufigste Fall ist ein kritischer SQL-Fehler, bei dem man nicht weiß, wo genau die problematische Abfrage im Code steht. Um sich selbst zu helfen, kann die Debugging-Ausgabe in datas/config.php aktiviert werden:
/** * Anzeige von Debugging-Informationen bei kritischen Fehlern. * Setzen Sie dies auf TRUE, wenn Sie experimentieren. * Setzen Sie es auf FALSE für Produktionsumgebungen. */ $cfg['debug_mode'] = TRUE;
Wenn dann ein SQL-Fehler auftritt, wird eine detaillierte Rückverfolgung angezeigt, die hilft, das Problem zu identifizieren:
Falls erforderlich, kann eine Rückverfolgung auch direkt im Code mit cot_backtrace() ausgegeben werden:
cot_backtrace(); // Zeige den Funktionsaufruf-Stack an
Einige Funktionen wie cot_backtrace() haben ein $clear_screen
-Argument, das standardmäßig auf TRUE gesetzt ist. Falls es auf FALSE gesetzt wird, wird die Ausgabe innerhalb der normalen Seitenstruktur angezeigt:
cot_backtrace(FALSE); // Bildschirm nicht leeren
Wenn Beobachtung und Rückverfolgung nicht ausreichen, um ein Problem zu analysieren, sind Variablen-Dumps und Kontrollpunkte nützlich.
cot_vardump();
cot_vardump() gibt die gesamte Struktur und den Inhalt aller globalen Variablen aus, die zum Zeitpunkt des Aufrufs verfügbar sind. Dies kann eine sehr große HTML-Seite erzeugen, daher sollte die Suchfunktion des Browsers (Strg+F) genutzt werden, um gezielt nach einer bestimmten Variable zu suchen.
cot_vardump() gibt die gesamte Struktur und den Inhalt aller globalen Variablen aus, die zum Zeitpunkt des Aufrufs vorhanden sind. In der Regel erzeugt dies eine sehr große HTML-Seite, daher ist es ratsam, Strg+F zu verwenden, um die gewünschte Variable zu finden.
Wenn Sie eine Funktion debuggen und lokale Variablen anzeigen möchten, können Sie das Makro COT_VARDUMP_LOCALS verwenden:
function foo($arg1, $arg2)
{
global $cfg;
$my_var = 'test';
eval(COT_VARDUMP_LOCALS);
}
Dies gibt den Inhalt von $arg1, $arg2, $cfg und $my_var aus.
Manchmal ist es praktischer, Variablen in einer Logdatei zu speichern, anstatt sie auf dem Bildschirm auszugeben. Professionelle Debugging-Tools verwenden „Breakpoints“, um das Programm Schritt für Schritt auszuführen und Variablen zu analysieren. In PHP können wir das Skript jedoch nicht einfach anhalten und fortsetzen. Deshalb verwenden wir stattdessen „Kontrollpunkte“.
Wenn ein Kontrollpunkt erreicht wird, speichert das Skript den aktuellen Zustand und den Wert der Variablen in einer Logdatei. Mit mehreren Kontrollpunkten können Sie genau nachvollziehen, was Ihr Skript während einer Debugging-Sitzung tut.
Angenommen, ich muss eine Funktion debuggen, die das Level eines Benutzers basierend auf seinen Punkten synchronisiert. Dafür verwende ich das Makro COT_CHECKPOINT_LOCAL:
function qa_sync_scorelvl($user_id = NULL) { global $usr, $qa_levels, $db_users; if (is_null($user_id)) { $user_id = $usr['id']; $urr['score'] = $usr['score']; $urr['scorelvl'] = $usr['scorelvl']; } else { $urr = qa_get_score($user_id); } // eval(COT_CHECKPOINT_LOCALS); // lokale Variablen speichern // if (isset($qa_levels[$urr['scorelvl'] + 1]) && $urr['score'] >= $qa_levels[$urr['scorelvl'] + 1]['minscore']) { // ... } // ... return $urr['scorelvl']; }
Nach einigen Aktionen im Browser öffne ich die Debugging-Logdatei und sehe folgende Ausgabe:
/home/trustmaster/htdocs/answers/plugins/answers/inc/points.php, 184: user_id = 2 usr = Array ( [id] => 1 ... [score] => 4 [scorelvl] => 6 [logpoint] => 1241583533 [isadmin] => 1 [auth_write] => 1 [auth_read] => 1 ) qa_levels = Array ( [0] => Array ( [name] => None [minscore] => -1000 ) [1] => Array ( [name] => One [minscore] => 0 ) [2] => Array ( [name] => Two [minscore] => 50 ) ... ETC. ) db_users = cot_users urr = Array ( [0] => 76 [1] => 2 ) ----------------
Ich bemerke, dass die Werte von urr = Array numerische Indizes haben, während die Funktion eigentlich assoziative Schlüssel erwartet. Nachdem ich diesen Fehler korrigiert habe, funktioniert die Funktion einwandfrei.
Wenn ich ein Plugin debuggen möchte, das mit globalen Variablen arbeitet, verwende ich die Funktion cot_checkpoint():
$qa_abuse = ($usr['id'] == 0 || $row['fp_posterid'] == $usr['id'] ? '' : cot_rc_link(cot_url('plug', 'e=modcp&m=report&post='.$row['fp_id']), $L['qa_report_abuse'])); // cot_checkpoint(); // globale Variablen speichern // $qa_best = ($qa_question['q_state'] == QA_QUESTION_OPEN && $usr['id'] == $ft_firstposterid) ? // ...
Die Logdatei kann ziemlich groß sein, da jede vollständige Speicherung etwa 1 MB umfasst (was immer noch weniger ist als bei den meisten CMS-Systemen). Um bestimmte Variablen zu finden, verwende ich die Suchfunktion meines Editors:
Sicherlich sind diese einfachen Funktionen nicht so komfortabel wie professionelle Debugging-Tools (wenn man weiß, wie man sie benutzt). Aber wenn Sie jemals daran gedacht haben, in Ihrem Code eine Zeile wie echo 'Was läuft hier falsch?'
einzufügen, werden diese Tools Ihre Arbeit erheblich erleichtern.
Und denken Sie daran: Debugging kann viel mehr Spaß machen, als Sie denken! Sie haben vielleicht kein Maschinengewehr, aber Sie haben viele Werkzeuge, um Bugs zu eliminieren!
Thanked: 12 mal