Menu

PHP Exceptions: throw und eigene Exception-Klassen

Eine Exception wirfst du in PHP mit throw new Exception('message');, und der Aufrufer liest sie in einem catch-Block mit $e->getMessage(). Hier: die eingebauten Exception-Klassen, eigene Exceptions schreiben, getCode(), getLine() und getPrevious(), throw als Ausdruck und die Fehler, die PHP selbst wirft, wie ValueError und TypeError.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Eine Exception wirfst du in PHP mit throw new Exception('message');. Die Ausführung stoppt in dieser Zeile und springt zum nächsten catch-Block, der den Typ der Exception akzeptiert, wo $e->getMessage() die Meldung liefert. Verwende eine bestimmte Klasse wie InvalidArgumentException, damit Aufrufer Probleme unterscheiden können.

Eine Exception ist ein Objekt. throw reicht sie den Aufrufstapel hinauf zum ersten Aufrufer mit passendem catch, und dieser Aufrufer entscheidet, was zu tun ist; die Funktion, die das Problem gefunden hat, muss das nicht wissen. Die Mechanik des Abfangens, finally und das Abfangen mehrerer Typen stehen auf der Seite zu try catch.

Was ein Exception-Objekt enthält

Jede Exception trägt eine Meldung, einen Ganzzahl-Code, die Datei und Zeile, in der sie erzeugt wurde, und einen Stack Trace. Der Konstruktor nimmt ($message, $code, $previous), alles optional:

