• Wann verwende ich Exceptions?

    von Nils Langner am 7. Mai 2009
    Dieser Artikel wurde auf Wunsch der phphatesme Leser verfasst und wurde über die Ideenschmiede eingereicht. Falls du auch eine Idee für einen Artikel hast, dann füge sie doch einfach hinzu.

    Diesen Artikel schiebe ich schon eine ganze Weile vor mir her. Obwohl es für mich intuitiv klar ist, wann ich Exceptions zu nutzen habe, finde ich es auf keinen Fall einfach dies auch in Worte zu fassen. Heute will ich es einfach mal versuchen, wenn es nicht klappt, dann hab ich ja nichts verloren. 

    Ich versuche mich mal mit der Standard Definition. Excpetions (Ausnahmen) werden immer dann geworfen, wenn etwas unerwartetes passiert. So das war’s, ich bin raus. Na gut, vielleicht noch mal ein paar Beispiele, was etwas unerwartetes sein könnte. Das Öffnen einer Config Datei, die ich für den Programmablauf brauche, so etwas ist unerwartet. Der Versuch des Öffnens einer Datei, deren Name mir der User als String übergeben hat, ist keine Ausnahme wert, falls diese nicht vorhanden ist. Niemals dem User vertrauen, ist eh so ein Leitspruch, der einem viel Ärger ersparen kann.

    Ein Fall, den ich vor kurzem gesehen habe (ich weiss aber leider nicht mehr wo) ist das Validieren der Userinformationen beim Anlegen eines neuen Benutzers. Hier gab es so etwas wie setUserName( $name ). Diese Methode hat eine Exception geworfen, falls der Name nicht einem bestimmten regulären Ausdruck genügt hat. Bis jetzt passt das wunderbar zu meiner Definition. Jetzt ging es aber weiter. Die Eingaben des Users wurden direkt in diese Methode gepumpt. Das darf natürlich nicht sein, denn wir wissen ja, Userinput ist böse. Wir brauchen also so etwas wie isValidUsername( $name ), was ich vor dem zuordnen aufrufen sollte. Und schwupps wird keine Ausnahme mehr verwendet, ausser der Name ändert sich irgendwie auf unerklärliche Art und Weise zwischendrin und das wäre wirklich unerwartet.

    Ich glaube jetzt sind wir mal gar nicht schlauer. Was ich eigentlich sagen will, Exception darf man gerne werfen, man muss dem Programmierer nur die Möglichkeit drum herum zu kommen. Der Fokus sollte hier auf der Validierungsfunktion liegen. Erst diese macht das ganze anwendbar, ohne in eine “falsche” Ausnahme zu laufen.

    Aber warum sollte man Execptions nicht als normale Kontrollstruktur verwenden, wie zum Beispiel IF? Von Java weiß ich, dass Exceptions sehr kostspielig sind, also kostspielig im Sinne von dauern sehr lange. Ob man dies eins zu eins auf PHP übertragen kann bin ich mir nicht sicher. Aber vielleicht liest ja ein Johannes mit und der kann seinen Senf dazu abgeben. Was ich aber weiß, ist dass Exceptions, die nicht richtig gefangen werden und irgendwo unerwartet im Source dann doch gestoppt werden die Wartbarkeit und die Fehlerfindung so extrem erschweren, dass man keinen Spaß man daran hat.

    Ich glaube aber, ihr macht es was ihr wollt und lasst euch von mir nicht reinreden. Macht also weiter so, wird schon richtig sein. Ich muss ja euren Code nicht warten.

    Nils Langner Nils Langner

    Auch wenn Ihr es mir nicht glauben werdet, aber ich habe nichts gegen PHP. Ich rege mich einfach nur gerne auf. Ok so schlimm ist es auch nicht. Eigentlich wollte ich schon immer einen Blog haben und da ...

    Zum Profil von Nils Langner

    25 Kommentare »


    • Manuel
      am 7. Mai 2009 um 08:15 Uhr

      Ich benutze Exceptions z.b. für WSDL aufrufe
      Ansonsten gehe ich recht sparsam damit um.


    • Cem
      am 7. Mai 2009 um 08:15 Uhr

      Ich persönlich logge mir immer alle geworfenen Exceptions auf Entwicklungssystemen &mdsash; Ich leite von mir geworfene allerdings auch von einer eigenen Exception-Klasse ab. Denn das Problem, dass Exceptions als Alternative zu Booleschen Rückgaben verwendet werden kann(!) in der Tat das Debugging entscheidend erschweren.


    • Michael
      am 7. Mai 2009 um 09:20 Uhr

      Ich versuche es ähnlich wie Nils und fahre mehrgleisig, schaffe es aber nicht immer. Beispiel:

      Userinput wird generell erstmal direkt nach der Annahme durch das Script geprüft. Dank des Zend Frameworks geht das wunderbar einfach mit Hilfe von Validators. Damit hat man schonmal die gröbsten Dinge abgefangen (Zahlenfelder nur Zahlen, mit Regex auf gültige Zeichen überprüfen etc).

      Zusätzlich habe ich in den eigentlichen Klassen (also beispielsweise kurz vor dem Speichern in eine Datenbank) nochmal eine Prüfung, ob ein dort übergebener Wert auch wirklich so in die Datenbank rein darf. Eigentlich sollte man meinen, dass die “erste Schicht” (Formular-Validators) ja reichen müßte, doppelt machen ist immer doof. ABER: Wir haben hier viele weitere “Eingänge” wie zB kleine Scripte, SOAP, RPC, AMF usw, die direkt mit den Klassen arbeiten, und an den Formular-Validators der Webseite vorbeiarbeiten.

      Naja, nicht ideal, aber besser einmal zuviel prüfen als einmal zu wenig. Nichts ist ärgerlicher als verlorene Daten oder Inkonsistenzen, gerade auf professioneller Ebene. Mein kleines Heimblog würde ich wahrscheinlich nicht doppelt und dreifach absichern.


    • Saphir2k
      am 7. Mai 2009 um 09:33 Uhr

      “Diesen Artikel schiebe ich schon eine ganze Weile vor mir her.”

      Verständlich! Leider trägt dieser Artikel auch nichts zum Verständnis bei. Das Aussagekräftigste steht im 2. Satz im bsatz 2, Satz 2.

      Schade, da hätte ich mir klare Worte gewünscht und ein paar gute Beispiele.

      Einige Fragezeichen tauchten bei folgenden Passagen auf:

      “Das Öffnen einer Config Datei, die ich für den Programmablauf brauche, so etwas ist unerwartet.”

      “… Macht also weiter so, wird schon richtig sein.”. Warum dann der Artikel???

      Bitte nicht in den falschen Hals bekommen, ein wenig Kritik ist, im Hinblilck auf Besserung, sicherlich erlaubt.

      Viele Grüße,
      Thorsten


    • Christoph
      am 7. Mai 2009 um 09:34 Uhr

      Ich finde Exceptions zur Validierung perfekt, natürlich nicht im DAO-Objekt sondern in einem extra Validation-Klasse für das DAO-Objekt.

      Was nützt mir ein Boolean der mir nur sagt falsche Eingabe?
      In einer Exception kann ich eine Liste anlegen in der jedes einzelne Attribut mit den einzelnen Fehlern beschrieben wird.
      Missbrauche ich die Exception?
      Ich weiß nicht ob das so 100%ig sauber ist, leider konnte ich bisher mit keinem darüber ernsthaft diskutieren.
      Was meint ihr?

      Ehrlich gesagt, ich hasse diese Boolean gewrickel.


    • Nils Langner
      am 7. Mai 2009 um 10:12 Uhr

      @Christoph: Vielleicht definierst du dir einfach ein ResultObject, dass genau wie deine Exception deine Fehler beinhaltet? Zusätzlich hat es natürlich eine isValid( ) Methode. Du würdest also immer ein Result zurückbekommen und es wäre auch konsitent vom Typ her. Denn eigentlich nutzt du ja gar nicht die Element, die eine Exception so besonders macht.

      @Saphir2k: Klar ist Krirtik erlaubt und ich war mir vor dem Schreiben schon sicher, dass es nicht so einfach wird. Und wenn es diesmal nicht ganz so geklappt hat, dann kann ich diesmal damit leben. Aber wie immer bin ich über jede Idee dankbar und wenn du Lust hast, kannst du gerne mal einen Gastbeitrag schreiben.


    • PHP-Desaster
      am 7. Mai 2009 um 10:28 Uhr

      Ich nutze Exceptions in internen Modulen. Diese implementieren zum Beispiel den Zugriff auf einen Webservice, übernehmen irgendwelche Datentransformationen, parsen/rendern Daten, etc. Diese Module werden an verschiedenen Stellen verwendet, befinden sich aber in einem Web- oder CLI-Kontext. Hier finde ich die Ausnahmen sehr angenehm, um solche Konstrukte wie $resultObject->isValid() vermeiden zu können. Außerdem kann ich damit super cool Methodchaining betreiben:

      $foo=$this->getWebservice()
      ->configure($configs)
      ->request()
      ->getResponseBody();


    • Rudwig Brown
      am 7. Mai 2009 um 12:09 Uhr

      Eine spontane Idee: (ich hab den Gedanken nicht fertig gedacht – vielleicht ist es auch Bullshit)
      Eine exceptionfreie Alternative zu boolschen Überprüfungen wäre ein State Objekt (Singleton? Vielleicht sogar im Zusammenhang mit AOP): In jeder relevanten Methode wird überprüft, ob der Status noch OK ist. Wenn nicht, wird entsprechend gehandelt. Wäre das was?


    • Nils Langner
      am 7. Mai 2009 um 12:35 Uhr

      @Rudwig: Ich find’s toll, wenn man solche Ideen hat, aber ich glaube diesmal wird sie kein Erfolg. Was würde ich denn machen, wenn der Status nicht mehr ok ist? Ich habe doch keine Ahnung, an welcher Stelle denn nun der switch zwischen Ok und Kaputt passiert ist. Vielleicht hab ich’s auch einfach nicht verstanden :)


    • Michael
      am 7. Mai 2009 um 12:52 Uhr

      Man müßte natürlich zusätzliche Informationen in diesem State Object speichern, wie Message, Code, Stack etc. Genau das, was eine Exception macht ;)
      Problem dabei wäre, dass man nach jedem Funktionsaufruf dieses State Object überprüfen müßte. Dann kann man auch gleich Exceptions nutzen…
      Wenn man sich eine gute Struktur aufbaut, spart man durch Exceptions auch viel Arbeit, je weiter “oben” man sie catched. Und wenn man will, kann man sie auch sehr feinkörnig abfangen, und quasi um jeden Funktionsaufruf einen try-catch-Block machen. Häufig ist das aber nicht gewollt.

      Ich bin ganz zufrieden mit Exceptions, und sehe keinen Grund, an dem Verfahren was zu ändern. Wenn ich da an Zeiten zurückdenke, wo es das nicht gab. Im PEAR-Framework kann man das noch gut beobachten: Nach fast jedem Funktionsaufruf muß man den Rückgabewert überprüfen:
      if (PEAR::isError($ret))…
      Ein absoluter Krampf, mir wird grad schlecht… Ohje…


    • PHP-Desaster
      am 7. Mai 2009 um 14:41 Uhr

      Ich nutze auch gerne das von Java/C# übernommene Konzept von inneren Ausnahmen, also in einem Modul gefangene Ausnahmen weiter zu werfen:

      try {
      $this->doStuff();
      }
      catch(MyException $ex) {
      throw new FooException(‘Failed to do stuff’, $ex);
      }

      Nur dran denken, auch die __toString-Methode der Ausnahme zu erweitern, um beide Stacktraces anzuzeigen.


    • Rudwig Brown
      am 7. Mai 2009 um 14:43 Uhr

      Hm. Ich glaube ich wurde wirklich nicht verstanden. Nicht mal von mir! ;)
      Ich hab schon sowas wie “PEAR::isError($ret)” gemeint, nur das es sich beim State nicht um einen Error/eine Exception handelt, sondern um eine erwartete Ausnahme :) jedoch für Fälle, die nicht mehr mit einer Validierung abgedeckt werden sollen/können.
      (@Michael: Sowas ist mit AOP übrigens nicht mehr ganz so ein großer Krampf.)
      Ich versuchs mal mit einem Beispiel:
      if ($state->isBadUser()) {
      //send him a lovely message
      }
      Falls der Ansatz immer noch nicht verstanden ist, werd ich mal damit rumexperimentieren und dann drüber bloggen.


    • JensK
      am 7. Mai 2009 um 15:31 Uhr

      Ein super Thema!

      Leider hat da jeder Entwickler seine Vorlieben und die Exceptions verkommen oft zu “GOTO”-Krücken – das Ergebnis: Spaghetti-Code.

      Ich stehe da voll hinter Nils und sage: Pflegt das selber!

      Exceptions sind NUR für Ausnahmen vorgesehen – habt ihr den Programmfluß zu steuern erinnert euch an die Coretools dafür.


    • Johannes
      am 7. Mai 2009 um 17:14 Uhr

      Ok, jetzt habe ich diesen Artikel den ganzen Tag offen und da angesprochen wollte ich auch antworten, wollte davor aber noch recherchieren, wozu ich aber heute nichtmehr komme.

      Zur Frage nach der Performance: Das habe ich nie genauer durchgemessen. Was auch daran liegt, dass ich solche Mikro-Benchmarks in 99% der Fälle blöd finde schönerer Code kostet (fast) immer nur ist das Bottleneck halt doch genau eine Datenbank Query. Sei es drum was is meine Schätzung: Von der Implementierung her, stark vereinfacht, funktionieren Exceptions so, das nach einem Statement geschaut wird ob das “Exception Flag” gesetzt ist (das is kein Flag sondern nen Pointer auf das aktive Exception-Objekt oder NULL) oder nicht. Ist es nicht gesetzt geht es weiter, falls es gesetzt ist passiert ein Sprung zum Ende des aktuellen Frames oder try{}-Blockes. Dort wird geschaut ob es ein catch gibt der passt – je mehr catch Blöcke und desto mehr inheritance desto teurer – wobei Fehlerfall Performance-Technisch egal sein sollte, sonst hat man eh ein Problem mit der Architektur – wenn kein catch Block passt aufräumen und weiter nach oben. Wie schaut das ohne Exceptions aus? Das Exception-Flag muss trotzdem geprüft werden, danach muss mit userland Code der Fehlerzustand geprüft werden und nach oben durchgereicht werden. Nun is userspace code langsamer als das direkt in der Engen zu machen weswegen ich vermuten würde (wie gesagt nicht durchgemessen …) das das oft schneller ist, zumindest so lange die Exception nicht auftritt.

      Das Problem das ich mit Exceptions sehe ist, dass schnell “wild” irgendwas passiert – ich rufe irgendeine Library-Funktion, die ruft wieder etwas auf und in irgendeinem Spezialfall wirft das ne Exception. Die Code coverage meiner Tests von meinem Code ist 100% und die Library hat auch 100% Abdeckung aber den Edge-Case finde ich erst auf Produktion wodurch ich dann nen Fatal Error habe und der User nur ne weiße Seite bekommt (display_errors -> 0, natürlich auf Produktion) Das macht Wartung recht aufwendig. Java versucht das zu lösen in dem man Exceptions die durchgereicht werden in der Signatur angeben muss (int foo() throws MyException {}) was bei meinen Java-Dingen aber dennoch noch genug “Uncaught Exceptions” geliefert hat, somit also auch nicht die perfekte Lösung ist…

      So, muss aber mal nen Release anouncement schreiben ;-)


    • Nils Langner
      am 7. Mai 2009 um 18:38 Uhr

      @Johannes: Klingt sehr interessant, vielen Dank. Ich bin mir auch gar nicht mehr so sicher, wo ich es damals gelesen habe, dass in Java Exceptions so kostspielig sind. Ich hab irgendwas in Erinnerung, dass man die VM anhalte müßte, um an den Stacktrace ranzukommen. Ich weiss jetzt auch gar nicht, ob meine Aussage Sinn macht :) Kenne die Interna von java nicht wirklich. Aber ich versuche es mal rauszufinden.


    • Ludwig
      am 7. Mai 2009 um 21:00 Uhr

      Ich weiß nicht warum stört mich auch an der php docu aber alle beispiele gehen davon aus das nur ein command im try block steht. warum ?

      Das schöne an exception ist doch das der codeablauf dort unterbrochen wird wo der fehler passierte und dann in den catch block gesprungen wird.

      Mal ein beispiel peudo code um das zu veranschauchlichen:

      try {
      $object = new FetchRowFromDatabase();
      $anderesobject = new DoSomethingWithIt($object);
      $object->WriteToDatabase($anderesobject);
      }catch(Exception $e){
      …. was auch immer zur fehlerbehandlung notwendig ist.
      }

      Jede der obigen klassen throwed eine exception, (oder auch eine eigene, um entsprechend drauf zu reagieren) ich muss mich nicht mehr drum kümmern ob es $object->WriteToDatabase wirklich gibt, wenn die initalisierung von $object eine exception wirft komm ich nicht mehr zu dem punkt.

      Ich sehe den vorteil von Exceptions einfach darin die Fehlerbehandlung zentral in einem oder mehreren catch blöcken zu haben, und dazwischen nur noch um die ablaufsteuerung selbst kümmern zu müssen ohne auch noch mit fehlerbehandlungen zu arbeiten.

      Ludwig


    • Johannes
      am 7. Mai 2009 um 22:22 Uhr

      Nils, naja, was teuer ist ist eine Exception überall durchzureichen, und ja beim Erzeugen des Exception Objekts muss ein Stacktrace erzeugt werden, Frage ist was die Alternative kostet … und ich hatte bewusst geschrieben, “dass das oft schneller ist, zumindest so lange die Exception nicht auftritt.” Man beachte den zweiten Halbsatz. Also der Vergleich zwischen “nach jedem Statement auf Fehler prüfen” und “einen längeren try-Block haben, evtl. sogar über Funktionscalls hinweg”.

      Die Performance bei “Exception tritt auf” kann stärker leiden, da gilt dann aber wieder “Es ist eine Ausnahme-Situation” nach der oft doch eh die Terminierung steht (naja, ok, das is meine Meinung – keinerlei cotrol-flow via Exception)


    • Johannes
      am 7. Mai 2009 um 22:24 Uhr

      Ludwig, grundsätzlich stimme ich dem zu, nur sollte man “catch (Exception $e)” vermeiden sondern spezialisierte catch-handler nehmen, sonst vergisst man schnell mal ne Exception die besonderes Handling erfordert und bekommt Wartungsschwierigkeiten.


    • Ludwig
      am 8. Mai 2009 um 00:14 Uhr

      @johannes. Ja hab ich eh auch geschreiben “… in einem oder mehreren catch blöcken zu haben …” wieder ein zusimples beispiel genommen :-)

      btw. danke für die erklärung zum internen ablauf von exceptions.


    • Re: Wann verwende ich Exceptions | Der PHP Hacker — Noch ein PHP Blog
      am 11. Mai 2009 um 07:36 Uhr

      [...] ist das aber anders: Vor ein paar Tagen fragte sich Nils, wie man wohl Exceptions korrekt verwendet. Jetzt finde ich endlich mal genug Zeit, meine Meinung zu dem Thema [...]


    • Aufruf an die PHP Experten: ErrorHandling HowTo | CWD - Customized Web Development
      am 26. Mai 2009 um 08:52 Uhr

      [...] Ich wollte Exceptions eigentlich nur verwenden wenn “unerwartetes” passiert. Zb. Keine Datenbankconnection, File konnte nicht ins Filesystem geschrieben werden.. solche dinge. Darüber hat auch Nils Langner in seinem Blog schon geschrieben. [...]


    • » StarLinks + Pfingstbonus: Exceptions, Startup-Ideen, Fachwörter-Lexikon, Domain-Tools - im Designpicks Blog
      am 29. Mai 2009 um 12:26 Uhr

      [...] Der Nils von phphatesme hat einmal versucht, den Nutzen von Exceptions zu erläutern. Der “PHP Hacker” hat eine gute Antwort dazu geschrieben und das Thema [...]


    • Chris
      am 11. September 2009 um 18:34 Uhr

      Bisher habe ich noch nicht so viel mit Exceptions gearbeitet. Aber warum erschweren diese das Debugging? Ich bin bisher davon ausgegangen, dass eine Exception das Debugging erleichtert, weil diese doch um einiges detailierter ist als ein false als Rückgabewert.


    • jensk
      am 14. September 2009 um 10:46 Uhr

      @Chris

      Du meinst den “Missbrauch” der Exception als Debbuger einfach temporär in den Code geschrieben – damit hast du Recht.

      “Richtige” Exceptions bleiben im ProduktivCode und steuern das Verhalten deiner Anwendung bei unerwarteten Fehlern.
      So ist die Definition.
      Wie du an dieser Diskussion siehst, werden sie aber auch zur Ablaufsteuerung benutzt, weil einige nicht ohne GoTo auskommen ;) .


    • Kim
      am 1. März 2010 um 11:38 Uhr

      Während ich viele Beiträge von phm hier schätze und gern lese, muss ich leider sagen, dass ich diesen Artikel für verwirrend, durcheinander und unterm Strich für nicht wertvoll halte.

      Exceptions sind in PHP stiefmütterlich behandelt, da sind wir uns wohl alle einig. Und wenn man kein Framework benutzt, das von Exceptions fleißig Gebrauch macht, kann man natürlich argumentieren, warum man mit Exceptions um sich schmeißen soll, wenn die Core-Funktionen von PHP das selbst nicht tun.

      Reden wir aber von größeren Projekten, in denen man eine saubere und übersichtliche Fehlerfallbehandlung einsetzen möchte und wartbaren Code erzielen möchte, dann sind Exceptions auch für PHP ein gutes Mittel.

      Das Performance-Argument ist m.E. absolut überbewertet im Artikel. Ordentlich strukturierter Code erzielt in aller Regel erheblich mehr Vorteile.

    RSS-Feed für Kommentare zu diesem Artikel. TrackBack-URL

    Einen Kommentar hinterlassen

    Werbung
    PHP Magazin
    Ausgabe 02/2010

    Dieses Mal mit Artikeln zu den Themen OpenSocial und Apache Shindig, Graphentheorie, Smarty3

    t3n
    Ausgabe 19

    Social Media (R)evolution. Weitere Themen sind noSQL, Crowdsourcing ...

    PHP Journal
    Ausgabe 2/2010

    PHP & Windows optimal nutzen, die besten PHP-CMS im Überblick, Google-API mit Zend Framework nutzen.

    Wir wurden schon öfters gefragt, ob man uns nicht irgendwie unterstützen kann. Die Antwort war immer einfach: Klar! Am einfachsten ist es eure nächsten Einkäufe bei Amazon über unsere Link abzuwickeln. Damit würdet ihr uns schon sehr helfen. Über Co-Autoren freuen wir uns aber noch mehr.