4.2 Fehlerbehandlung 

Zunächst unterscheiden wir zwei Arten von Fehlern: Zum einen gibt es Fehler, die bei der Verwendung einer Applikation auftreten, also Fehler in der Business-Logik. Sie treten meist durch inkorrekte Bedienung oder andere externe Störungen auf, wie beispielsweise durch den Abbruch einer Datenbankverbindung. Bei solchen Fehlern ist es nicht möglich, mit der gewählten Aktion fortzufahren. Die Anwendung ist aber durchaus in der Lage, dem Benutzer den Grund für die Störung mitzuteilen und gegebenenfalls ein gewisses Maß an nützlichen Informationen bereitzustellen. Fehler dieser Art wurden im vorherigen Kapitel, »Die Applikationsbasis«, durch das Signal error induziert, welches z. B. in Abschnitt 3.4.1, »Ein einfacher Action-Controller«, gesendet wird, falls der Aktion zur Darstellung eines Blog-Eintrags keine gültige ID übergeben wurde.
Die zweite Art von Fehlern ist schwerwiegender, denn es handelt sich um Fehler, welche die Applikation in einem nicht mehr zu rettenden Status hinterlassen. Solche Fehler treten meist durch Bugs, also Programmierfehler, auf. Ist dies der Fall, kann die Anwendung nicht mehr korrekt weiterarbeiten und muss den kompletten Vorgang abbrechen. Hierbei kann man von Glück sprechen, wenn die Anwendung dem Benutzer noch eine passable Fehlermeldung anzeigen kann und nicht umgehend sämtliche Arbeit einstellt. Derartige Fehler werden in PHP mit dem Fehlerstatus FATAL gekennzeichnet.
Das Ziel dieses Abschnitts ist es, beide Fehlerarten entsprechend zu verarbeiten und dem Benutzer des GP-Blogs auf bestmögliche Art eine Meldung zukommen zu lassen. Bei fatalen Fehlern bleiben hier nicht viele Möglichkeiten offen: es kann lediglich eine Meldung ausgegeben werden, dass es zu einem solchen Fehler gekommen ist. Bei Fehlern in der Logik ist es immerhin möglich, dem Benutzer einen Hinweis zu geben, wie er fortfahren kann. Der folgende Abschnitt wird sich daher zuerst mit solchen Fehlern beschäftigen. Anschließend werden Sie sehen, wie mit fatalen Fehlern umgegangen werden kann.
4.2.1 Ausgaben puffern 

