Startseite >> PHP-Debugging leicht gemacht

PHP-Debugging leicht gemacht

Rubrik: PHP, zu den Kommentaren

Um eine Anwendung zu debuggen kann man eine Menge Blödsinn in seinen Code schreiben. Es ist nun mal leider so, dass die Zeit zum Auffinden eines Fehlers umgekehrt proportional zur Schwere des Fehlers ist. Der beste Weg wäre natürlich immer die Benutzung eines richtigen Debuggers - die Benutzung ist aber meistens nicht ganz einfach. Es gibt nämlich noch eine zweite Eigenart von Fehlern. Sie treten erst auf, wenn man die Anwendung auf dem Produktivserver installiert hat - in der Test- bzw. Entwicklungsumgebung funktioniert alles. Spätestens hier kommt man mit dem lokal installierten Debugger nicht weiter.

Da wir aber unseren Code nicht mit unendlich vielen print, print_r, var_dump und error_log-Anweisungen verunstalten wollen, muss eine praktischere und einfachere Lösung her. Genau bei diesem Problem hat mir mal wieder das Buch PHP Design Patterns weiter geholfen.

Als erstes eine Überlegung, wo wir eigentlich hin wollen. Es soll möglich sein, Debug-Meldungen auf unterschiedliche Art und Weise auszugeben. Vom einfachen echo über ein Logfile bis zu E-Mail oder Jabber. Die Abschaltung des Debuggers nicht zu vergessen - dies sollte ebenfalls ohne große Änderungen am Code möglich sein.

Jeder der Ausgabewege muss natürlich extra programmiert werden. Um ein bisschen Ordnung in die verschiedenen Klassen zu bringen, bietet es sich an ein Interface zu implementieren, welches die Mindest-Funktionalität der verschiedenen Debugger vorgibt. Die Schnittstelle eines Debuggers beschränkt sich hier auf die Funktion debug(). Mehr sollen die Debugger erst einmal nicht können.

<?php
/**
 * Interface Debugger.
 * 
 * Definiert die Funktion, die von jedem Debugger implementiert werden muss.
 *
 */
interface Debugger{
    
/**
     * Ausgabe einer Debug-Meldung
     *
     * @param string $message Die Meldung
     */
    
public function debug($message);
}
?>

Da wir verschiedene Debugger haben und diese auch beliebig kombinieren wollen, z.B. Logfile immer, dazu dann echo, E-Mail oder Jabber, müssen diese irgendwie "kontrolliert" werden. Für diese Aufgabe bietet sich das Composite-Pattern an. Unsere Composite-Debugger-Klasse ist selbst wieder ein Debugger, implementiert also unser Interface. In dieser Klasse können dann beliebige Debugger registriert werden. Alle Debug-Meldungen gehen an die Composite-Klasse und diese delegiert die Meldungen dann an alle registrierten Debugger weiter. Welche das sind, wird über die Config eingestellt.

<?php
/**
 * Composite-Klasse, die Debug-Nachrichten an mehrere Debugger weiterleiten kann.
 * Diese Klasse wird eigentlich immer als Debugger benutzt, je nach gewünschtem
 * Debug-Verhalten können beliebige Debugger registriert werden.
 *
 */
class DebuggerComposite implements Debugger{
    
    protected 
$debuggers = array();
    
    
/**
     * Einen weiteren Debugger registrieren
     *
     * @param Debug_Debugger $debugger
     */
    
public function addDebugger(Debugger $debugger){
        
$this->debuggers[] = $debugger;
    }
    
    
/**
     * Ausgabe der Debug-Meldungen.
     * Die Meldungen werden an alle registrierten Debugger weiter geleitet.
     *
     * @param string $message Die Debug-Meldung
     */
    
public function debug($message){
        foreach(
$this->debuggers as $debugger){
            
$debugger->debug($message);
        }
    }
}
?>

Als Beispiel für einen Debugger sei hier mal die Ausgabe in eine Log-Datei angegeben. Die Klasse implementiert das Singleton-Pattern, da von dem Debugger immer nur eine Instanz benötigt wird. Der Funktion getInstance() wird einfach das Logfile übergeben. Die debug()-Funktion benutzt dann die Funktion error_log zur Ausgabe Der Debug-Meldung. Es muss natürlich dafür gesorgt werden, dass die Datei, in die geschrieben werden soll, im richtigen Verzeichniss vorhanden ist und PHP schreibend darauf zugreifen kann.

