Ausnahmebehandlung mit PHP

Werfen und fangen

PHP bietet seit Version 5 eine Ausnahmebehandlung, wie Sie diese aus anderen Programmiersprachen kennen. Wir zeigen Ihnen, wie Sie am besten werfen und fangen. Von Andreas Hitzig



AUF EINEN BLICK
» Die Version PHP 5 bietet dem Entwickler die Möglichkeit, eigene Ausnahmeregeln zu definieren, was eine komfortablere Verwaltung von Programmblöcken erlaubt.
» Im Workshop zeigen wir Ihnen, wie Sie am besten mit dem neuen Modell arbeiten und worauf Sie bei der Implementierung achten sollten.



In der aktuellen Version PHP 5 ist es endlich möglich, eigene Ausnahmeregeln zu definieren, Ausnahmen mit throw (Werfen) auszulösen und mit catch (Fangen) wieder einzufangen. Das ermöglicht eine deutlich komfortablere Verwaltung von Programmblöcken und bietet eine individuelle Abarbeitung von definierten Fehlerzuständen. Bisher mussten Sie Fehler mit Hilfe von Error-Flags und der trigger_error-Funktion generieren und verwalten. Diese Vorgehensweise ist in der objektorientierten Umgebung von PHP 5 nicht mehr zeitgemäß. Der neue Excep-tion-Ansatz bietet Ihnen eine höhere Flexibilität und bessere Kontrolle über die Fehlerzustände.

Die Grundarchitektur

Die Ausnahme in PHP besteht immer aus mehreren Elementen. Die Klammer um die komplette Fehlerbehandlung bildet ein try-Block. Dieser kennzeichnet den Beginn der Ausnahmebehandlung und stellt die verschiedenen Optionen dar. Innerhalb dieses Blocks muss es mindestens einen catch-Block geben, welcher für die Abwicklung von Fehlerzuständen zuständig ist. Innerhalb eines try-Blocks kann es beliebig viele catch-Blöcke geben, um verschiedene Klassen von Ausnahmen abzufangen und zu behandeln. Ist der Programmcode ohne Ausnahmen ausgeführt worden, wird das Programm nach dem try-Block normal fortgesetzt.

Das Auslösen der Ausnahmen erfolgt über die Funktion throw(). In einem solchen Fall geben Sie noch die Klasse der Ausnahme an, und PHP begibt sich mit diesem Daten auf die Suche nach der zuständigen Klasse und der Ausnahmebehandlung.

Die Ausführung des Programmcodes findet anschließend im zugehörigen catch-Block statt. Findet PHP keine passende Routine, so endet die Programmausführung in einem fatalen Fehler mit einer Meldung Uncaught Exception. Diese Fälle können Sie allerdings zusätzlich mit Hilfe der Funktion set_Exception_handler() abfangen, damit das Programm immer in einem definierten Zustand endet.

Im ersten Beispiel wird eine Zahl durch eine andere geteilt und das Ergebnis ausgegeben. Sollte es bei der Division zu einem Fehler kommen, falls durch null geteilt wird, kommt die Ausnahmebehandlung zum Tragen (Listing 1).

LISTING 1: DIVISION
<?php
function teilen($x, $y) {
if (!$y) {
throw new Exception('Nicht durch null teilen!!!');
}
else return $x/$y;
}

try {
echo teilen(10,2) . "<br>";
echo teilen(5,0) . "<br>";
}
catch (Exception $e) {
echo 'Fehlermeldung: ', $e->getMessage(), "<br>";
}

echo 'Hier geht es weiter';

?>

Die Funktion teilen() besitzt zwei Übergabeparameter x und y. Hat y den Wert 0, wird eine Ausnahme ausgelöst, da ansonsten eine Division durch null bevorsteht. Ansonsten wird das Ergebnis, also x geteilt durch y, zurückgegeben.