Um zu verhindern, dass beim Auftreten eines Fehlers noch Teile der korrekten Informationen dargestellt werden, wird der PHP-Ausgabepuffer verwendet. Um diesen einzuschalten, wird eine Änderung in der Datei index.php vorgenommen:
ob_start(); $blog = gpBlogController::getInstance(); $blog->run(); ob_end_flush();
Listing 4.1 Puffern der Ausgabe
Vor dem Start der Ausführung wurde der Befehl ob_start() [http://php.net/ob_start ] eingefügt. Dieser veranlasst PHP, alle von diesem Punkt an getätigten Ausgaben nicht sofort an den Webbrowser zu senden, sondern diese zunächst zwischenzuspeichern. Es ist so später möglich, die gesammelten Ausgaben in einem Rutsch abzuschicken oder auch die Ausgaben zu löschen und somit ungewollte Darstellungen zu vermeiden. Nach der Ausführung des Haupt-Controllers werden die gesammelten Daten mittels ob_end_flush() [http://php.net/ob_end_flush ] gesendet. PHP würde dies implizit beim Beenden des aktuellen Requests selbst erledigen, allerdings ist es sauberer, das Senden im Code explizit auszuführen, damit genau erkennbar ist, an welchem Punkt die eigentliche Ausgabe erfolgt. Im folgenden Abschnitt werden Sie sehen, wo das Puffern der Ausgaben konkret benötigt wird. Denn bisher wird der Interpreter nur angewiesen, die Daten zu puffern und anschließend gesammelt auszugeben, was keinen nennenswerten Effekt hat.
4.2.2 Die Fehler-Aktion 

Das in Kapitel 3, »Die Applikationsbasis«, eingeführte Fehlersignal error stellt einen Fehler in der Applikationslogik dar. Die bisher unterschlagene Behandlung dieses Fehlersignals soll an dieser Stelle implementiert werden. Hierzu wird ein eigener Action-Controller entwickelt, der sich explizit der Darstellung von Fehlern annimmt (zu finden in classes/actions/error.php):
class gpBlogActionError implements gpBlogAction
{
public static function registerSlots()
{
}
public static function error( $message,
ezcUrl $backUrl = null )
{
}
public static function fatal( $message )
{
}
}Listing 4.2 Aktion zum Behandeln der Fehler
Die typische Methode zur Registrierung von Aktions-Slots, registerSlots(), kennen Sie bereits aus dem letzten Kapitel. Sie registriert lediglich zwei neue Slots, von denen bereits einer aus Kapitel 3, »Die Applikationsbasis«, bekannt ist, der Slot error. Neu an dieser Stelle ist der Slot fatal, den Sie in Abschnitt 4.2.3, »Fatale Fehler«, besser kennenlernen werden. Im Unterschied zu den Slots anderer Aktionen, welche alle in der Aktions-Signalsammlung registriert wurden, werden die hier vorgestellten Signale in der Hauptsignalsammlung, wo auch Slots wie display gesammelt werden, registriert. Dieser Umstand soll Sie hier aber nicht weiter interessieren. Mehr Informationen zur SignalSlot-Komponente können Sie in Kapitel 3, »Die Applikationsbasis«, nachlesen.
Das error-Signal bringt zwei Parameter mit sich: Die darzustellende Fehlermeldung und optional eine URL, auf die verwiesen werden soll, um dem Benutzer einen Leitfaden zu geben, wie er fortfahren kann.
public static function error( $message, ezcUrl $backUrl = null )
{
ob_clean();
echo "<b>An error occured!</b>";
echo "<u>Error message was:</u> $message";
if ( $backUrl !== null )
{
echo " <a href='$backUrl'>Back</a>";
}
}Listing 4.3 Ausgabe der erhaltenen Fehler
Zunächst wird innerhalb der Methode der PHP-Ausgabepuffer gelöscht, womit verhindert wird, dass partielle Ausgaben einer Aktion zusammen mit der Fehlermeldung angezeigt werden. Die weiteren Ausgaben dieser Methode werden wiederum in den Ausgabepuffer geschrieben. Er wird, wie Sie wenige Zeilen vorher gesehen haben, zum Ende des Requests an den Browser gesendet. Die Funktion ob_clean() [http://php.net/ob_clean ] löscht den Puffer lediglich, schaltet die Pufferung aber nicht ab.
Das error-Signal erwartet als Parameter die Fehlermeldung, die dem Benutzer angezeigt werden soll. An dieser Stelle wird hier lediglich die Fehlermeldung mit ein wenig HTML-Dekoration ausgegeben. Optional kann dem Signal ein zweiter Parameter übergeben werden, eine Instanz von ezcUrl. Ist dies der Fall, wird zusätzlich zur Fehlermeldung ein Link ausgegeben, den der Benutzer verfolgen kann, um nach dem Fehler fortzufahren. Instanzen von ezcUrl verfügen über die magische Methode __toString() und können somit direkt wie ein String verwendet werden. Das Objekt gibt dabei die Textdarstellung der enthaltenen URL aus. Mehr zu ezcUrl haben Sie bereits in Kapitel 3, »Die Applikationsbasis«, erfahren.
In einer echten Anwendung sollten Sie dem Benutzer an Stelle eines Links direkt eine sinnvolle Seite zeigen, die abhängig vom Fehler, dem Besucher Lösungsmöglichkeiten oder Ablenkung anbietet. Um die Komplexität zu reduzieren verzichten wir in der Beispielapplikation darauf.
| Testen eines Fehlers |
|
Sie können die Fehlerbehandlung in Aktion sehen, wenn Sie zum Beispiel die Aktion show_entry über Ihren Browser aufrufen, ohne einen ID-Parameter anzuhängen. Wie Sie die Beispielapplikation zum Testen aufsetzen, erfahren Sie in Abschnitt 1.3.3, »Testen des GP-Blogs«. Zum Testen des Fehlers benutzen Sie die URL http://gpblog/ show_entry/. |
Die Implementierung der Aktion fatal fällt noch ein wenig spartanischer aus, denn hier wird lediglich die übergebene Fehlermeldung ausgegeben. Wo das entsprechende Signal für diese Aktion gesendet wird, sehen Sie im folgenden Abschnitt.
public static function fatal( $message )
{
ob_clean();
echo "A fatal error occured in the system. Please
contact the administrator for further support.<br />";
echo "Internal error message: '$message'.";
}Listing 4.4 Behandeln eines fatalen Fehlers
4.2.3 Fatale Fehler 

Bei der Behandlung von fatalen Fehlern, wobei eine Exception in PHP ebenfalls zu einem fatalen Fehler wird, falls Sie diese nirgends fangen, kommt die Execution-Komponente ins Spiel. Obwohl Ihre API recht rudimentär und einfach wirkt, so ist sie doch ein sehr mächtiges Werkzeug, um unschöne Fehlereffekte zu vermeiden. Aufgerufen wird die Execution-Komponente zentral in der Datei index.php, die dazu erneut wie folgt erweitert wird:
ezcExecution::init( 'gpBlogExecutionHandler' ); ob_start(); $blog = gpBlogController::getInstance(); $blog->run(); // Ein fataler Fehler könnte hier auftreten... ezcExecution::cleanExit(); ob_end_flush();
Listing 4.5 Fehlerbehandlung initialisieren
Bevor der Haupt-Controller seine Arbeit aufnimmt, wird die Execution-Komponente initialisiert. Der Aufruf der init()-Methode erwartet den Namen einer Klasse als Parameter. Im Falle eines fatalen Fehlers wird auf dieser Klasse die statische Methode onError() aufgerufen. Anschließend wird das PHP-interne Output-Buffering gestartet und sämtliche Ausgaben somit gecachet, bevor sie an den Browser gesendet werden. Sie erinnern sich, dass der Ausgabepuffer in der Methode zur Fehlerbehandlung gelöscht wurde, um partielle Ausgaben echter Inhalte zusammen mit Fehlermeldungen zu verhindern (Abschnitt 4.2.2, »Die Fehleraktion«).
Wie allerdings erkennt die Execution-Komponente einen fatalen Fehler? Dies ist in PHP nicht trivial, da beim Auftreten eines solchen Fehlers der Interpreter umgehend beendet wird. Allerdings ist es möglich, mit Hilfe der PHP-Funktion register_shutdown_function() [http://php.net/register_shutdown_function ] über das Beenden des Interpreters informiert zu werden, was sogar noch funktioniert, falls ein fataler Fehler aufgetreten ist. Dies wird von der Execution-Komponente ausnutzt: Wird der Interpreter beendet, bevor die Methode cleanExit() aufgerufen wurde, so muss es sich um einen Fehler handeln, was impliziert, dass cleanExit() bei einem normalen Programmlauf am Ende des Programms ausgeführt wird. Denn hierdurch weiß die Execution-Komponente, dass es sich beim aktuellen Shutdown der PHP-Engine um die reguläre Beendigung des Programms handelt. Wird dieser Punkt in der Datei index.php erreicht, wird auch der Ausgabepuffer geleert. Die gesammelten Ausgabedaten werden in einem Rutsch an den Browser gesendet.
Würde also ein fataler Fehler an der durch den Kommentar gekennzeichneten Stelle auftreten, so würde er von der Execution-Komponente entsprechend behandelt. Tritt ein solcher Fehler nach dem Aufruf von cleanExit() auf, kümmert sich die Komponente allerdings nicht mehr darum.
Die Klasse gpBlogExecutionHandler ist recht unspektakulär implementiert:
class gpBlogExecutionHandler implements ezcExecutionErrorHandler
{
public static function onError( Exception $e = null )
{
$signals = gpBlogController::getInstance()->mainSignals;
$message = $e !== null ? $e->getMessage() : "Unknown";
$signals->emit( "fatal", $message );
}
}Listing 4.6 Eigene Execution-Handler
Klassen, die sich um die Behandlung fataler Fehler kümmern, müssen das Interface ezcExecutionErrorHandler implementieren, welches lediglich die onError()-Methode definiert. Diese erhält als Parameter die aufgetretene Exception, falls der fatale Fehler von einer solchen herrührt. Sie sendet das Signal fatal entweder mit der Exception-Nachricht als Fehlermeldung oder dem String Unknown. Voraussetzung dafür, dass dies funktioniert, ist natürlich, dass sowohl der Haupt-Controller als auch die Error-Aktion noch funktionstüchtig und nicht selbst von dem fatalen Fehler betroffen sind.
Tritt im GP-Blog also an beliebiger Stelle ein fataler Fehler auf, zeigt der PHP-Interpreter nun nicht mehr eine hässliche Fehlermeldung an, sondern eine entsprechende Fehlerseite. Wir beschränken uns aber hier auf das einfache Ausgeben der Fehlerinformation. In einer realen Anwendung präsentieren Sie am besten eine statische Fehlerseite und verwenden das fatal-Signal zur Benachrichtigung des Administrators.




Ihre Meinung






