Exceptions (Ausnahmen)
Inhaltsverzeichnis
 
  PHP hat ein Exceptionmodell ähnlich dem anderer Programmiersprachen. Eine
  Exception kann in PHP geworfen (throw) und abgefangen (catch) werden.
  Um das Abfangen potentieller Exceptions zu ermöglichen, sollte der jeweilige
  Code von einem try-Block umschlossen werden. Jeder try-Block muss
  mindestens einen zugehörigen catch- oder finally-Block besitzen.
 
 
  Wenn eine Exception geworfen wird und der aktuelle Funktionsbereich keinen
  catch-Block hat, steigt die Exception im Aufrufstapel bis zur aufrufenden
  Funktion auf, bis sie einen passenden catch-Block findet. Alle
  finally-Blöcke, auf die sie unterwegs trifft, werden ausgeführt. Wenn der
  Aufrufstapel bis in den globalen Bereich abgewickelt ist, ohne auf einen
  passenden catch-Block zu stoßen, bricht das Programm mit einem fatalen
  Fehler ab, es sei denn, es wurde ein globaler Exception-Handler gesetzt.
 
 
  Das geworfene Objekt muss eine Instanz von (instanceof)
  Throwable sein. Der Versuch ein Objekt
  werfen, das das nicht ist, wird einen fatalen PHP-Fehler zur Folge
  haben.
 
 
  Seit PHP 8.0.0 ist das Schlüsselwort throw ein Ausdruck und kann in jedem
  Ausdruckskontext verwendet werden. In früheren Versionen war es eine
  Anweisung und musste in einer eigenen Zeile stehen.
 
 
  catch
  
   Ein catch-Block definiert, wie auf eine geworfene Exception reagiert
   werden soll. Ein catch-Block definiert eine oder mehrere Arten von
   Exceptions oder Fehlern, die er behandeln kann, und optional eine Variable,
   der die Exception zugewiesen werden soll (vor PHP 8.0.0 war die Variable
   erforderlich). Der erste catch-Block, auf den eine geworfene Exception
   oder ein Fehler trifft, der mit dem Typ des geworfenen Objekts
   übereinstimmt, behandelt das Objekt.
  
  
   Mehrere catch-Blöcke können verwendet werden, um verschiedene Klassen von
   Exceptions abzufangen. Wenn innerhalb des try-Blocks keine Exception
   geworfen wird, wird die normale Programmausführung nach dem letzten in
   Folge definierten catch-Block fortgesetzt. Exceptions können innerhalb
   eines catch-Blocks geworfen (oder erneut geworfen) werden. Falls nicht,
   wird die Ausführung nach dem catch-Block, der geworfen wurde,
   fortgesetzt.
  
  
   Wenn eine Exception geworfen wird, führt PHP den Programmcode hinter der
   werfenden Anweisung nicht aus, sondern versucht, den ersten passenden
   catch-Block zu finden. Falls eine Exception nicht abgefangen wird, wird
   ein fataler Fehler mit einer
   "Uncaught Exception ..."-Nachricht ausgegeben, sofern
   keine Behandlung mittels set_exception_handler()
   definiert wurde.
  
  
   Seit PHP 7.1.0 kann ein catch-Block mehrere Exceptions getrennt durch
   Pipe-Zeichen (|) angeben. Dies ist nützlich, wenn
   unterschiedliche Exceptions von unterschiedlichen Klassenhierarchien gleich
   behandelt werden sollen.
  
  
   Seit PHP 8.0.0 ist der Variablenname für eine abgefangene Exception
   optional. Wird er nicht angegeben, wird der catch-Block trotzdem
   ausgeführt, hat aber keinen Zugriff auf das geworfene Objekt.
  
  
 
  finally
  
   Ein finally-Block kann auch nach den catch-Blöcken oder stattdessen
   definiert werden. Egal, ob eine Exception geworfen wurde, wird der Code
   innerhalb des finally-Blocks immer nach den try- und catch-Blöcken
   ausgeführt, bevor die normale Ausführung fortgesetzt wird.
  
  
   Eine bemerkenswerte Wechselwirkung besteht zwischen dem finally-Block
   und einer return-Anweisung. Wird eine return-Anweisung innerhalb der
   try- oder catch-Blöcke angetroffen, wird der finally-Block dennoch
   ausgeführt. Außerdem wird die return-Anweisung ausgewertet, wenn sie
   angetroffen wird, aber das Ergebnis wird erst nach dem finally-Block
   zurückgegeben. Des Weiteren wird, wenn der finally-Block ebenfalls eine
   return-Anweisung enthält, der Wert aus dem finally-Block zurückgegeben.
  
  
   Eine weitere bemerkenswerte Wechselwirkung tritt auf, wenn sowohl im try-
   als auch im finally-Block eine Exception geworfen wird. In diesem Fall
   wird die vom finally-Block geworfene Exception weitergegeben, während die
   vom try-Block geworfene Exception als deren vorherige Exception verwendet
   wird.
  
  
  Der globale Exception-Handler
  
   Wenn eine Exception in den globalen Bereich aufsteigen darf, kann sie durch
   einen globalen Exception-Handler abgefangen werden, falls gesetzt. Die
   Funktion set_exception_handler() kann eine Funktion
   festlegen, die anstelle eines catch-Blocks aufgerufen wird, wenn kein
   anderer Block aufgerufen wird. Der Effekt ist im Wesentlichen derselbe, als
   ob das gesamte Programm in einen try-catch-Block mit dieser Funktion
   als catch verpackt wäre.
  
  
 
  Anmerkungen
  Hinweis: 
   
    Interne PHP-Funktionen verwenden in den meisten Fällen
    Error-Reporting, nur moderne
    objektorientierte Erweiterungen
    nutzen Exceptions. Fehler können allerdings einfach mittels
    ErrorException in eine
    Exception umgewandelt werden. Diese Technik funktioniert jedoch nur bei
    nicht-fatalen Fehlern.
   
   
    Beispiel #1 Fehlermeldungen in Exceptions umwandeln
    
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
    throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
     
    
  
  
  
 
  Beispiele
  
   Beispiel #2 Eine Exception werfen
   