Im try-Block wird die Funktion teilen() zweimal aufgerufen, wobei der zweite Aufruf zu einem Fehlerzustand führt und damit direkt in den catch-Block verzweigt wird. An dieser Stelle wird der Fehler abgefangen und ausgegeben. Dabei wird auch der Fehlertext, also Nicht durch null teilen!!!, in die Fehlermeldung integriert. Da der Fehler in einen kontrollierten Zustand überführt werden konnte, kann die Programmausführung anschließend mit der Programmzeile echo 'Hier geht es weiter'; fortgesetzt werden. Im Beispiel wird als Erstes das Ergebnis der erfolgreichen Ausführung von teilen(10,2) ausgegeben, anschließend die Fehlermeldung und zum Abschluss noch der letzte echo-Befehl.

Eigene Klasse für Ausnahmen

Die Behandlung von Ausnahmen in PHP 5 sieht auch die Möglichkeit einer eigenen Exception-Klasse vor. Diese wird im Fehlerfall aufgerufen und die entsprechenden Routinen abgearbeitet. Ihre eigene Exception-Klasse muss allerdings eine Erweiterung der Standard-Klasse Exception sein - weitere Voraussetzungen gibt es jedoch nicht. Durch die Erweiterung erbt Ihre Klasse alle Methoden und Eigenschaften der Klasse Exception und muss nur noch um Ihre eigenen Fehlerbehandlungen erweitert werden:

class meineAusnahmen extends Exception
{
public function Fehlermeldung() {
$FehlerMldg = 'Fehler in Zeile
'.$this->getLine().' in '
.$this->getFile() .': <b>'
.$this->getMessage().'</b> ist keine
gültige E-Mail-Adresse';
return $FehlerMldg; } }

Die Klasse wird beim Auslösen einer Fehlermeldung entsprechend mitgegeben. Eine mögliche Fehlerbehandlungsroutine sieht dann so aus:

try {
if(filter_var($email,
FILTER_VALIDATE_email) === false) {
throw new meineAusnahmen($email);
}
}
catch (meineAusnahmen $e) {
echo $e->Fehlermeldung();
}

Innerhalb des try-Blocks wird untersucht, ob es sich beim Inhalt der Variable email um eine gültige E-Mail-Adresse handelt. Im positiven Fall wird die Ausführung direkt nach dem try-Block fortgesetzt, ansonsten wird eine Ausnahme ausgelöst und dieser als Parameter der Inhalt der Variablen email mitgegeben.

Im Fall eines Fehlers wird die Funktion Fehlermeldung() innerhalb der Klasse meineAusnahmen aufgerufen. In dieser Funktion wird, wie bereits gesehen, die Fehlermeldung zusammengesetzt und anschließend zurückgegeben. Das Ergebnis der Rückgabe wird über einen echo-Befehl im catch-Block ausgegeben (Bild 1).

In der PHP-5-Dokumentation finden Sie einige Seiten, welche die Klasse Exception genauer beschreiben (Bild 1)

Neben den Methoden der Standard-Klasse, welche im Kasten »Die Klasse Exception« auf Seite 36 näher beschrieben sind, besitzt die Klasse im Standard auch vier Eigenschaften:

Die einzelnen Werte der Eigenschaften lesen Sie am einfachsten über die korrespondierenden Methoden aus. In den beiden ersten Beispielen gab es jeweils nur eine Fehlerbehandlungsroutine. Dies ist jedoch bei der Vielzahl möglicher Fehlerursachen in der Regel nicht ausreichend. Aus diesem Grund bietet Ihnen das Exception-Handling von PHP 5 die Möglichkeit, für unterschiedliche auftretende Fehler auch unterschiedliche Optionen der Abarbeitung zu etablieren.