<?php
/**
 * Klasse zum schreiben von Debugmeldungen in ein Logfile
 *
 */
class DebuggerLog implements Debugger {
    
    protected 
$logfile null;
    private static 
$instance null;
    
    public static function 
getInstance($logfile){
        if(
self::$instance === null){
            
self::$instance = new DebuggerLog($logfile);
        }
        return 
self::$instance;
    }
    
    protected function 
__construct($logfile){
        
$this->logfile $logfile;
    }
    
    private function 
__clone(){}
    
    public function 
debug($message){
        
error_log($this->addTimestamp($message), 3$this->logfile);
    }
    
}
?>

Die Benutzung der Debugger funktioniert folgendermaßen. Im Skript basics.php (siehe Blog mit PHP) wird eine Instanz der Composite-Klasse erzeugt, die gewünschten Debugger registriert und die Klasse dann in der Registry registriert. Fortan kann von jeder Stelle im Programmcode aus der Debugger angefordert werden und eine Debug-Meldung ausgegeben werden. Das könnte z.B. so aussehen:

Ausschnitt aus basics.php:

<?php
$debuggerComposite 
= new DebuggerComposite();
$debuggerVoid DebuggerVoid::getInstance();
$debuggerComposite->addDebugger($debuggerVoid);
if(
$settings->get('debug.debug') == 1){
    
// DebuggerEcho registrieren
    
if($settings->has('debug.echo') && $settings->get('debug.echo') == 1){
        
$debuggerEcho DebuggerEcho::getInstance();
        
$debuggerComposite->addDebugger($debuggerEcho);
    }
    
// DebuggerLog registrieren
    
if($settings->has('debug.logfile')){
        
$debuggerLog DebuggerLog::getInstance('logfile');
        
$debuggerComposite->addDebugger($debuggerLog);
    }
}
$registry->registerDebugger($debuggerComposite);
?>

Wie leicht zu erkennen, passieren hier zweierlei Dinge. Zum einen wird der Debugger DebuggerVoid immer registriert, zum anderen werden die weiteren Debugger nur registriert, wenn der Debug-Modus angeschaltet ist und bestimmte weitere Einstellungen in den Settings vorhanden sind. (Meiner Registry-Klasse habe ich zwei weitere Funktionen zum registrieren und anfordern des Debuggers spendiert - zur Registry siehe Teil 1)

Der Debugger DebuggerVoid hat garkeine Funktion, die debug()-Funktion dieses Debuggers tut einfach nichts. Dies hat den Vorteil, dass der Code zur Ausgabe der Debug-Meldungen nicht unbedingt aus dem Code entfernt werden muss. Der Code kann weiterhin vorhanden sein und erzeugt keine Fehler, auch wenn das Debugging abgeschaltet ist. Die Nachrichten gehen einfach an den DebuggerVoid und "verschwinden".

Der Lohn der ganzen Mühe ist zum einen das einfachere Konfigurieren sowie abschalten des Debuggings, sowie die einfache Benutzung. An jeder beliebigen Code-Stelle kann jetzt der Debugger angefordert werden und Debug-Meldungen ausgegeben werden. Und das alles in einer einzigen Code-Zeile - und die sieht so aus:

<?php
Registry
::getInstance()->getDebugger()->debug('Debug-Meldung');
?>

Und schon wird die Meldung an alle registrierten Debug-Klassen weitergeleitet und wandert somit in alle gewünschten Meldungskanäle. Wenn eine Instanz der Klasse Registry bereits vorhanden ist, so kann diese natürlich verwendet werden anstelle von Registry::getInstance().

Viel Spaß beim Debuggen - und - naja - alle Angaben ohne Gewähr ;-)

yigg

0 Kommentare zu: PHP-Debugging leicht gemacht

Einen Kommentar schreiben






Captcha

Buch-Box

PHP Design Patterns
Die Entwicklung des Debuggers zieht sich durch die Kapitel 2, Gutes Software-Design und 3, Erzeugungsmuster und wird schrittweise von einer recht einfachen Lösung zu einer praktischen Implementierung weiter entwickelt.
Mehr Informationen bei:
Amazon.de

Beiträge nach Rubrik
Amazon.de: Bestseller RSS