<?php
function inverse($x) {
    if (!$x) {
       throw new Exception('Division durch Null.');
    }
    return 1/$x;
}
try {
    echo inverse(5) . "\n";
    echo inverse(0) . "\n";
} catch (Exception $e) {
    echo 'Exception abgefangen: ',  $e->getMessage(), "\n";
}
// Ausführung fortsetzen
echo "Hallo Welt\n";
?>
    
   Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
0.2
Exception abgefangen: Division durch Null
Hallo Welt
 
   
  
   Beispiel #3 Exceptionbehandlung mit einem finally-Block
   
<?php
function inverse($x) {
    if (!$x) {
        throw new Exception('Division durch Null.');
    }
    return 1/$x;
}
try {
    echo inverse(5) . "\n";
} catch (Exception $e) {
    echo 'Exception abgefangen: ',  $e->getMessage(), "\n";
} finally {
    echo "Erstes finally.\n";
}
try {
    echo inverse(0) . "\n";
} catch (Exception $e) {
    echo 'Exception abgefangen: ',  $e->getMessage(), "\n";
} finally {
    echo "Zweites finally.\n";
}
// Ausführung fortsetzen
echo "Hallo Welt\n";
?>
    
   Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
0.2
Erstes finally.
Exception abgefangen: Division durch Null.
Zweites finally.
Hallo Welt
 
   
  
   Beispiel #4 Wechselwirkung zwischen dem finally-Block und return
   
<?php
function test() {
    try {
        throw new Exception('foo');
    } catch (Exception $e) {
        return 'catch';
    } finally {
        return 'finally';
    }
}
echo test();
?>
    
   Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
 
  
   Beispiel #5 Verschachtelte Exceptions
   
<?php
class MyException extends Exception { }
class Test {
    public function testing() {
        try {
            try {
                throw new MyException('foo!');
            } catch (MyException $e) {
                // Exception erneut werfen
                throw $e;
            }
        } catch (Exception $e) {
            var_dump($e->getMessage());
        }
    }
}
$foo = new Test;
$foo->testing();
?>
    
   Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
 
  
   Beispiel #6 Behandlung mehrerer Exceptions in einem Catch-Block
   
<?php
class MyException extends Exception { }
class MyOtherException extends Exception { }
class Test {
    public function testing() {
        try {
            throw new MyException();
        } catch (MyException | MyOtherException $e) {
            var_dump(get_class($e));
        }
    }
}
$foo = new Test;
$foo->testing();
?>
    
   Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
 
  
   Beispiel #7 Catch-Block ohne Angabe einer Variablen
   Erst ab PHP 8.0.0 erlaubt.
<?php
class SpecificException extends Exception {}
function test() {
    throw new SpecificException('Oopsie');
}
try {
    test();
} catch (SpecificException) {
    print "Eine SpecificException wurde geworfen, aber die Details interessieren uns nicht.";
}
?>
    
   Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
Eine SpecificException wurde geworfen, aber die Details interessieren uns nicht.
 
   
  
   Beispiel #8 Als Ausdruck werfen
   Erst ab PHP 8.0.0 erlaubt.
<?php
function test() {
    do_something_risky() or throw new Exception('Es hat nicht funktioniert');
}
function do_something_risky() {
    return false; // Fehler simulieren
}
try {
    test();
} catch (Exception $e) {
    print $e->getMessage();
}
?>
    
   Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
Es hat nicht funktioniert
 
    
   
    Beispiel #9 Exception in try und in finally
    
<?php
try {
    try {
        throw new Exception(message: 'Dritte', previous: new Exception('Vierte'));
    } finally {
        throw new Exception(message: 'Erste', previous: new Exception('Zweite'));
    }
} catch (Exception $e) {
    var_dump(
        $e->getMessage(),
        $e->getPrevious()->getMessage(),
        $e->getPrevious()->getPrevious()->getMessage(),
        $e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
    );
}
     
    Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
string(5) "Erste"
string(6) "Zweite"
string(5) "Dritte"
string(6) "Vierte"