Im folgenden Beispiel wird zusätzlich die Verarbeitung über Ihre eigene FehlerbehandlungsKlasse und die Standard-Klasse gemischt. Es wird wieder die E-Mail-Adresse herangezogen und auf zwei unterschiedliche Fälle untersucht: Handelt es sich um eine ungültige E-Mail-Adresse, kommt die zuvor definierte Klasse zum Einsatz. Bei einer E-Mail-Adresse, welche die Domain test.com nutzt, soll die Meldung ausgegeben werden, dass es sich um keine zulässige Domain handelt (Listing 2).

LISTING 2: CHECK EINER E-MAIL-ADRESSE
try {
if(filter_var($email, FILTER_VALIDATE_email) === false) {
throw new meineAusnahmen($email);
}
if(strpos($email, "test.com") !== false) {
throw new Exception("$email stammt von einer unzulässigen
Domain");
}
}

catch (meineAusnahmen $e) {
echo $e->Fehlermeldung();
}

catch(Exception $e) {
echo $e->getMessage();
}

Die Behandlung des ersten Fehlerfalls, also die Verwendung einer ungültigen E-Mail-Ad-resse, kennen Sie ja bereits vom Beispiel zuvor. Interessant wird es erst beim zweiten Fall, wenn die Domain test.com mit ins Spiel kommt.

Diesen Umstand überprüfen Sie im zweiten if-Befehl, indem Sie die Position von test.com innerhalb der E-Mail-Adresse prüfen. Sollte die Zeichenkette bei der Suche gefunden werden, wird eine entsprechende Ausnahme ausgelöst.

In diesem Fall kommt allerdings nicht die kundeneigene Fehlerklasse zum Einsatz, sondern die Standard-Klasse. Diese können Sie, wie im Beispiel gezeigt, parallel zu Ihrer kundeneigenen Fehlerbehandlungsklasse nutzen. Wird somit test.com als Domain verwendet, erhalten Sie über die Standardverarbeitung die Fehlermeldung <E-Mail-Adresse> stammt von einer unzulässigen Domain (Bild 2).

Sie können auch mehrere catch-Blöcke in Ihre Ausnahmebehandlung integrieren (Bild 2)

Zweifacher Wurf

Die neue Ausnahmebehandlung bietet Ihnen auch die Möglichkeit, eine verschachtelte try...catch-Struktur aufzubauen. Ein solches Konstrukt bietet Ihnen die Flexibilität, wenn innerhalb des try-Blocks noch ein Fehler auftaucht, diesen für den Benutzer in einen sauberen Zustand überzuleiten, bei dem er keine kryptische Fehlermeldung erhält.

Dies wird erreicht, indem Sie innerhalb des inneren try-Blocks eine Ausnahme auffangen und anschließend eine neue Ausnahme generieren, allerdings von der Klasse meineAusnahmen().

Der innere try-Block sichert die if-Schleife ab - enthält die E-Mail-Adresse den String test.com, wird eine Ausnahme ausgelöst. Diese fangen Sie direkt über den inneren catch-Block ab. Dieser enthält allerdings keine Fehlerausgabe, sondern löst seinerseits eine neue Ausnahme der Klasse meineAusnahmen() aus. Diese wird anschließend durch den äußeren catch-Block abgefangen und an die eigene Klasse zur Ausnahmebehandlung weitergeleitet (Listing 3).

LISTING 3: KLASSE MEINEAUSNAHME()
try {
try {
if(strpos($email, "test.com") !== false) {
throw new Exception($email);
}
}
catch(Exception $e) {
throw new meineAusnahmen($email);
}
}

catch (meineAusnahmen $e) {
echo $e->Fehlermeldung();
}

In dieser Konstruktion sehen Sie auch direkt die Logik, mit welcher die Ausnahmebehandlung von PHP ans Werk geht. Sie sucht, nachdem eine Ausnahme ausgelöst wurde, immer zuerst im eigenen try-Block nach einem passenden catch-Block. Sollte dort keiner zu finden sein, wird anschließend eine Ebene höher nach dem passenden Gegenstück gesucht (Bild 3).

Sie können innerhalb eines catch-Blocks auch eine neue Exception auslösen (Bild 3)

Falsche Verwendung

Achten Sie bei der Definition von Ausnahmen immer auf deren korrekten Einsatz. Eine try...catch-Konstruktion ist dafür gedacht, mögliche Fehlerfälle zu definieren und diese in einen konsistenten Zielzustand zu überführen. Das folgende Beispiel zeigt, wie die Funktion des Exception-Handlings missbräuchlich eingesetzt wird. Prinzipiell funktioniert auch ein solches Vorgehen, allerdings ist die Zweckentfremdung nicht zielführend und sorgt beim Lesen des Programmcodes für Verwirrung (Listing 4).

LISTING 4: ZWECKENTFREMDUNG
<?php

$f1fahrer = array('Massa', 'Hamilton', 'Button', 'Vettel',
'Webber');
try {
foreach($f1fahrer as $value) {
if($value = 'Vettel') {
throw new Exception("Dies ist ein deutscher Formel-1-
Weltmeister");
}
}
}
catch(Exception $e) {
echo $e->getMessage();
}
?>

Das Beispiel beinhaltet ein Array mit einer Sammlung aktueller Formel-1-Fahrer. Wird der Name Vettel in der Liste erkannt, wird eine Ausnahme ausgelöst, die foreach-Schleife abgebrochen und die Fehlermeldung über die Exception-Klasse und die Methode getMessage() ausgegeben. Eine korrekte Umsetzung des Beispiels samt Abbruch der foreach-Schleife hätten Sie in diesem Fall auch problemlos durch eine break-Anweisung erreicht. Eine Ausnahme sollte immer nur in einem Fehlerfall ausgelöst werden und nicht zum Abbruch von Schleifen (Bild 4).

try... catch-Blöcke sind zum Abfangen von Fehlern vorgesehen und sollten nicht zweckentfremdet werden (Bild 4)

Damit Ihnen auch keine Ausnahme entwischt, bietet PHP 5 die Option, eine Fehlerbehandlung auf der obersten Ebene zu setzen, ohne eine umgebende try...catch Struktur. Mit der Funktion set_Exception_handler() setzen Sie eine beliebige benutzerdefinierte Funktion zur Verarbeitung von Ausnahmen, die an keiner anderen Stelle abgefangen werden. Dies ist somit ein ergänzender Weg zu try...catch, da bei dieser Vorgehensweise die Fehlerbehandlungsroutine direkt aufgerufen und der Fehler verarbeitet wird.

Diese Funktion kommt in der Regel zum Einsatz, wenn ein Fehler auftritt, der bisher nicht abgefangen wird. Die Funktion besitzt einen Übergabeparameter - den Namen der Funktion, welche die Fehlerbehandlung übernimmt:

<?php
function meineAusnahme($Exception) {
echo "<b>Ausnahme:</b> " ,
$Exception->getMessage(); }
set_Exception_handler
('meineAusnahme');
throw new Exception('Eine unerwartete
Ausnahme ist eingetreten');
?>

Im Beispiel wird als Erstes für die Ausnahmebehandlung die Funktion meineAusnahme() definiert. Wird eine neue Ausnahme mit throw ausgelöst, kommt automatisch die zuvor definierte Funktion zum Einsatz.

Sie können die Funktion set_Exception_handler() auch dazu nutzen, verschiedene Funktionen für die Fehlerbehandlung zu setzen. In manchen Fällen wird es sinnvoll sein, die Ausnahmebehandlungsroutinen kontextbezogen zu setzen.

Im folgenden Beispiel wird die Funktion für die Ausnahmebehandlung verwendet und führt Fälle, die nicht über einen try-Block abgefangen werden, kontrolliert zu einem Ende.

Damit die unterschiedlichen Fehlerzustände im Protokoll besser sichtbar sind, wird die Ausnahmebehandlung dazu verwendet, den Texten verschiedene CSS-Formatierungen zuzuweisen. Bei der ersten Validierung auf die Domain test.com wird die Funktion mittelschwerer_Fehler() verwendet und der Fehlertext in orangefarbiger Schrift ausgegeben. Fehlt die Variable email, handelt es sich um einen schwerwiegenden Fehler.

Anschließend soll die Standard-Ausnahmebehandlung zum Einsatz kommen. Dazu setzen Sie die individuelle Fehlerbehandlung mit der Funktion restore_Exception_handler(); zurück. Wird bei der Überprüfung der E-Mail-Adresse ein Problem festgestellt, erhalten Sie die Meldung Ungültige E-Mail Adresse (Listing 5).

LISTING 5: STANDARDKLASSE
<?php

$email = 'mail@test';

function mittelschwerer_Fehler($Exception){
echo '<p style="color: orange;">'.$Exception->getMessage().'</p>';
}

function schwerer_Fehler($Exception){
echo '<p style="color: red;">'.$Exception->getMessage().'</p>';
}

set_Exception_handler('schwerer_Fehler');

if(!isset($email)){
throw new Exception('Keine E-Mail-Adresse vorhanden');
}

set_Exception_handler('mittelschwerer_Fehler');

if(strpos($email, "test.com") !== false) {
throw new Exception("$email von unzulässiger Domain");
}

restore_Exception_handler();

if(filter_var($email, FILTER_VALIDATE_email) === false) {
throw new Exception('Ungültige E-Mail-Adresse');
}

echo 'Dies ist die letzte Meldung - das Programm ist zu Ende';

?>

Beim Testen des Skripts können Sie abhängig vom Wert der Variable email schnell die einzelnen Ausnahmezustände simulieren. Sie werden jedoch im Fall einer Ausnahme unter keinen Umständen zum letzten echo-Befehl gelangen. Beim kontrollierten Abfangen der Ausnahmen endet die Verarbeitung jeweils innerhalb der selbst definierten Funktionen oder mit der Ausgabe der Beschreibung der Ausnahme über die Standard-Klasse Exception.

DIE KLASSE Exception
Innerhalb der Klasse Exception gibt es eine Reihe von Methoden, die Sie für die Analyse und Verarbeitung der Fehlerzustände nutzen können. In der folgenden Übersicht haben wir die zur Verfügung stehenden Methoden und deren Funktionen zusammengefasst.

Methode und Beschreibung
getMessage: Beschreibung der Ausnahme als Zeichenkette.
getCode: Ermittlung des Fehlercodes.
getPrevious: Gibt die vorhergehende Ausnahme zurück. Falls es die erste Exception ist, bekommen Sie den Wert NULL als Antwort.
getFile: Liefert den Namen der Datei, welche den Fehler generiert hat.
getLine: Rückgabewert ist die Zeilennummer innerhalb der Datei, die den Fehler generiert hat.
getTrace: Gibt Ihnen den Stack Trace der Ausnahme als Array zurück.
getTraceAsString: Gibt Ihnen den Stack Trace der Ausnahme als String zurück.

Zusätzlich zu diesen Methoden gibt es innerhalb der Klasse noch die Methoden _construct, _toString und _clone. Bei der ersten handelt es sich, wie der Name bereits vermuten lässt, um die Methode, welche die Ausnahme erzeugt. _toString gibt die textuelle Beschreibung der Fehlermeldung zurück. Diese Methode kommt zum Einsatz, wenn Sie die Fehlermeldung ausgeben.
Die Methode _clone() versucht, die Exception zu replizieren. Dies führt jedoch zu einem Fatal Error, die Anwendung der Methode ist deswegen im Umfeld der Exceptions nicht sinnvoll.
Eine komplette Übersicht der Klasse samt Methoden und Eigenschaften finden Sie in der Online-Dokumentation von PHP (www.php.net/manual/en/class.exception.php).

Steuerung über Fehlercodes

Innerhalb der Klasse Exception gibt es neben get-Message()getCode()