getLine() ist die Zeile von throw new, nicht die Zeile des catch. Der Trace listet die Aufrufe auf, die dorthin geführt haben (#0 /home/index.php(8): findOrder()), und das liest man meist zuerst, wenn ein Fehler in einem Log auftaucht. Die Exception in einen String umzuwandeln (echo $e; oder (string) $e) gibt alles im Standardformat von PHP aus.

Eingebaute Exception-Klassen

PHP bringt in seiner Standard PHP Library (SPL) eine Familie von Exception-Klassen mit. Diejenige zu werfen, die das Problem benennt, macht deinen Code lesbarer und lässt Aufrufer gezielt abfangen. Alle diese Klassen erweitern Exception:

KlasseWirf sie, wenn
InvalidArgumentExceptionein Argument die falsche Form hat: ein leerer Name, eine unbekannte Option
DomainExceptionein Wert außerhalb der sinnvollen Menge liegt: ein negativer Preis
OutOfRangeExceptionder Code nach einem Index gefragt hat, der nie existieren kann (ein Bug beim Aufrufer)
OutOfBoundsExceptionein Schlüssel oder Index in Daten fehlt, die erst zur Laufzeit bekannt sind
RangeExceptionein berechnetes Ergebnis während der Laufzeit außerhalb des gültigen Bereichs liegt
LengthExceptionetwas zu lang oder zu kurz ist
RuntimeExceptionein Problem erst zur Laufzeit erkennbar ist: eine volle Festplatte, ein Timeout
UnexpectedValueExceptioneine Funktion einen Wert eines unerwarteten Typs geliefert oder bekommen hat
LogicExceptionder Code selbst falsch ist, ein Bug statt einer schlechten Eingabe
JsonExceptiongeworfen von json_encode()/json_decode() mit JSON_THROW_ON_ERROR

InvalidArgumentException, DomainException, LengthException und OutOfRangeException erweitern LogicException; OutOfBoundsException, RangeException und UnexpectedValueException erweitern RuntimeException. Die Elternklassen jeder Klasse kannst du selbst prüfen:

TypeError hat Error als Elternklasse, nicht Exception: Es gehört zur zweiten Familie, unten.

Fehler, die PHP selbst wirft

Seit PHP 7, und viel stärker seit PHP 8, werfen PHPs eigene Funktionen und Operatoren Unterklassen von Error für Probleme, die früher Warnungen waren. Du fängst sie genauso ab, aber catch (Exception $e) passt nicht auf sie:

DivisionByZeroError, TypeError, ValueError, ArgumentCountError, UnhandledMatchError und das einfache Error erweitern alle Error. Jede davon markiert öfter einen Bug im aufrufenden Code als schlechte Daten, und deshalb leben sie außerhalb des Exception-Baums. declare(strict_types=1) ist das, was str_repeat(5, 2) zu einem TypeError macht; ohne würde PHP 5 in "5" umwandeln.

Eine eigene Exception-Klasse schreiben

Eine einzeilige Unterklasse ist oft alles, was du brauchst: Der Klassenname selbst trägt die Bedeutung, und Aufrufer können genau diesen Typ abfangen. Wenn der Abfangende Daten braucht (welche Bestellung, welcher Betrag), fügst du Eigenschaften hinzu und reichst die Meldung mit parent::__construct() weiter:

Wähl die Elternklasse mit Bedacht: RuntimeException zu erweitern (statt einfach Exception) bedeutet, dass Code, der RuntimeException abfängt, auch deine behandelt. Eine kleine Hierarchie, eine Basis-Exception pro Bibliothek oder Modul mit spezifischen Unterklassen darunter, lässt Aufrufer wählen zwischen „alles aus dem Zahlungsmodul abfangen“ und „nur genau diesen einen Fall abfangen“.

Exceptions mit previous verketten

Wenn du eine Exception einer unteren Ebene abfängst und eine einer höheren Ebene wirfst, übergib die ursprüngliche als drittes Argument. Nichts geht verloren: getPrevious() geht zurück bis zur eigentlichen Ursache.

Der Nutzer sieht „customers.csv could not be imported“; das Log, das die Kette abgeht, bekommt außerdem „row 2 has 2 columns“. Wird eine verkettete Exception nie abgefangen, gibt PHPs fataler Fehler die ganze Kette aus: zuerst die ursprüngliche Ursache, dann jede Hülle unter einer Zeile Next.

throw als Ausdruck

Seit PHP 8.0 ist throw ein Ausdruck und kann damit überall stehen, wo ein Wert erwartet wird: nach ??, in einem ternären Ausdruck, in einer Arrow Function. Das macht „hol das oder scheitere“ zu einer einzigen Zeile:

Wann man nicht wirft

Exceptions sind für Situationen, die die aktuelle Funktion nicht behandeln kann und die der normale Ablauf nicht erwartet. Eine Suche ohne Treffer, ein leeres optionales Feld oder ein Nutzer, der nicht angemeldet ist, sind gewöhnliche Ergebnisse: Liefere null, false oder ein leeres Array, lass den Aufrufer prüfen, und behalte Exceptions für die Fälle, in denen Weitermachen falsch wäre.

Zwei weitere Gewohnheiten halten Exception-Code lesbar: Fang nie eine Exception ab, nur um sie zu ignorieren (ein leeres catch (Exception $e) {} versteckt den nächsten echten Bug), und wirf bestimmte Klassen statt eines bloßen Exception, damit ein Aufrufer nie gezwungen ist, alles abzufangen, um einen Fall zu behandeln.

Häufig gestellte Fragen

Wie werfe ich in PHP eine Exception?

Erzeuge ein Exception-Objekt und wirf es: throw new InvalidArgumentException('Quantity must be positive');. Die Ausführung stoppt in dieser Zeile und springt zum nächsten passenden catch-Block; gibt es keinen, endet das Skript mit einem fatalen Fehler „Uncaught“.

Wie erstelle ich in PHP eine eigene Exception?

Erweitere Exception oder eine ihrer Unterklassen: class PaymentFailedException extends RuntimeException {}. Diese eine Zeile reicht, um sie unter ihrem eigenen Typ zu werfen und abzufangen. Füge Eigenschaften und einen Konstruktor hinzu, wenn der Abfangende zusätzliche Daten braucht, und rufe parent::__construct($message, $code, $previous) auf.

Was ist der Unterschied zwischen getMessage und getCode?

getMessage() liefert den Text, der als erstes Konstruktorargument übergeben wurde, gedacht für Menschen und Logs. getCode() liefert die als zweites Argument übergebene Ganzzahl (standardmäßig 0), nützlich für maschinelle Prüfungen, etwa um Fehler HTTP-Statuscodes zuzuordnen.

Welche Exception sollte ich in PHP werfen?

Verwende eine eingebaute SPL-Klasse, die zum Problem passt: InvalidArgumentException für ein ungültiges Argument, DomainException für einen Wert außerhalb des Erlaubten, RuntimeException für Fehler, die erst zur Laufzeit sichtbar werden (eine volle Festplatte, ein Timeout), LogicException für Programmierfehler. Für Fehler, die deine Aufrufer unterscheiden müssen, erstellst du eine eigene Unterklasse.

Was ist Exception Chaining in PHP?

Die ursprüngliche Exception als drittes Konstruktorargument zu übergeben, wenn du eine neue wirfst: throw new ImportException('Import failed', 0, $e);. Die neue Exception liefert Kontext, und $e->getPrevious() darauf liefert die ursprüngliche Ursache, also geht kein Detail verloren.

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

LOS GEHT'S