T-Rex: Wann wirft man eine Exception

Moin,

Tjo Frage steht ja schon im topic. Um möglichst vielen Missverständnissen vorzubeugen hier vielleicht noch ein Beispiel.

Hab da ein Objekt. Dieses Objekt verwaltet einen Datensatz. Es gibt bei diesem Objekt unter anderem eine Methode "delete". Diese Löscht den Datensatz in der Datenbank. Die Methode braucht zwingend den Tabellenname. Es gibt 3 "Fehlerfälle":
1. Tabellenname ist nicht bekannt -> Exception
2. Es gibt keinen Primärschlüssel -> return false (Löschen war also nicht erfolgreich)
3. Datensatz wurde bereits gelöscht -> return true (Löschen war bereits erfolgreich)

Ich streite gerade mit mir selbst wieso ich nicht bei 1,2 und 3 einfach Exceptions werfe. Da ist eine kleine Stimme und die sagt dass alles was die Methode nicht mit Erfolg beendet, sprich das die Methode ihre Arbeit erledigt, mit einer Exception quittiert werden sollte. Die etwas lautere Stimme sagt, dass nur eine Exception geworfen werden sollte, wenn es "Technisch" unmöglich ist, dass die Methode ausgeführt wird, was eben nur dann eintritt, wenn der Tabellenname nicht bekannt ist.

Gibt es vielleicht irgendeine Regel wann man Exceptions werfen sollte?

Gruß
der etwas kränkelnde und deshalb gramatikalisch schwer zu verstehende
T-Rex

  1. Tach!

    Hab da ein Objekt. Dieses Objekt verwaltet einen Datensatz. Es gibt bei diesem Objekt unter anderem eine Methode "delete". Diese Löscht den Datensatz in der Datenbank. Die Methode braucht zwingend den Tabellenname. Es gibt 3 "Fehlerfälle":

    1. Tabellenname ist nicht bekannt -> Exception

    Das sollte schon vorher auffallen, wenn eine solch wichtige Abhängigkeit nicht gegeben ist.

    1. Es gibt keinen Primärschlüssel -> return false (Löschen war also nicht erfolgreich)

    Braucht es im Prinzip auch nicht, man kann auch mit anderen Bedingungen Datensätze löschen. (Ich kenne aber deinen Anwendungsfall nicht. Gegebenenfalls ist auch der Primärschlüssel eine grundlegende Abhängigkeit.)

    1. Datensatz wurde bereits gelöscht -> return true (Löschen war bereits erfolgreich)

    Hier kann man argumentieren: Der Datensatz ist weg, Ziel erfüllt, keine Ausnahmebedingung (oder: Aktion erfolgreich, betroffene Datensätze: 0). Aber auch hier muss man wieder fragen, was genau der Anwendungsfall ist und warum es wichtig wäre, auf bereits gelöschte oder nie vorhandene Daten zu reagieren (und ob das eine Ausnahmesituation ist und nicht anhand der Anzahl betroffener Datensätze erkannt werden kann).

    Ich streite gerade mit mir selbst wieso ich nicht bei 1,2 und 3 einfach Exceptions werfe. Da ist eine kleine Stimme und die sagt dass alles was die Methode nicht mit Erfolg beendet, sprich das die Methode ihre Arbeit erledigt, mit einer Exception quittiert werden sollte. Die etwas lautere Stimme sagt, dass nur eine Exception geworfen werden sollte, wenn es "Technisch" unmöglich ist, dass die Methode ausgeführt wird, was eben nur dann eintritt, wenn der Tabellenname nicht bekannt ist.

    Gibt es vielleicht irgendeine Regel wann man Exceptions werfen sollte?

    In Ausnahmesituationen.

    "Nicht mit Erfolg beendet" sich zum Beispiel auch eine Funktion, die eine Teilstring in einem String nicht findet. Ist das eine Ausnahme oder eine normale "is nich"-Situation. Oft wird es keine Ausnahme sein, manchmal schon.

    dedlfix.

    1. Noch so spät Wach? Dann erstmal Danke, dass du nochmehr schlaf für mich opferst :).

      Leider bist du etwas zu sehr auf mein Beispiel eingegangen. Natürlich ist der Primärschlüssel nicht immer wichtig, aber darum geht es mir nicht :).

      In Ausnahmesituationen.

      Wann erkenne ich so eine Ausnahme?
      Wieder zu meinem Beispiel. Das der Tabellenname fehlt wäre so eine Ausnahme? Den zum löschen des Datensatzes wäre der Tabellenname elementar. ABER ich könnte hier auch einfach ein (return) false zurück geben, denn gelöscht werden kann ja nichts.

      Der Aufrufer weiß dann aber das "warum" nicht, also wieso nichts gelöscht werden konnte. Aus der Sicht würde es wieder Sinn machen in jedem Fall eine Exception zu werfen. Dann könnte man noch eine Funktion schreiben, die Exceptions zu speichern und man hätte eine schöne Exception Logdatei / Datenbank und hat die Chance eventuelle Fehler zu beheben. Das würde jedoch sehr viele Exceptions und Exception Handlings im Programm bedeuten - *Seufz*.

      throw Gruß("T-Rex");

      1. Hallo,

        Noch so spät Wach? Dann erstmal Danke, dass du nochmehr schlaf für mich opferst :).

        spät? Es ist doch noch früh am Abend ...

        throw Gruß("T-Rex");

        catch
         { Ciao,
            Martin
         }

        --
        Fettflecke werden wieder wie neu, wenn man sie regelmäßig mit etwas Butter einschmiert.
        Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
      2. Lieber T-Rex,

        In Ausnahmesituationen.

        Wann erkenne ich so eine Ausnahme?

        wenn Deine Applikation von Dir so geschrieben wurde, dass beim Aufruf immer der Tabellenname mitgeliefert wird, dann wäre das Fehlen desselben eine Ausnahme.

        Liebe Grüße,

        Felix Riesterer.

        --
        ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
        1. Wann erkenne ich so eine Ausnahme?

          wenn Deine Applikation von Dir so geschrieben wurde, dass beim Aufruf immer der Tabellenname mitgeliefert wird, dann wäre das Fehlen desselben eine Ausnahme.

          Ja aber wenn der Tabellenname nicht vorhanden ist könnte auch ein return false kommen. Wenn kein PrimaryKey vorhanden ist, wird ja auch nichts gelöscht und ein false wird zurück gegeben.

          Gruß
          return T-Rex

          1. Lieber T-Rex,

            es ging mir nicht um die Logik hinter dem Rückgabewert, sondern um "die Definition von Ausnahme". Du wolltest doch wissen, woran Du eine solche erkennen kannst...

            Liebe Grüße,

            Felix Riesterer.

            --
            ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
          2. Ja aber wenn der Tabellenname nicht vorhanden ist könnte auch ein return false kommen.

            Dann müsstest du konsequenterweise aber auch sagen, selbst wenn die Datenbank nicht existiert, kommt nur false zurück. Auch dann kann ja nichts gelöscht werden.
            Irgendwann geht der Fehler in eine Richtung, wo er nicht mehr gewöhnlich ist sondern wo es kritisch wird.
            Wenn ein Eintrag namens 123 nicht da ist, ok dann ist der eben nicht da. (*) Aber wenns die angesprochene Tabelle nicht gibt, spricht das für einen schweren Fehler im Programm. Da sollte es dann schon gebührend knallen. Eine Exception ist auch ein Zeichen an den Programmierer, dass etwas wirklich ernstes schief gelaufen ist, das wahrscheinlich auf einen Programmiergehler hindeutet.

            (*) Wobei auch das ein schwererer Fehler sein kann. Immerhin kam von irgendwoher die Idee, Eintrag 123 zu löschen. Diese Zahl wurde also wahrscheinlich kurz vorher irgendwo gefunden. Wenn die dann plötzlich nicht mehr da ist, wäre mir das auch eine Meldung an den User wert. Z.B. "wurde bereits gelöscht, check mal warum das auf einmal nicht mehr da ist".

      3. Tach!

        Wann erkenne ich so eine Ausnahme?
        Wieder zu meinem Beispiel. Das der Tabellenname fehlt wäre so eine Ausnahme?

        Nein, der Tabellenname ist eine Abhängigkeit. Ohne diesen darf das Objekt gar nicht erstellt werden können (zumindest für einen 0815-Anwendungsfall eines Tabellen- oder Datensatzobjekts). Und es sollte zumindest so eine elementare Prüfung stattfinden, wie das Prüfen auf Leerstring. Dass der Konstruktur das DBMS befragt, ob es die Tabelle gibt, ist unter Umständen nicht unbedingt erforderlich.

        Eine Ausnahme wäre, wenn das Statement fehlschlägt, vielleicht weil eine Tabelle mit solchem Namen nicht existiert, keine Berechtigung oder eine Sperre vorliegt - irgend etwas, das abseits der normalen Funktionalität abläuft. Und dieses Abseits kann man nicht pauschal konkretisieren, weil es anwendungsfallabhängig ist.

        Das würde jedoch sehr viele Exceptions und Exception Handlings im Programm bedeuten - *Seufz*.

        Das ist natürlich. Nicht selten benötigt man mehr Programmierung für das Reagieren auf Ausnahmezustände.

        dedlfix.

  2. Moin!

    Du schon wieder mit einem Dependency-Injection-Thema! ;)

    Grundsätzlich sollte man nicht beliebig viele Exceptions werfen, nur weil man das Konzept mal gehört hat. Exceptions sind auch nichts, was man als Ersatz für GOTO heranziehen sollte, also zur Programmflusssteuerung.

    Exceptions sollten insbesondere auch fangbar sein und gefangen werden. Es braucht also ein Konzept, wie man mit Exceptions aus diversen Codeabteilungen umgehen will. Dazu gibts mindestens zwei verbreitete Konzepte.

    Und wenn man alle diese Dinge beachtet hat, kommt dann vermutlich noch die Realität daher und macht einem einen Strich durch die Rechnung. :)

    Hab da ein Objekt. Dieses Objekt verwaltet einen Datensatz. Es gibt bei diesem Objekt unter anderem eine Methode "delete". Diese Löscht den Datensatz in der Datenbank. Die Methode braucht zwingend den Tabellenname. Es gibt 3 "Fehlerfälle":

    1. Tabellenname ist nicht bekannt -> Exception
    2. Es gibt keinen Primärschlüssel -> return false (Löschen war also nicht erfolgreich)
    3. Datensatz wurde bereits gelöscht -> return true (Löschen war bereits erfolgreich)

    Ich streite gerade mit mir selbst wieso ich nicht bei 1,2 und 3 einfach Exceptions werfe. Da ist eine kleine Stimme und die sagt dass alles was die Methode nicht mit Erfolg beendet, sprich das die Methode ihre Arbeit erledigt, mit einer Exception quittiert werden sollte. Die etwas lautere Stimme sagt, dass nur eine Exception geworfen werden sollte, wenn es "Technisch" unmöglich ist, dass die Methode ausgeführt wird, was eben nur dann eintritt, wenn der Tabellenname nicht bekannt ist.

    Wenn du mit return true|false schon beide möglichen Fälle für Booleans abgedeckt hast für "Fehlerfälle", und zusätzlich noch eine Exception für ganz harte Fails, wie sieht denn dann der Erfolgsfall-Rückgabewert der Funktion aus?

    Grundsätzlich ist es eine gute Idee, wenn man den Code so gestaltet, dass möglichst keine Fehler gemacht werden können. Damit spiele ich auf deinen ersten Fehlerfall an: Wenn du dein Objekt so schreibst, dass man es nur dann erstellen kann, wenn ein Tabellenname übergeben wird, dann kann die delete-Methode nur aufgerufen werden, wenn der Tabellenname bekannt ist, und der Grund für die Exception entfällt, weil der Fehlerfall entfällt.

    Da sind wir dann wieder bei der Dependency Injection: Wenn das Objekt im Konstruktor den Tabellennamen übergeben bekommen muss, ansonsten kein Objekt erstellt wird (Konstruktor wirft Exception), dann ist für den Entwickler die Sache sonnenklar, er wird sofort beim ersten Testlauf mit der Nase drauf gestoßen, weil er das Objekt, was er eigentlich gar nicht wirklich erstellen wollte, sondern das bitteschön erst mal zu Testzwecken nur schnellschnell hergestellt werden soll, eben nicht ohne diesen Aufwand bekommt. Es kann also gar kein Code entstehen, der das Objekt erst mal erstellt und dessen andere Methoden benutzt, und am Ende kommt man ans Löschen und entdeckt: "Oh, es braucht den Tabellennamen!", und vergisst zwei versteckte Stellen, wo dieser Name auch noch hätte gesetzt werden müssen.

    Im übrigen frage ich mich, was ein Objekt, welches "einen Datensatz verwaltet", mit dem Tabellennamen will? Wenn ich die Delete-Methode dieses Objekts aufrufen würde, dann würde dieses Objekt sich intern an das Tabellen-Objekt wenden, aus dem es herkommt, und das als Referenz bei der Konstruktion mit übergeben wurde, und dort die Delete-Methode mit seiner eigenen ID aufrufen. Und das Tabellen-Objekt weiß dann natürlich, wie es etwas in der Datenbank löscht, indem es den DELETE-Query an das Datenbank-Objekt schickt, in dem die Tabelle steckt...

    Auf diese Weise hat man gleich alle seine Abhängigkeiten schön ineinander gekapselt: Das Tabellen-Objekt kann nicht existieren, wenn es nicht aus dem DB-Objekt heraus erstellt wurde, und es enthält für weitere Querys eben dieses DB-Objekt intern. Das Daten-Objekt kommt aus dem Tabellen-Objekt heraus, wenn man die Methode zum Lesen eines Datensatzes aufruft, und es enthält intern das Tabellen-Objekt für die weitere Lebensdauer dieses Datensatzes.

    Also braucht nur noch das DB-Objekt genannt bekommen, für welchen Tabellennamen es ein Tabellen-Objekt erzeugen soll, und über dieses Tabellenobjekt wird dann das Datensatzobjekt erzeugt, welches ohne Kenntnis des Tabellennamens weitere Aktionen für sich in seiner Tabelle veranlassen kann.

    Ok, mal zurück zu den Exceptions:

    Dein Datenobjekt hat jetzt also immer fest die Verbindung zu seiner zugehörigen Tabelle, der Grund für das Scheitern ohne diese Verbindung ist entfallen.

    Bleiben noch zwei Fälle:
    1. Der Lösch-Query kann erfolgreich ausgeführt werden und liefert die Anzahl der gelöschten Zeilen in der Tabelle zurück.
    2. Der Query scheitert aus irgendeinem Grund.

    Für den Fall 1 braucht es keine Exception. Weil es sich hier um eine DB-Operation handelt, wird unabhängig von der Sinnhaftigkeit des Ergebnisses dieses einfach zurückgegeben, bei DELETE eben die affected_rows. Wenn die ausführende Applikation sich für die Anzahl der gelöschten Datensätze interessiert und bei "0" was meckern möchte, hat sie dadurch genau die benötigte Information.

    Und für den Fall 2 wirft die Methode, die den Query an die Datenbank sendet und hinterher auf Fehler prüft, sinnvollerweise eine Exception mit der DB-Fehlermeldung als Grund. Auf diese Weise kann man in der Entwicklungsphase schnell und direkt an die DB-Fehlermeldung gelangen, und der Code kann entweder auf try/catch beim löschenden Code verzichten, und einem allgemeinen Exception-Handler überlassen, diesen Fail durch eine allgemeine Fehlerseite dem User zu kommunizieren, oder der löschende Code ist so programmiert, dass try/catch diese DB-Fehler-Exception fängt, und irgendeine Art von Alternativprogrammierung greifen kann.

    Noch ein paar Anmerkungen zu den Exception-Klassen: Man kann die von PHP gelieferte Klasse "Exception" erweitern, um individuelleres catch zu erlauben. Details dazu stehen in http://de2.php.net/manual/de/language.exceptions.extending.php. Es gibt in PHP auch ein paar vordefinierte Exceptions, insbesondere diverse durch SPL eingeführte. Diese Exceptions sind alle dafür vorgesehen, bei einem bestimmten Problemtyp zu passen: Die InvalidArgumentException wirft man z.B., wenn irgendwo ein Funktionsaufruf ungültige Parameter festgestellt hat. Der Programmierer wird die Exception dann sehen und sieht: "Ungültiges Argument".

    Ich persönlich mag diese Art Exceptions nicht. Was hilft es mir, wenn ich weiß, dass irgendwo (tief?) in meinem Aufruf-Stack mal ein ungültiges Argument übergeben wurde und eine Exception ausgelöst hat? Auf einer sehr hohen Ebene habe ich gar nicht die Möglichkeit, die feinen Details einer sehr tiefen Ebene zu lösen. Ich müsste diese Exception also wegen Unlösbarkeit des Problems entweder fangen und komplett ignorieren (warum dann überhaupt verschiedene Typen nehmen), oder bis ganz nach "oben" ins Hauptprogramm durchschlagen lassen und dem User eine allgemeine Fehlermeldung zeigen.

    Mein Exception-Stil, der u.A. auch vom Zend Framework gepflegt wird, fügt im Prinzip für jede Klasse oder Klassengruppe eine eigene Exception ein, die von allen zugehörigen Klassen geworfen werden kann:

    class Abc_Def_Service -> throw new Abc_Def_Exception()

    class Abc_Def_Exception extends Abc_Exception

    class Abc_Exception extends Exception

    Auf diese Weise kann man ebenfalls alle Exceptions fangen mit catch (Exception $e), man kann alle Exceptions seiner eigenen Library fangen mit catch (Abc_Exception $e), und man kann selektiv alle Exceptions vom Abc_Def_Service, Abc_Def_Handler, Abc_Def_Dataobject und so weiter fangen: catch (Abc_Def_Exception $e).

    Wenn man das letztgenannte tut, kann man sicher sein, dass man keine Exceptions vom Abc_Xyz_Service fängt, denn der würde Abc_Xyz_Exception werfen. Man kann die Quelle gefangener Exceptions also eingrenzen auf die bekannten Codeteile. Wenn Abc_Def_Service intern tatsächlich Abc_Xyz_Service benutzt, und dieser dann eine Exception wirft, gibts zwei Möglichkeiten:

    Entweder Abc_Def_Service weiß, dass da Abc_Xyz_Exceptions kommen könnten, und hat ein passendes try/catch. Das wird dann greife, die Exception fangen, und der Abc_Def_Service wird wissen, was im Falle einer Exception als Alternativprogramm zu tun ist. Sollte der Programmierer zu der Überzeugung kommen, dass diese Abc_Xyz_Exception als Fehlerfall nicht behebbar ist, und eigentlich eine Exception geworfen gehört, so würde er dann eine neue Abc_Def_Exception werfen - und ab PHP 5.3 die gefangene Exception als dritten Parameter in den Konstruktor mit hineingeben, damit diese interne Information der Fehlerauslösung fürs eventuelle Debugging zur Verfügung steht.

    Alternativ kann der Abc_Def_Service auf das Auftreten einer solchen Exception nicht vorbereitet sein und fängt sie nicht. Das wird dazu führen, dass die Abc_Xyz_Exception weiter nach außen durchschlägt. Der Code, der eine Methode des Abc_Def_Service aufgerufen hat, hat aber nur ein Try/Catch für eine Abc_Def_Exception, weil er speziell nur die internen Fehler des Abc_Def_Service behandeln kann, also wird die Abc_Xyz_Exception hier nicht gefangen, sondern gelangt vermutlich ganz nach außen auf die Hauptprogrammebene, ggf. auch in den definierten Exception-Handler, und wird dort entweder mit einem allgemeinen "Blitzableiter" catch (Exception $e) gefangen, oder verursacht auf andere Art eine allgemeine "funzt nicht"-Seite.

    Gibt es vielleicht irgendeine Regel wann man Exceptions werfen sollte?

    Gruß
    der etwas kränkelnde und deshalb gramatikalisch schwer zu verstehende
    T-Rex

    PS: Hab deine Mail erhalten und wohlwollend zur Kenntnis genommen. ;)

    - Sven Rautenberg

    1. Moin Sven,

      soviel Text und Mühe hätte ich jetzt nicht erwartet, danke :).

      Also ich hab es ja schon an anderer Stelle gesagt, ihr sollt euch nicht zu sehr auf das Beispiel versteifen. Du hast schlicht die Grundlage geändert um einer Exception aus dem Weg zu gehen. Wäre das hier ein Spiel hättest du die Spielregeln geändert -> Cheater ;).
      Wobei ich mir immer noch die Frage stelle ob das eine Exception ist.

      Die Sache mit den unterschiedlichen Exceptions sieht interessant aus. Mir stellt sich nur die Frage "wozu"? Ich bin selbst mal auf die Idee gekommen unterschiedliche Exceptions zu machen. Hab die Idee aber verworfen da ich keinerlei Vorteil darin sah.
      Die Exceptions die ich aktuell werfe, können mittels spezieller Klasse gespeichert werden. Ich hab also in meiner Datenbank ein Exceptionlog. Da sehe ich auch den "trace" also den weg der genommen wurde bis zu der Exception. Wieso brauche ich 3 (oder x) verschieden Exceptions?

      Wir können an die Sache der Exceptions auch mal anders ran gehen. Mein ehemaliger Chef war der Sache z.B. total abgeneigt (nagut er war auch unittests abgeneigt...). Für ihn gab es immer nur die Zustände true und false (und natürlich andere Werte, wenn man z.B. das aktuelle Datum zurück haben möchte). True = Funktion lief wie erwartet und false = Funktion hatte ein Problem. Hier und da wurde dann ein "Errorhandling" benötigt. Das bedeutet in diesem Fall dass er eine Fehlermeldung haben möchte, damit er bzw. der User weiß wo das Problem war. Das hat er dann bei jedem Objekt wo er es brauchte extra implementiert. Das war immer eine Variable ("message") welche einen Fehlertext beinhaltete. Das ganze sah dann immer ungefähr so aus:

      class cKlasse  
      {  
      private $strMessage = "";  
      public function getMessage()  
      {  
         return $this->strMessage;  
      }  
      public function method()  
      {  
      //--- mach irgendwas  
         if(Fehler)  
         {  
            $this->strMessage = "Fehler!";  
            return false;  
         }  
      }  
      }  
      
      

      Und schwups sind wir bei meiner Ursprungsfrage die ich immer noch nicht geklärt sehe, wann werfe ich eine Exception? Wie man bei meinem alten Chef sieht muss man gar keine Exceptions schmeissen.
      Andere sagen bei Ausnahmen. Tjo bei dem Wort Exception hätte ich das fast vermutet, aber wann spreche ich von einer Ausnahme? Wenn die Funktion ihrer Aufgabe nicht nachkommen kann? Wenn ja, dann müsste ich bei meinem Beispiel 3 Exceptions schmeissen. Oder gibt es dafür keine Regel und ich mache es nach Gefühl?

      • Gruß T-Rexberg
      1. Lieber T-Rex,

        eigentlich hat Sven Dir schon alles sehr ausführlich beantwortet. Schade, dass Du vor lauter Bäumen keinen Wald mehr siehst.

        Und schwups sind wir bei meiner Ursprungsfrage die ich immer noch nicht geklärt sehe, wann werfe ich eine Exception?

        Im Ausnahmefall, wie der Name schon sagt.

        Tjo bei dem Wort Exception hätte ich das fast vermutet, aber wann spreche ich von einer Ausnahme? Wenn die Funktion ihrer Aufgabe nicht nachkommen kann?

        Wenn sie das nicht "kann", obwohl sie es "müsste". In Deinem Fall sagtest Du, dass der Tabellenname immer mitgegeben werden muss, sonst kann die Funktion nicht das tun, was sie soll.

        Wenn ja, dann müsste ich bei meinem Beispiel 3 Exceptions schmeissen.

        Wie kommst Du nun auf drei? In einem der drei Fälle kann die Funktion "true" zurückliefern, da sie korrekt den Datensatz hat löschen können. Im anderen Fall, wenn der Datensatz nicht (mehr) existiert, konnte sie trotzdem korrekt ihre Aufgabe lösen, wenngleich das gewünschte Ergebnis auf unerwartetem Weg zustande kam. Wegen dem "unerwartet" käme eine Exception in Frage.

        Im dritten Fall kann die Funktion ihre Aufgabe nicht lösen, da ihr eine notwendige Information fehlt, nämlich der Tabellenname. Das bedeutet, dass Du als Programmierer einen Fehler gemacht hast. Das ist auf jeden Fall eine Exception wert.

        Oder gibt es dafür keine Regel und ich mache es nach Gefühl?

        Wie Sven schon angedeutet hat, macht das jeder nach seinem eigenen System. In Deinem Fall gibt es einen sehr guten und einen guten Grund eine Exception zu werfen. Der sehr gute Grund ist das Versagen des Programmierers einen (gültigen!) Tabellennamen zu übergeben. Der gute Grund ist das unerwartete ("unerwartet" beschreibt doch eine Ausnahme, oder nicht?) Fehlen des zu löschenden Datensatzes.

        Die Frage ist, wie Du mit den beiden Exceptions, die ich als "guten Grund" oder "sehr guten Grund" beschrieben habe, umgehst. Fehlt ein gültiger Tabellenname, kann das Script nicht ordnungsgemäß weiterarbeiten und muss eine Exception werfen. Oder was machst Du, wenn die Folgeoperationen auf eine korrekte Löschung angewiesen sind und zwingend die Bestätigung für "nicht (mehr) vorhanden" benötigen? Ohne gültigen Tabellennamen kann diese Bestätigung nicht erfolgen. Hier wird eine Exception wohl zum Abbruch des Scripts führen (müssen).

        Im anderen Fall, wenn der Datensatz als "nicht (mehr) vorhanden" _bestätigt_ werden kann, dann ist der Grund des Nichtvorhandenseins vielleicht ein außergewöhnlicher, stört aber den weiteren Verlauf des Scriptes nicht unbedingt. Eine solche Exception sollte vielleicht gelogged werden, aber nicht zum Abbruch des Scripts führen.

        Liebe Grüße,

        Felix Riesterer.

        --
        ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
        1. eigentlich hat Sven Dir schon alles sehr ausführlich beantwortet. Schade, dass Du vor lauter Bäumen keinen Wald mehr siehst.

          So fühle ich mich auch gerade...

          Also aus deinem Posting nehme ich mit, dass jeder selbst sein System hat ob und wann er Exceptions wirft. Daraus schliesse ich, dass es keine gängigen Anhaltspunkte gibt, außer es gibt eine Ausnahme. Diese definier ich jetzt so, dass etwas womit die Operation rechnet bzw. auf dessen sie basiert nicht vorhanden ist. In meinem Fall wäre das der Tabellenname.

          Gruß
          exception of evolution
          T-Rex

          1. Tach!

            Also aus deinem Posting nehme ich mit, dass jeder selbst sein System hat ob und wann er Exceptions wirft.

            Na klar, solange es keine Gesetze für Programmierstile gibt.

            Daraus schliesse ich, dass es keine gängigen Anhaltspunkte gibt, außer es gibt eine Ausnahme. Diese definier ich jetzt so, dass etwas womit die Operation rechnet bzw. auf dessen sie basiert nicht vorhanden ist. In meinem Fall wäre das der Tabellenname.

            Warum ist das in diesem Fall der Tabellenname? Warum ist der nicht bereits zwingend vorhanden? Du wirst dir diese Frage gefallen lassen müssen, solange du keinen konkreten Anwendungsfall präsentierst, warum der Tabellenname abwesend sein kann. Bis dahin muss man vom Üblichen ausgehen und da wäre das Fehlen bereits im Konstruktor aufgefallen. - Oder aber, du bringst Beispiele, bei denen sich das Problem durch eine bessere Vorgehensweise nicht oder nur schwer auflösen lässt.

            dedlfix.

            1. Sooooo nehmen wir mal an der Tabellenname wird im Konstructor übergeben. Dann kann es natürlich sein, dass man einen Tabellennamen übergeben könnte, den es schlicht nicht gibt. Dann wäre natürlich eine Funktionalität nicht schlecht, welche überprüft ob es den Tabellennamen gibt oder nicht. Wenn es den Tabellennamen nicht gibt sollte logischerweise eine Exception geworfen werden. Bei diesem Beispiel sehe auch ich auf anhieb das eine Exeption an der Stelle fast schon zwingend ist.

              Dann stehe ich aber vor dem unschönen Problem dass ich jedes erzeugen einer Instanz mit einem Try/Catch umschliessen muss. Wenn ich das richtig sehe, kann ich meinen alten Chef verstehen, der sich komplett gegen Exceptions ausgesprochen hat. Das würde den Code unnötig aufblähen.

              Könnt diese Gedanken bitte kommentieren.

              Gruß
              aufgeblähter (wegen zu viel Lebkuchen)
              T-Rex

              1. Tach!

                Sooooo nehmen wir mal an der Tabellenname wird im Konstructor übergeben. Dann kann es natürlich sein, dass man einen Tabellennamen übergeben könnte, den es schlicht nicht gibt. Dann wäre natürlich eine Funktionalität nicht schlecht, welche überprüft ob es den Tabellennamen gibt oder nicht. Wenn es den Tabellennamen nicht gibt sollte logischerweise eine Exception geworfen werden. Bei diesem Beispiel sehe auch ich auf anhieb das eine Exeption an der Stelle fast schon zwingend ist.

                Angenommen, du hast dich dafür entschieden, den Tabellennamen im Konstruktor (oder vom Konstruktor initiiert) zu prüfen, dann wäre eine Exception (wenn ich jetzt nichts übersehen habe) die einzige Möglichkeit, eine Instantiierung des Objektes zu verhindern. Anderenfalls wird ein Objekt erzeugt, und wenn du nicht irgendeine Eigenschaft oder Methodenrückgabewert prüfst, die/der dir Prüfungsfehler signalisiert, bekommst du höchstwahrscheinlich Folgefehler.

                Den Tabellennamen zu prüfen ist technisch kein direktes Problem. Du kannst zum Beispiel das INFORMATION_SCHEMA befragen. Dazu wird aber jetzt schon eine Datenbankverbindung benötigt und Dinge wie Lazy Connect wären damit schon gestorben. Eine Alternative wäre, nur offensichtliche Problemfälle zu prüfen (Leerstring) und den Rest der sowieso benötigten Reaktion auf andere syntaktische Fehler zu überlassen.

                Dann stehe ich aber vor dem unschönen Problem dass ich jedes erzeugen einer Instanz mit einem Try/Catch umschliessen muss. Wenn ich das richtig sehe, kann ich meinen alten Chef verstehen, der sich komplett gegen Exceptions ausgesprochen hat. Das würde den Code unnötig aufblähen.

                Der Code "bläht" sich mit jeder Art, auf un- und vorhersehbare Fehler zu reagieren, auf. Wenn du kein Exception-Handling nimmst, brauchst du für ein robustes Programm eben "herkömmliche" if-else-Unterscheidungen. Diese jedoch stehen an jeder Stelle, an der was passieren kann. In einem try-Block hast du wenigstens die Möglichkeit, ein Bündel voll Anweisungen zu notieren, und dabei erstmal vom Gut-Fall auszugehen, ungestört durcharbeiten zu können. Besonders dann ist das interessant, wenn dich nicht der genaue Fehlerort interessiert, sondern nur ob während dieses Bündels alles gut ging oder eben nicht. Als Programmierer wirst du dich vielleicht für die genaue Stelle interessieren, der Anwender, der statt der Anweisungen des Bündels jedoch nur eine Einzelaktion (in seinem UI) sieht oder startet, kann mit einem solchen Detail weniger anfangen und bekommt nur das Ergebnis zu sehen. Damit der Programmierer nachher in seinen Logs oder aus der vorgelesenen Fehlermeldung die genaue Stelle findet, ist eine Unterscheidung wichtig. Das kann einerseits ein signifikanter Meldungstext sein, andererseits auch bereits durch den Namen der Exception eindeutig werden. Und damit hätten wir dann auch einen Grund, eigene Exceptions abzuleiten. Die müssen ja nicht unbedingt Funktionalität zur Basis-Exception hinzufügen, ein anderer Name reicht oftmals schon für die Erkennbarkeit der problematischen Stelle.

                Ein weiterer Grund für die Erstellung eigener Exceptions ist, dass man zum Beispiel Datenbankfehler erwartet und in seinem Catch-Block nur darauf reagieren will. Für andere Exceptions hat man für den Augenblick keine Reaktion vorgesehen, also lässt man die anderen ungefangen weiterziehen und kümmert sich vielleicht in irgendeiner übergeordneten Ebene darum.

                dedlfix.

                1. Also erstmal auch dir ein riesen Dank für deine Mühe!

                  Mein Problem ist glaube ich eine Entscheidung zu treffen ob ich Exceptions wirklich komplett in mein System einbaue. Meine Befürchtung ist, dass der Code dadurch so sehr aufbläht wird, dass er nicht mehr wartbar wird. Etwas läuft schief, liegt es am Hauptsystem oder ist eine der tausenden catch Blöcke dafür verantwortlich? Und was machen die catch Blöcke überhaupt? Sind die in der Large eine wirkliche Alternative zu erstellen (z.B. Platzhalterbild laden, wenn kein Hauptbild gefunden wurde) oder machen die Catchblöcke nichts weiter als die Exception zu loggen und das Programm ab zu brechen. Wenn dem so ist, dann schaffe ich das auch ohne Exceptions. Wie schon an anderer Stelle gesagt wurde, am Ende wird der Programmablauf beendet und eine Fehlermeldung wird präsentiert oder eine bestimmte Defaultseite wird geladen.

                  Ich habe hier eine Klasse von der wird sehr viel abgeleitet. Wenn ich in diese Klasse eine Exception in den Konstruktor baue, muss ich quasi bei jeder Funktion oder Methode im kompletten System ein try catch block einbauen. Und da frage ich mich ob es die Mühe wert ist. Vor allem würden Fehler dieser Klasse sofort beim testen auffallen.

                  Auf der anderen Seite empfinde ich es als guten Programmierstil Exceptions zu werfen. Denn damit bietet man dem Aufrufer die Chance einen Plan B zu entwickeln. Da ich aber gleichzeitig der Aufrufer bin und weiß, dass es keinen Plan B gibt (zumindest nicht für diese wichtige Klasse), ist die Überlegung natürlich nur logisch ob ich überhaupt Exceptions brauche.

                  Gruß
                  TwoFace
                  T-Rex

                  1. Tach!

                    Mein Problem ist glaube ich eine Entscheidung zu treffen ob ich Exceptions wirklich komplett in mein System einbaue. Meine Befürchtung ist, dass der Code dadurch so sehr aufbläht wird, dass er nicht mehr wartbar wird.

                    Da wirst du erstmal Erfahrung aufbauen müssen, was üblicherweise durch Fehlschläge und Umbarbeiten beim Verlassen des Holzweges begleitet wird.

                    Etwas läuft schief, liegt es am Hauptsystem oder ist eine der tausenden catch Blöcke dafür verantwortlich? Und was machen die catch Blöcke überhaupt? Sind die in der Large eine wirkliche Alternative zu erstellen (z.B. Platzhalterbild laden, wenn kein Hauptbild gefunden wurde) oder machen die Catchblöcke nichts weiter als die Exception zu loggen und das Programm ab zu brechen.

                    Das kommt drauf an. Wenn es eine Lösung für auftretende Probleme oder auch nur eines speziellen Problems gibt, dann kann man das im catch angehen. Wenn nicht, delegiert man das nach oben weiter. Ob es weiter oben korrigiert werden kann, ist nicht mehr Sache der Subroutine.

                    Wenn dem so ist, dann schaffe ich das auch ohne Exceptions. Wie schon an anderer Stelle gesagt wurde, am Ende wird der Programmablauf beendet und eine Fehlermeldung wird präsentiert oder eine bestimmte Defaultseite wird geladen.

                    Das muss, wie schon gesagt, nicht unbedingt sein. Wenn es stattdessen was sinnvolles für den Anwender gibt, sollte man das nehmen und nicht nur eine Meldung bringen. Und selbst die Meldung sollte dem Anwender etwas nützliches sagen und nicht davon ausgehen, dass das nur der Administrator zu Gesicht bekommt.

                    Ich habe hier eine Klasse von der wird sehr viel abgeleitet. Wenn ich in diese Klasse eine Exception in den Konstruktor baue, muss ich quasi bei jeder Funktion oder Methode im kompletten System ein try catch block einbauen. Und da frage ich mich ob es die Mühe wert ist. Vor allem würden Fehler dieser Klasse sofort beim testen auffallen.

                    Was würdest du denn ohne Exception machen? Da kannst du doch auch nicht eventuelle Fehler unbeachtet lassen und einfach weitermachen.

                    Auf der anderen Seite empfinde ich es als guten Programmierstil Exceptions zu werfen. Denn damit bietet man dem Aufrufer die Chance einen Plan B zu entwickeln. Da ich aber gleichzeitig der Aufrufer bin und weiß, dass es keinen Plan B gibt (zumindest nicht für diese wichtige Klasse), ist die Überlegung natürlich nur logisch ob ich überhaupt Exceptions brauche.

                    Vielleicht ist es dann nicht verkehrt, deine Gedanken auch mehr der B-Planung zu widmen. Als Programmierer ist man gelegentlich betriebsblind. Ideal ist es, wenn man auch noch selbst der Anwender ist, dann weiß man schon besser, was man benötigt.

                    Wieauchimmer, mal ein Vergleich zwischen herkömmlicher und Exceptions-Fehlerbehandlung für eine 08/15-Datenabfrage. Ich nehme mal bewusst die mysql-Extension und nicht das objektorientierte mysqli (oder gar PDO, das man auf Exception-Werfen statt herkömmmlicher Fehlermeldungen umstellen kann), weil man mit den herkömmlichen Funktionen und ihrer Art mit Fehlern umzugehen immer wieder in Berührung kommt, egal, wie objektorientier man sein eigenes System baut.

                    $conn = mysql_connect(...);  
                    mysql_select_db('db', $conn);  
                    mysql_set_charset('...', $conn);  
                    $sql = "SELECT ... FROM ...";  
                    $res = mysql_query($sql, $conn);  
                    while ($row = mysql_fetch_assoc($res)) {  
                      ...  
                    }  
                    mysql_close($conn); // braucht man nicht unbedingt
                    

                    Dieser Ansatz ist schön kompakt. Soweit so schlecht, ohne Fehlerbehandlung ist das natürlich Mist.

                    $fehler = false;  
                    if ($conn = mysql_connect(...)) {  
                      if (mysql_select_db('db', $conn)) {  
                        if (mysql_set_charset('...', $conn)) {  
                          $sql = "SELECT ... FROM ...";  
                          if ($res = mysql_query($sql, $conn)) {  
                            while ($row = mysql_fetch_assoc($res)) {  
                              ...  
                            }  
                          } else {  
                            $fehler = true;  
                            // Reaktion auf Abfragefehler  
                          }  
                        } else {  
                          $fehler = true;  
                          // Reaktion auf Set-Charset-Fehler  
                        }  
                      } else {  
                        $fehler = true;  
                        // Reaktion auf DB-Select-Fehler  
                      }  
                      mysql_close($conn);  
                    } else {  
                      $fehler = true;  
                      // Reaktion auf Verbindungsfehler  
                    }  
                      
                    if ($fehler)  
                      // ...
                    

                    Mit voll ausgebauter Fehlerbehandlung wird es nicht übersichtlicher, und dabei gehe ich mal davon aus, dass es für den Anwender unwichtig ist, wo genau der Fehler auftrat, weswegen ich nur ein Flag setze, das hinterher ausgewertet wird. Als "Reaktion" kommt dann im Prinzip nur noch ein Logging für den Administrator in Frage. Man kann das etwas weniger verschachteln, müsste dann aber mit goto ans Ende springen.

                    if (!$conn = mysql_connect(...)) {  
                      $fehler = true;  
                      // Reaktion auf Verbindungsfehler  
                      goto ende;  
                    }  
                    if (!mysql_select_db(...))  
                    ...  
                      
                    ende:  
                    if ($fehler)  
                      // ...
                    

                    Nun das Ganze mit Exceptions.

                    try {  
                      if (!$conn = mysql_connect(...))  
                        throw new Exception('Verbindungsfehler');  
                      if (!mysql_select_db('db', $conn))  
                        throw new Exception('Select-DB-Fehler');  
                      if (!mysql_set_charset('...', $conn)) {  
                        throw new Exception('Set-Charset-Fehler');  
                      $sql = "SELECT ... FROM ...";  
                      if (!$res = mysql_query($sql, $conn))  
                        throw new Exception('Abfragefehler');  
                      while ($row = mysql_fetch_assoc($res)) {  
                        ...  
                      }  
                      mysql_close($conn);  
                    } catch {  
                      // Reaktion auf Fehler  
                      // - Alternative für den Anwender  
                      // - Meldungstext für den Admin loggen  
                    }
                    

                    Hier sind immer noch eine Menge if-Anweisungen drin, was aber an PHP liegt. Die ifs übersetzen sozusagen die herkömmlich gemeldeten Fehlerzustände in Exceptions. Wäre PHP eine vollständig objektorientierte Sprache, würden die Methoden des DB-Objektes selbständig Exceptions werfen und der Code innerhalb des try sähe so aus wie eingangs der kompakte. In diesem Fall bringen Exceptions wieder etwas Übersicht in den Code.

                    dedlfix.

                    1. Du hast recht mit Exceptions habe ich in der Tat keine Erfahrung... und ich hab bis heute schon sehr viel umgebaut :(.

                      Sehr schönes Beispiel!
                      Nehmen wir mal an dieses ganze SQL Zeugs würde in einer Funktion stehen, dan wäre das ganze sogar noch übersichtlicher:

                      try {  
                         my_mysql_connection();  //--- wird eventuell Exception  
                      } catch {  
                        // Reaktion auf Fehler  
                        // - Alternative für den Anwender  
                        // - Meldungstext für den Admin loggen  
                      }
                      

                      dann brauche ich an jeder Stelle ein try catch, sobald ich my_mysql_connection() aufrufe. Wenn die aufrufe in der Nähe voneinander stehen kann man das ganze freilich in einen try catch packen. Wenn die Aufrufe aber in andere Funktionen wiederum gepackt sind, brauche ich in diesen Funktionen auch wieder einen try catch Zweig und wenn man da nur die Exception weiter gibt. Kombiniert mit einem jeweiligen Plan B ist das enorm viel Code. Aber die Lösung hab ich ja jetzt für dieses Problem des vielen Codes. Man darf halt nur nicht vergessen für Fälle wo ein Plan B sinnvoll erscheint diesen auch zu implementieren.
                      Wobei mich das zur nächsten Frage bringt. Wie erzwingt man eine Exception bei einem Unittest? Die Frage kommt aber irgendwann mal gesondert, wenn ich mir dazu mehr Gedanken gemacht habe.

                      try
                      {
                         Gruß
                         T-Rex
                      }catch{
                         echo "Ein Gruß konnte aus unbekannten Gründen nicht gegeben werden!";
                      }

                      1. Tach!

                        Nehmen wir mal an dieses ganze SQL Zeugs würde in einer Funktion stehen, dan wäre das ganze sogar noch übersichtlicher:

                        try {

                        my_mysql_connection();  //--- wird eventuell Exception
                        } catch {
                          // Reaktion auf Fehler
                          // - Alternative für den Anwender
                          // - Meldungstext für den Admin loggen
                        }

                        
                        > dann brauche ich an jeder Stelle ein try catch, sobald ich my\_mysql\_connection() aufrufe.  
                          
                        Ja, wenn du das quer im Quelltext verteilst, brauchst du an jeder Stelle eine Prüfung. Das ist aber prinzipiell unabhängig von der Entscheidung, ob auf Fehler mit Exceptions oder herkömmlich reagiert werden soll. Die wesentlichere Frage ist, an welcher Stelle auf Fehler sinnvollerweise bezogen auf den Anwendungsfall reagiert werden kann, und da muss dann die Behandlung erfolgen.  
                          
                        
                        > Wenn die aufrufe in der Nähe voneinander stehen kann man das ganze freilich in einen try catch packen.  
                          
                        Ist es denn sinnvoll, eine Fehlerbehandlungsstelle über mehrere Arbeitseinheiten zu spannen? Kommt drauf an. Wenn die Arbeitseinheit "eine Abfrage" lautet, dann würde ein try-catch sinnvoll sein. Weiter "oben" heißt die Arbeitseinheit "hole mir die Daten eines Users" und umfasst vielleicht mehrere Abfragen. Wenn das in einem try-catch steht, spannt sich das auch nur über eine Arbeitseinheit. Die Abfrage kann üblicherweise nicht entscheiden, was "weiter oben" für eine Alternative sinnvoll wäre, also reicht sie nur die Exception hoch. Der "oberen" Arbeitseinheit ist in dem Fall mal egal, welche Datenabfrage fehlerhaft war, mit der Lücke ist der User nicht darstellbar. Aber es gibt eine Alternative, weswegen die Exception hier gefangen und erledigt werden kann.  
                          
                        
                        > Wenn die Aufrufe aber in andere Funktionen wiederum gepackt sind, brauche ich in diesen Funktionen auch wieder einen try catch Zweig und wenn man da nur die Exception weiter gibt.  
                          
                        Ja, wie bei herkömmlicher Fehlerbehandlung auch.  
                          
                        
                        > Kombiniert mit einem jeweiligen Plan B ist das enorm viel Code. Aber die Lösung hab ich ja jetzt für dieses Problem des vielen Codes. Man darf halt nur nicht vergessen für Fälle wo ein Plan B sinnvoll erscheint diesen auch zu implementieren.  
                          
                        Je komplexer die Aufgabenstellung wird, desto mehr Fehlerbehandlung muss rein. Das kann man nicht vermeiden, wenn das Programm robust sein soll.  
                          
                        
                        > Wobei mich das zur nächsten Frage bringt. Wie erzwingt man eine Exception bei einem Unittest?  
                          
                        Man baut den Testfall so, dass die zu testende Einheit was zu meckern hat. Manch ein Testframework fängt Exceptions ab, ansonsten kann man den Delinquenten auch selbst in einen try-catch packen und den Test für gut befinden oder dessen Fehlschlagen (wenn keine Exception ausgelöst wurde) weitermelden.  
                          
                          
                        dedlfix.
                        
                        1. Heute ist "dedlfix Tag" :).

                          Also Fehlerbehandlung braucht man freilich immer, die Frage ist nur wo. Denkbar wäre, dass die Objekte, welche den Fehlerverursachen diese auch selbst handhaben. Wie schon gesagt ein Datenbank Fehler wird sehr wahrscheinlich zu einem Programmabbruch führen. Deshalb kann ein entsprechendes Objekt per Default sich um Fehlerbehandlungen selbst kümmern. Natürlich mit entsprechenden Schnittstellen, dass man diese Automatische Fehlerkontrolle noch selbst übersteuern kann und sich gegebenen falls ein Fehlerobjekt nach außen holt.

                          Man baut den Testfall so, dass die zu testende Einheit was zu meckern hat. Manch ein Testframework fängt Exceptions ab, ansonsten kann man den Delinquenten auch selbst in einen try-catch packen und den Test für gut befinden oder dessen Fehlschlagen (wenn keine Exception ausgelöst wurde) weitermelden.

                          Generell muss es natürlich so sein. Ich frag mich jedoch wie man einen Fehler bei mysql_connect() erzeugt. Denn der Fall will ja auch getestet sein. Oder der Fall das der MySQL Server nicht erreichbar ist.

                          Gruß
                          dedlfix Tag Erfinder
                          T-Rex

                          1. Tach!

                            Ich frag mich jedoch wie man einen Fehler bei mysql_connect() erzeugt. Denn der Fall will ja auch getestet sein. Oder der Fall das der MySQL Server nicht erreichbar ist.

                            Versuch einen Verbindungsaufbau zu einer AAdresse, die kein (MySQL-)Server ist, nimm einen nicht existenten Usernamen oder ein falsches Passwort.

                            dedlfix.

                            1. Sooooo jetzt aber:

                                
                              ...  
                              public function method()  
                              {  
                                 try {  
                                    $this->objObjekt->do( $this->arArray );  
                                 } catch (cException $objException) {  
                                    throw $objException;  
                                 }  
                              }  
                              ...  
                              
                              

                              Die Methode "methode" wird aufgerufen. Sie ruft unter anderem die Methode "do" eines anderen Objektes auf. Diese Methode wiederum macht irgendwas und braucht dafür zwingend ein Array. Wenn kein Array übergeben wird, wirft die Methode eine Exception. Die Methode "method" stellt zusammen mit ihrem Objekt jedoch klar, dass auf jeden Fall ein Array übergeben wird. Es kann nicht vorkommen, dass ein String oder sowas übergeben wird und somit wird auch nie eine Exception geworfen.

                              1. Muss ich hier überhaupt ein Try drumherum bauen?
                              2. Wenn ja, wie kann ich hier eine Exception erzwingen ohne eine künstliche Methode, welche arArray einen unrealistischen Wert gibt?
                              3. Ist es überhaupt richtig eine Exception zu werfen, wenn kein Array übergeben wird? Man könnte alternativ auch einen PHP Fehler erzeugen.
                              4. Kann man einen Standardtypen bei der Parameter Übergabe erzwingen? Ich habe es schon so probiert:
                              function do(array $arArray) und so
                              function do((array) $arArray) und so
                              function do((array()) $arArray) und so
                              function do(array() $arArray)

                              Gruß
                              erzwungener
                              T-Rex

                              1. Tach!

                                Wenn kein Array übergeben wird, wirft die Methode [do()] eine Exception. Die Methode "method" stellt zusammen mit ihrem Objekt jedoch klar, dass auf jeden Fall ein Array übergeben wird. Es kann nicht vorkommen, dass ein String oder sowas übergeben wird und somit wird auch nie eine Exception geworfen.

                                1. Muss ich hier überhaupt ein Try drumherum bauen?

                                Müssen muss man gar nichts. Wenn das Array nicht der Grund für eine Exception ist, sehe ich keinen Grund, eine solche abzufangen. Wenn allerdings noch andere Exception kommen könnte, und du dich um sie kümmern wolltest, dann ...

                                1. Wenn ja, wie kann ich hier eine Exception erzwingen ohne eine künstliche Methode, welche arArray einen unrealistischen Wert gibt?

                                Für einen Unit-Test? Da gibt es doch schon einen, der do() testet. Beim method()-Testen testest du die Arbeitsweise von method() und nichts anderes. Ob method() noch do() oder sonstwas aufruft, ist belanglos. Sieh es als Black-Box an.

                                1. Ist es überhaupt richtig eine Exception zu werfen, wenn kein Array übergeben wird? Man könnte alternativ auch einen PHP Fehler erzeugen.

                                Exceptions kann man fangen, PHP-Fehler nur @unterdrücken oder in einen eigenen (mehr oder weniger globalen) Handler laufen lassen. Wenn du objektorientiert und mit Exceptions arbeitest, solltest du bei Exceptions bleiben.

                                1. Kann man einen Standardtypen bei der Parameter Übergabe erzwingen? Ich habe es schon so probiert:

                                Nur bei Klassen (ab PHP5) und Arrays (ab PHP 5.1): Type Hinting

                                dedlfix.

                    2. Moin!

                      Nun das Ganze mit Exceptions.

                      try {

                      if (!$conn = mysql_connect(...))
                          throw new Exception('Verbindungsfehler');
                        if (!mysql_select_db('db', $conn))
                          throw new Exception('Select-DB-Fehler');
                        if (!mysql_set_charset('...', $conn)) {
                          throw new Exception('Set-Charset-Fehler');
                        $sql = "SELECT ... FROM ...";
                        if (!$res = mysql_query($sql, $conn))
                          throw new Exception('Abfragefehler');
                        while ($row = mysql_fetch_assoc($res)) {
                          ...
                        }
                        mysql_close($conn);
                      } catch {
                        // Reaktion auf Fehler
                        // - Alternative für den Anwender
                        // - Meldungstext für den Admin loggen
                      }

                      
                      >   
                      > Hier sind immer noch eine Menge if-Anweisungen drin, was aber an PHP liegt. Die ifs übersetzen sozusagen die herkömmlich gemeldeten Fehlerzustände in Exceptions. Wäre PHP eine vollständig objektorientierte Sprache, würden die Methoden des DB-Objektes selbständig Exceptions werfen und der Code innerhalb des try sähe so aus wie eingangs der kompakte. In diesem Fall bringen Exceptions wieder etwas Übersicht in den Code.  
                        
                      Man kann es auch auf die kurze Formel bringen: Mit Exceptions kann man Produktivcode und Fehlerbehandlungscode voneinander trennen.  
                        
                      Und in der Regel wird man in einer vollständig OOP-orientierten Applikation nie diese Befehlsabfolge innerhalb einer einzigen Klasse vorfinden. Üblicherweise wird das Herstellen der DB-Connection automatisch durch instanziieren der DB-Klasse passieren (vorzugsweise "lazy"), danach steht dann eine Query-Methode zur Verfügung (deren erstmaliger Aufruf stellt die DB-Verbindung her bzw. checkt durch ein Ping, ob die Verbindung noch existiert), und das Schließen der Verbindung wird dem Script-Ende-Aufräumen überlassen.  
                        
                      Die einzelnen Schritte können jeweils ihren Erfolg prüfen - das ist in dem Einzelfall jeweils relativ unaufwendig. Und diese Einzelschritte können auch jeweils ihre Exception werfen.  
                        
                      Ein nutzendes Skript kann um den gesamten Block des DB-Abfragens ein try/catch legen und jede irgendwo geworfene Exception fangen.  
                        
                      Und was ist das Alternativprogramm? Mal als Beispiel: Wenn eine DB-Abfrage nur die Anzahl eingeloggter User ermitteln soll, wäre das Alternativprogramm `return 0;`{:.language-php}, denn um eine lebenswichtige Info handelt es sich eher nicht.  
                        
                       - Sven Rautenberg
                      
                      1. Tach!

                        Und in der Regel wird man in einer vollständig OOP-orientierten Applikation nie diese Befehlsabfolge innerhalb einer einzigen Klasse vorfinden.

                        Ja, mir war nur grad nichts besseres eingegfallen. Zumindest kennt man das in dieser zusammenstehenden Form aus 08/15-Scripts.

                        [...] das Schließen der Verbindung wird dem Script-Ende-Aufräumen überlassen.

                        Auch hier stimme ich zu, mysql_close() verwende ich normalerweise nicht. Ich hatte nur für das Beispiel eine Anwendungsmöglichkeit für den finally-Teil gesucht - bevor ich feststellte, das es den in PHP nicht gibt. Es ist dann einfach drin geblieben.

                        Und was ist das Alternativprogramm? Mal als Beispiel: Wenn eine DB-Abfrage nur die Anzahl eingeloggter User ermitteln soll, wäre das Alternativprogramm return 0;, denn um eine lebenswichtige Info handelt es sich eher nicht.

                        Lebenswichtig ist sie nicht, aber 0 wäre vermutlich schlicht falsch. Und komisch obendrein, wenn grad jemand eingeloggt ist und man ihm 0 erzählen tät. Dann doch lieber null oder -1, damit man im aufrufenden Teil einen "nicht ermittelbar"-Text wählen kann.

                        dedlfix.

              2. Hey T-Rex,

                Dann stehe ich aber vor dem unschönen Problem dass ich jedes erzeugen einer Instanz mit einem Try/Catch umschliessen muss.

                ich weiß weder, ob es schon genannt wurde, noch, ob Du damit was anfangen kannst. Aber kennst Du die PHP-Funktionen set_exception_handler und Co schon?
                Das ist zwar eine PHP-Funktion und kein allgemeines Programmiertechnik-Thema, könnte Dich aber eventuell weiterbringen. Vielleicht haben auch andere Programmiersprachen eine ähnliche Funktion.

                Ansonsten gäbe es vielleicht die Möglichkeit, den gesamten Programmfluss in einen einzigen Try-Block einzufassen und den anschließenden Catch-Block die "oberste" Exception fangen zu lassen. So könntest Du alle Exceptions abfangen, die keiner speziellen Behandlung bedürfen, was in vielen Fällen ausreichen dürfte.

                Gruß, Dennis

                1. Hey T-Rex, hey dedlfix,

                  sorry, mein Posting war wohl so eine Art "Doppel-Post". Dedlfix hat ja bereits das beschrieben, was ich auch meinte.

                  Gruß, Dennis

                2. Ansonsten gäbe es vielleicht die Möglichkeit, den gesamten Programmfluss in einen einzigen Try-Block einzufassen und den anschließenden Catch-Block die "oberste" Exception fangen zu lassen. So könntest Du alle Exceptions abfangen, die keiner speziellen Behandlung bedürfen, was in vielen Fällen ausreichen dürfte.

                  Den Gedanken hatte ich auch schon. Das setzt aber voraus, dass der gesamte Code in einen try passt. Wenn man irgendwas nochmal zusätzlich kapseln möchte funktioniert es schon wieder nicht. Dann hätte man pro Kapselung nochmal einen try.

                  Bei der Funktion habe ich mir noch keine Meinung gebildet. Die kannte ich aber in der Tat nicht - danke!

                  Gruß
                  Exception Fänger
                  T-Rex

                  1. Tach!

                    Ansonsten gäbe es vielleicht die Möglichkeit, den gesamten Programmfluss in einen einzigen Try-Block einzufassen und den anschließenden Catch-Block die "oberste" Exception fangen zu lassen. So könntest Du alle Exceptions abfangen, die keiner speziellen Behandlung bedürfen, was in vielen Fällen ausreichen dürfte.

                    Das kommt drauf an, wie man "ausreichend" definiert. Eine ungefangene Exception bedeutet immer Programmabbruch. Das dürfte aus Anwendersicht eher "ungenügend" sein.

                    Den Gedanken hatte ich auch schon. Das setzt aber voraus, dass der gesamte Code in einen try passt. Wenn man irgendwas nochmal zusätzlich kapseln möchte funktioniert es schon wieder nicht. Dann hätte man pro Kapselung nochmal einen try.
                    Bei der Funktion habe ich mir noch keine Meinung gebildet. Die kannte ich aber in der Tat nicht - danke!

                    Die ist auch eher als allerletzter Notnagel zu verstehen, ähnlich wie set_error_handler() für herkömmliche Fehler. Allerdings stoppt die Ausführung des Scripts nicht nach dem Ausführen des Errorhandler (bei weniger schwerwiegenden Meldungsklassen). Auf auftretende Fehler sollte man, soweit möglich, vor Ort reagieren und einen möglichen alternativen Programmzweig ausführen, der allen Seiten etwas bringt. Der oberste Fehlerhandler kann nicht viel mehr als das Auftreten für den Administrator dokumentieren und eine allgemeine Tröstmeldung an den Anwender geben. In der Regel wird man sogar nicht mal wissen, wie weit Aufgabe und Ausgabe gediehen sind, um sie einigermaßen ordentlich abzuschließen.

                    dedlfix.

                    1. Hey dedlfix,

                      Das kommt drauf an, wie man "ausreichend" definiert. Eine ungefangene Exception bedeutet immer Programmabbruch. Das dürfte aus Anwendersicht eher "ungenügend" sein.

                      das stimmt natürlich, aus Anwendersicht taugt das gar nichts. Aber deshalb sollte das Programm ja auch möglichst so ausgereift sein, dass keine kritischen Exceptions geworfen werden müssen.

                      Ich kann da nur von mir ausgehen: Für mich ist jede Exception erstmal mit einem Programmabbruch verbunden, da irgendetwas gewaltig schief gelaufen ist (wie z.B. eine nicht vorhandene Datenbank-Tabelle, wobei natürlich alles auf den speziellen Anwendungsfall ankommt).
                      Da ich set_exception_handler() (in PHP) verwende, kann ich jetzt erstmal an allen möglichen Stellen mit Exceptions um mich werfen (nein, so schlimm ist das in der Praxis natürlich nicht), die ja alle gefangen werden. Das Programm sollte dann so sein, dass diese Exceptions im Produktivbetrieb erst gar nicht mehr geworfen werden müssen, sonst hab ich einen großen Fehler im Programm eingebaut oder es ist etwas wirklich schwerwiegendes passiert; dann ist ein Abbruch aber auch gerechtfertigt.

                      Anders ist es bei Exceptions, mit deren Auftreten ich rechne: Die bekommen einen eigenen try-Block mit einem spezifischen catch. Häufig nutze ich das z.B., wenn ich damit rechne, dass eine Datei nicht vorhanden, dies aber für den weiteren Programmfluss irrelevant ist.

                      Daher also meine Aussage "häufig ausreichend", da es bei mir sehr viele kritische Exceptions, jedoch nur wenige spezielle gibt.

                      Den Gedanken hatte ich auch schon. Das setzt aber voraus, dass der gesamte Code in einen try passt. Wenn man irgendwas nochmal zusätzlich kapseln möchte funktioniert es schon wieder nicht. Dann hätte man pro Kapselung nochmal einen try.
                      Bei der Funktion habe ich mir noch keine Meinung gebildet. Die kannte ich aber in der Tat nicht - danke!

                      Die ist auch eher als allerletzter Notnagel zu verstehen, ähnlich wie set_error_handler() für herkömmliche Fehler. Allerdings stoppt die Ausführung des Scripts nicht nach dem Ausführen des Errorhandler (bei weniger schwerwiegenden Meldungsklassen). Auf auftretende Fehler sollte man, soweit möglich, vor Ort reagieren und einen möglichen alternativen Programmzweig ausführen, der allen Seiten etwas bringt. Der oberste Fehlerhandler kann nicht viel mehr als das Auftreten für den Administrator dokumentieren und eine allgemeine Tröstmeldung an den Anwender geben.

                      Das stimmt natürlich alles!

                      In der Regel wird man sogar nicht mal wissen, wie weit Aufgabe und Ausgabe gediehen sind, um sie einigermaßen ordentlich abzuschließen.

                      Meiner Meinung nach sollte die Ausgabe erst ganz zuletzt ausgeführt werden, sodass das kein Problem darstellen sollte (außer natürlich, BEI der Ausgabe tritt eine Exception auf; hm, wo ich da gerade so drüber nachdenke: Was macht man in so einem Fall?).
                      Die Aufgaben abzuschließen ist in diesem Fall doch auch kein Problem? Im Gegensatz zu fatalen Fehlern werden doch bei Exceptions die Destruktoren der Klassen noch ausgeführt oder irre ich mich? So könnte man beispielsweise bei den Datenbank-Aufgaben doch einen Roll-Back für alles Unvollständige durchführen (wie gesagt, bin mir nicht sicher und korrigiert mich bitte, wenn dies falsch ist).

                      Gruß, Dennis

                      1. Lieber Dennis,

                        du hast meine Problem beseitigt und mich zu einer Entscheidung gebracht. Was du geschrieben hast ist logisch und passt genau zu meinem vorgehen!! Du hast absolut recht. Viele Exceptions die geworfen werden führen zu einem Programmabbruch - vor allem wenn man das Wort "Exception" und "Tabelle" in einem Satz verwendet. Wieso also Codeverseuchende try catch Blöcke.

                        Jetzt macht die Funktion die du präsentiert hast sehr viel Sinn :). Programmabbrüche lasse ich einfach in diese Funktion laufen, da werden sie Dokumentiert und eine "reine HTML Seite" mit einer Variable, nämlich einem Fehlertext wird präsentiert.
                        Das ist eine super Idee :).

                        Gruß
                        lass dir mal einen besseren Spruch einfallen als ","
                        T-Rex

                      2. Tach!

                        Ich kann da nur von mir ausgehen: Für mich ist jede Exception erstmal mit einem Programmabbruch verbunden, da irgendetwas gewaltig schief gelaufen ist (wie z.B. eine nicht vorhandene Datenbank-Tabelle, wobei natürlich alles auf den speziellen Anwendungsfall ankommt).

                        Das ist meiner Meinung nach noch nicht so gewaltig, komplett abzubrechen. In dem Fall wird man üblicherweise zwar die geplante Aktion abbrechen, man kann jedoch einen alternativen Weg gehen. In dem sollte man irgendwas sinnvolles für den Anwender ausführen, zum Beispiel seine Bestellung per Mail abschicken, anstatt ihn in die Arme der Konkurrenz zu entlassen, oder ihm sagen, was er zur Zielerreichung machen kann. Gewaltige Ereignisse wäre eher sowas wie Speicherüberlauf, und das meldet PHP herkömmlich und unabfangbar.

                        Anders ist es bei Exceptions, mit deren Auftreten ich rechne: Die bekommen einen eigenen try-Block mit einem spezifischen catch. Häufig nutze ich das z.B., wenn ich damit rechne, dass eine Datei nicht vorhanden, dies aber für den weiteren Programmfluss irrelevant ist.

                        Es kommt drauf an, ob ich in dem Falle eine Exception erzeugen würde. Ist die Datei normalerweise vorhanden, dann ist es eine Ausnahme, wenn sie weg ist (z.B. Konfigurationswertedatei im Normalbetrieb). Für ein Setup-Script, das die Datei erstellen oder wenn vorhanden ändern soll, ist es Normalfall, wenn die Datei nicht existiert und rechtfertigt im Prinzip keine Exception.

                        In der Regel wird man sogar nicht mal wissen, wie weit Aufgabe und Ausgabe gediehen sind, um sie einigermaßen ordentlich abzuschließen.
                        Meiner Meinung nach sollte die Ausgabe erst ganz zuletzt ausgeführt werden, sodass das kein Problem darstellen sollte (außer natürlich, BEI der Ausgabe tritt eine Exception auf; hm, wo ich da gerade so drüber nachdenke: Was macht man in so einem Fall?).

                        Dann wird man vielleicht ein bereits gerendertes Seiten-Gerüst haben, in das man statt der seitenspezifischen Ausgabe eine Alternative einfügt. Oder man hat gar nichts, und muss sich halt was angemessenes für den Fall ausdenken.

                        Die Aufgaben abzuschließen ist in diesem Fall doch auch kein Problem? Im Gegensatz zu fatalen Fehlern werden doch bei Exceptions die Destruktoren der Klassen noch ausgeführt oder irre ich mich?

                        Das weiß ich auch grad nicht. Aber ein Destruktor wird die Aufgabe nicht abschließen können, sondern nur die geöffneten Ressourcen ordentlich schließen können. Aber ...

                        So könnte man beispielsweise bei den Datenbank-Aufgaben doch einen Roll-Back für alles Unvollständige durchführen (wie gesagt, bin mir nicht sicher und korrigiert mich bitte, wenn dies falsch ist).

                        ... bei Datenbankaktionen muss man immer mit einem Fehlschlagen rechnen, weswegen die in einen eigenen try-Block gehören. Und dann kann man in den catch-Teil das Rollback einfügen und allgemeine Abschlussarbeiten für Gut- und Fehlerfall ins finally.

                        dedlfix.

                        1. Hey dedlfix,

                          Ich kann da nur von mir ausgehen: Für mich ist jede Exception erstmal mit einem Programmabbruch verbunden, da irgendetwas gewaltig schief gelaufen ist (wie z.B. eine nicht vorhandene Datenbank-Tabelle, wobei natürlich alles auf den speziellen Anwendungsfall ankommt).

                          Das ist meiner Meinung nach noch nicht so gewaltig, komplett abzubrechen. In dem Fall wird man üblicherweise zwar die geplante Aktion abbrechen, man kann jedoch einen alternativen Weg gehen.

                          Wahrscheinlich sind meine "fatalen Fehler" tatsächlich nicht so fatal, wie ich annahm...

                          In dem sollte man irgendwas sinnvolles für den Anwender ausführen, zum Beispiel seine Bestellung per Mail abschicken, anstatt ihn in die Arme der Konkurrenz zu entlassen,

                          Das bedingt aber, dass ich das Fehlschlagen im Produktivbetrieb bereits eingeplant habe. Wie bereits gesagt gehe ich aber davon aus, dass die (meisten) Exceptions im Produktiveinsatz nicht geworfen werden sollten, da ich (hoffentlich) alle Fehlerquellen für diese Exceptions schon eliminiert habe. Was Du beschreibst trifft aber auf meine "speziellen Exceptions" zu.

                          oder ihm sagen, was er zur Zielerreichung machen kann.

                          Damit habe ich persönlich schlechte Erfahrungen gemacht. Viele Benutzer halten eine "sinnlose", automatische Fehlermeldung für besser als eine Fehlermeldung, die ihnen Alternativen aufzeigt. Bei der Fehlermeldung mit dem Angebot von Alternativen denken viele: "Aha. Die kennen das bereits. Aber anstatt was dran zu ändern (sind die zu blöd dazu?), wollen die jetzt, dass ich mich damit zufriedengebe und die Arbeit für die übernehme." Bei der nicht-aussagekräftigen: "Mal wieder diese sch***-Computer. Immer das gleiche. Naja, dann versuche ich es halt nochmal."
                          Traurig, aber wahr: Bei einer aktuellen Studie in unserem Institut kam genau das raus.

                          Gewaltige Ereignisse wäre eher sowas wie Speicherüberlauf, und das meldet PHP herkömmlich und unabfangbar.

                          Ok, stimmt! Wir müssen unterscheiden, ob es sich um Exceptions handelt, auf die ich im Programmfluss reagieren KÖNNTE und solchen, bei denen im Augenblick "Hopfen und Malz verloren sind".

                          Anders ist es bei Exceptions, mit deren Auftreten ich rechne: Die bekommen einen eigenen try-Block mit einem spezifischen catch. Häufig nutze ich das z.B., wenn ich damit rechne, dass eine Datei nicht vorhanden, dies aber für den weiteren Programmfluss irrelevant ist.

                          Es kommt drauf an, ob ich in dem Falle eine Exception erzeugen würde. Ist die Datei normalerweise vorhanden, dann ist es eine Ausnahme, wenn sie weg ist (z.B. Konfigurationswertedatei im Normalbetrieb). Für ein Setup-Script, das die Datei erstellen oder wenn vorhanden ändern soll, ist es Normalfall, wenn die Datei nicht existiert und rechtfertigt im Prinzip keine Exception.

                          Da muss ich mal drüber nachdenken. Im Grunde hast Du aber sicherlich recht. Wo wir ja wieder bei T-Rex Ausgangsfrage wären...

                          In der Regel wird man sogar nicht mal wissen, wie weit Aufgabe und Ausgabe gediehen sind, um sie einigermaßen ordentlich abzuschließen.
                          Meiner Meinung nach sollte die Ausgabe erst ganz zuletzt ausgeführt werden, sodass das kein Problem darstellen sollte (außer natürlich, BEI der Ausgabe tritt eine Exception auf; hm, wo ich da gerade so drüber nachdenke: Was macht man in so einem Fall?).

                          Dann wird man vielleicht ein bereits gerendertes Seiten-Gerüst haben, in das man statt der seitenspezifischen Ausgabe eine Alternative einfügt. Oder man hat gar nichts, und muss sich halt was angemessenes für den Fall ausdenken.

                          Das schon. Aber was, wenn irgendwo dabei ein Fehler auftritt? In letzter Konsequenz darf ich bei sowas doch keine Exception mehr werfen, sondern muss mich auf das interne Error-Handling verlassen!? Sonst käme man doch in Endlosschleife: Anwendungs-Fehler => Exception => Error-Seiten-Fehler => Exception => Error-Seiten-Fehler => Exception... Oder habe ich da was nicht bedacht?

                          Die Aufgaben abzuschließen ist in diesem Fall doch auch kein Problem? Im Gegensatz zu fatalen Fehlern werden doch bei Exceptions die Destruktoren der Klassen noch ausgeführt oder irre ich mich?

                          Das weiß ich auch grad nicht. Aber ein Destruktor wird die Aufgabe nicht abschließen können, sondern nur die geöffneten Ressourcen ordentlich schließen können. Aber ...

                          Hab's grad getestet, die Destruktoren werden aufgerufen. Was aber natürlich nichts daran ändert, dass...

                          So könnte man beispielsweise bei den Datenbank-Aufgaben doch einen Roll-Back für alles Unvollständige durchführen (wie gesagt, bin mir nicht sicher und korrigiert mich bitte, wenn dies falsch ist).

                          ... bei Datenbankaktionen muss man immer mit einem Fehlschlagen rechnen, weswegen die in einen eigenen try-Block gehören. Und dann kann man in den catch-Teil das Rollback einfügen und allgemeine Abschlussarbeiten für Gut- und Fehlerfall ins finally.

                          ich mir bei einigen Dingen nochmal Gedanken über die Grund-Designs machen muss...

                          Auch wenn's eigentlich T-Rex Thread ist, kann ich Dir wieder nur für Deine Anregungen danken!

                          Gruß,
                          der T-Rex mag mein "Gruß," nicht
                          Dennis

                          1. Tach!

                            In dem sollte man irgendwas sinnvolles für den Anwender ausführen, zum Beispiel seine Bestellung per Mail abschicken, anstatt ihn in die Arme der Konkurrenz zu entlassen,

                            Das bedingt aber, dass ich das Fehlschlagen im Produktivbetrieb bereits eingeplant habe. Wie bereits gesagt gehe ich aber davon aus, dass die (meisten) Exceptions im Produktiveinsatz nicht geworfen werden sollten, da ich (hoffentlich) alle Fehlerquellen für diese Exceptions schon eliminiert habe. Was Du beschreibst trifft aber auf meine "speziellen Exceptions" zu.

                            Du kannst bestimmte Situationen vorhersehen und sie trotzdem nicht beeinflussen. Syntaxfehler im Statement kann man vermeiden. Dass ein DBMS jedoch grad mal Pause macht, lässt sich nicht eliminieren. Du kannst lediglich mit entsprechendem Aufwand für eine höhere Verfügbarkeit sorgen, aber 100% wirst du nicht erreichen können. Das gleiche gilt für alle anderen externen Dienste, die man anzusprechen gedenkt.

                            Dann wird man vielleicht ein bereits gerendertes Seiten-Gerüst haben, in das man statt der seitenspezifischen Ausgabe eine Alternative einfügt. Oder man hat gar nichts, und muss sich halt was angemessenes für den Fall ausdenken.
                            Das schon. Aber was, wenn irgendwo dabei ein Fehler auftritt?

                            Das sollte dann so einfach sein, dass es keinen Fehler mehr produzieren kann, beispielsweise eine fest codierte Mini-HTML-Seite. Noch etwas variablen Text einzufügen ist auch exceptionlos möglich.

                            In letzter Konsequenz darf ich bei sowas doch keine Exception mehr werfen, sondern muss mich auf das interne Error-Handling verlassen!? Sonst käme man doch in Endlosschleife: Anwendungs-Fehler => Exception => Error-Seiten-Fehler => Exception => Error-Seiten-Fehler => Exception... Oder habe ich da was nicht bedacht?

                            Theoretisch schon, aber wir befinden uns in einem globalen Exception-Handler, der sollte nicht auch noch Exceptions werfen um die maximale Rekursionstiefe des Systems zu testen ...

                            ... bei Datenbankaktionen muss man immer mit einem Fehlschlagen rechnen, weswegen die in einen eigenen try-Block gehören. Und dann kann man in den catch-Teil das Rollback einfügen und allgemeine Abschlussarbeiten für Gut- und Fehlerfall ins finally.

                            finally kennt PHP nicht, hab ich grad beim Recherchieren für meine andere Antwort festgestellt.

                            dedlfix.

                            1. Hey dedlfix,

                              Du kannst bestimmte Situationen vorhersehen und sie trotzdem nicht beeinflussen. Syntaxfehler im Statement kann man vermeiden. Dass ein DBMS jedoch grad mal Pause macht, lässt sich nicht eliminieren. Du kannst lediglich mit entsprechendem Aufwand für eine höhere Verfügbarkeit sorgen, aber 100% wirst du nicht erreichen können. Das gleiche gilt für alle anderen externen Dienste, die man anzusprechen gedenkt.

                              Dann wird man vielleicht ein bereits gerendertes Seiten-Gerüst haben, in das man statt der seitenspezifischen Ausgabe eine Alternative einfügt. Oder man hat gar nichts, und muss sich halt was angemessenes für den Fall ausdenken.
                              Das schon. Aber was, wenn irgendwo dabei ein Fehler auftritt?

                              Das sollte dann so einfach sein, dass es keinen Fehler mehr produzieren kann, beispielsweise eine fest codierte Mini-HTML-Seite. Noch etwas variablen Text einzufügen ist auch exceptionlos möglich.

                              In letzter Konsequenz darf ich bei sowas doch keine Exception mehr werfen, sondern muss mich auf das interne Error-Handling verlassen!? Sonst käme man doch in Endlosschleife: Anwendungs-Fehler => Exception => Error-Seiten-Fehler => Exception => Error-Seiten-Fehler => Exception... Oder habe ich da was nicht bedacht?

                              Theoretisch schon, aber wir befinden uns in einem globalen Exception-Handler, der sollte nicht auch noch Exceptions werfen um die maximale Rekursionstiefe des Systems zu testen ...

                              gut, dann freue ich mich, dass wir das ähnlich sehen. Allerdings kann ja auch eine einfache Variablenzuweisung schiefgehen, sodass dann in letzter Konsequenz doch das interne Error-Handling anspringen muss. Allerdings sind Exceptions in dem globalen Error- (oder Exception-) Handling natürlich reichlich sinnlos.

                              ... bei Datenbankaktionen muss man immer mit einem Fehlschlagen rechnen, weswegen die in einen eigenen try-Block gehören. Und dann kann man in den catch-Teil das Rollback einfügen und allgemeine Abschlussarbeiten für Gut- und Fehlerfall ins finally.

                              finally kennt PHP nicht, hab ich grad beim Recherchieren für meine andere Antwort festgestellt.

                              Ja, hab ich auch eben gesehen. Aber das Prinzip dahinter gilt ja auch für PHP wie für alle anderen Programmiersprachen auch und zumindest laut Thema wollte T-Rex was zum Thema "Programmiertechnik", auch wenn ich auf PHP umgeschwungen bin.

                              Dennis

                            2. Du kannst bestimmte Situationen vorhersehen und sie trotzdem nicht beeinflussen. Syntaxfehler im Statement kann man vermeiden. Dass ein DBMS jedoch grad mal Pause macht, lässt sich nicht eliminieren. Du kannst lediglich mit entsprechendem Aufwand für eine höhere Verfügbarkeit sorgen, aber 100% wirst du nicht erreichen können. Das gleiche gilt für alle anderen externen Dienste, die man anzusprechen gedenkt.

                              Also ich finde den Weg den Dennis mir gezeigt hat genial. Man kann alle Objekte, Methoden und Funktionen mit Exceptions verseuchen. Wenn der Aufrufer eine Idee hat wie der Fehler abgefangen werden könnte, dann kann er einen Try und Catch block benutzen, ansonsten wird ein Default Weg gegangen. Und gerade bei den Datenbank Zugriffen ist das eine feinde Sache. Denn seien wir mal ehrlich, die meisten Datenbank Zugriffe sind essentiell für das Programm. Wenn man gerade auf dem Ebay Artikel xyz steht und die Daten nicht aus der Datenbank geholt werden können nützt mir auch kein Plan B.

                              Und genau hier war mein Problem. Wenn ich in die Objekte, welche einen Datenbank Zugriff haben, eine Exception einbaue, dann muss die (so dachte ich) immer in ein try catch block. Und das würde den Code gigantisch aufblähen, wobei ich im catch block immer sowas in der art wie $objException->log(); gesehen hab.

                              Wenn ich da an meinen alten Arbeitgeber denke. Der hatte ein System komplett ohne Exceptions und hat auch nicht geprüft ob die Datenbank erreichbar ist oder nicht. Generell sind in dem System etliche Sicherheitslücken drin. Ausfälle hat er wahrscheinlich nie mitbekommen (außer der komplette Server streikte, da helfen aber auch keine Exceptions mehr). Und trotzdem hat er ein System verkauft welches, wenn man alle Kunden zusammen nimmt, 3 Millionen Euro im Monat generiert. Bei 3 Millionen verzichte ich auch auf Exceptions :D.

                              Achja nebenbei was ist finally? Wenn das bedeutet das eine Methode bei einer Vererbung nicht mehr überschrieben werden darf, dann heißt es in PHP schlicht final.

                              Gruß
                              Dennis Tatoo Träger
                              T-Rex

                              1. Tach!

                                Achja nebenbei was ist finally?

                                try {
                                  // A
                                } catch {
                                  // B
                                } finally {
                                  // C
                                }

                                Wenn in Block A etwas exceptioniert, wird in B gesprungen. C wird in jedem Fall ausgeführt, egal ob A durchlief oder B bemüht werden musste.

                                dedlfix.

                                1. Danke für die Antwort! Aber um C aus zu führen könnte man C doch einfach hiner den try catch Block schreiben:

                                  try {
                                    // A
                                  } catch {
                                    // B
                                  }
                                  C

                                  Gruß
                                  T -Rex

                                  1. Tach!

                                    Danke für die Antwort! Aber um C aus zu führen könnte man C doch einfach hiner den try catch Block schreiben:

                                    (Bist du Holländer? Die schreiben die Silben bei zu-Verben auch getrennt.) Jein, wenn die Exception keinen passenden catch-Block findet oder zwar gefangen aber dann doch weitergeworfen wird, kommt das Programm nicht hinter den try-catch-Block, sondern macht beim catch-Block von einem der Aufrufer weiter.

                                    dedlfix.

                  2. Hey T-Rex,

                    Ansonsten gäbe es vielleicht die Möglichkeit, den gesamten Programmfluss in einen einzigen Try-Block einzufassen und den anschließenden Catch-Block die "oberste" Exception fangen zu lassen. So könntest Du alle Exceptions abfangen, die keiner speziellen Behandlung bedürfen, was in vielen Fällen ausreichen dürfte.

                    Den Gedanken hatte ich auch schon. Das setzt aber voraus, dass der gesamte Code in einen try passt.

                    Ja, das muss er. Aber wo könnte es dabei Probleme geben? Insbesondere wenn Du objektorientiert programmierst, hast Du doch am Anfang des Skripts etwas wie

                    $app = new Application($parameter);  
                    // mach was..., z.B. $app->run();
                    

                    Wenn Du das dann in einen try-Block packst und danach eine Haupt-Exception fängst, hast Du doch den gesamten Code der Anwendung in einem einzigen try-Block.

                    Wenn man irgendwas nochmal zusätzlich kapseln möchte funktioniert es schon wieder nicht.

                    Doch, Du kannst ja try-catch-Blöcke auch ineinander verschachteln. So hast Du für eine spezielle Exception einen eigenen catch-Block, für alle anderen aber den äußersten, der alles abfängt. Ähnliches macht im Endeffekt auch die genannte Funktion.

                    Gruß, Dennis

                    1. Tach!

                      Doch, Du kannst ja try-catch-Blöcke auch ineinander verschachteln. So hast Du für eine spezielle Exception einen eigenen catch-Block, für alle anderen aber den äußersten, der alles abfängt.

                      Ein try kann mehrere catch-Blöcke haben, jeder davon kann eine bestimmte Exception-Klasse fangen. Man muss die try-catch-Blöcke also bei lediglich mehreren möglichen Exception-Klassen für denselben zu überwachenden Code nicht schachteln.

                      dedlfix.

          2. Yerf!

            Daraus schliesse ich, dass es keine gängigen Anhaltspunkte gibt, außer es gibt eine Ausnahme.

            Was ich als Anhaltspunkt kenne ist, das man das übermäßige werfen von Exceptions wärend eines Programmlaufes vermeiden sollte.

            Also für Fehlerzustände die häufig auftreten können keine Exceptions werfen oder dem Programmierer eine Möglichkeit geben das vorher abzufragen.

            Aber andererseits sagt das auch der Name: ein Fehler der häufig auftritt ist keine "Ausnahme".

            Bezogen auf dein Beispiel würde ich wenn das Löschen fehlschlägt weil der Datensatz nicht vorhanden ist:

            • in einer Single-User Umgebung in der es nicht vorkommen darf das datensätze "einfach so" verschwinden eine Exception werfen

            • in einer Multi-User Umgebung wo jemand anders den Datensatz gelöscht haben könnte *keine* Exception werfen. Derjenige der Löscht sollte dann aber über die Rückgabe der Methode erfahren können ob etwas gelöscht wurde oder nicht (falls das entsprechend weiter behandelt werden muss)

            Gruß,

            Harlequin

            --
            RIP --- XHTML 2
            nur die Besten sterben jung
  3. Gibt es vielleicht irgendeine Regel wann man Exceptions werfen sollte?

    jo, immer wenn du Lust hast. Das war für mich einer der Gründe mit diesem kranken Java-Wahnsinn aufzuhören. Du hast da überall nur so'n try und catch und throw etc. rumfliegen, daß nicht viel wirkliche Programmleistung übrigbleibt.

    1. jo, immer wenn du Lust hast. Das war für mich einer der Gründe mit diesem kranken Java-Wahnsinn aufzuhören. Du hast da überall nur so'n try und catch und throw etc. rumfliegen, daß nicht viel wirkliche Programmleistung übrigbleibt.

      Ich versuche einen Konsequenten Weg zu finden, denn man mit Argumenten untermauern kann. Deswegen wiederstrebt es mir ein "Lust und Laune" Programm zu entwickeln. Das mit den vielen Try und Catches kam mir von Anfang an Suspekt vor...

      Gruß
      optimistischer Pesimist
      T-Rex

      1. Das mit den vielen Try und Catches kam mir von Anfang an Suspekt vor...

        ja, ist es auch. Und die Frage: was kommt am Ende dabei raus? Eine Fehlermeldung a lá: "schwerer Ausnahmefehler" oder sowas? Tolle wurst. Sowas erlebe ich täglich in der Suchfunktion der hiesigen Stadtbücherei. Für den Schrott haben die Zigtausende bezahlt. Meine Suchfunktion, die ich mal für ein Forum programmiert habe, hat nie Fehler produziert - ein Hobbyprodukt!

  4. hi T-Rex,

    Gibt es vielleicht irgendeine Regel wann man Exceptions werfen sollte?

    Was Perl-DBI betrifft ja: "Konfiguriere den DB Handler so, dass _jeder_ Fehler eine Exception wirft". Das wird so empfohlen und ich fahre ganz gut damit. Why RaiseError

    Hotti

  5. Sowas passiert dann wenn Exceptions fehlen:
    http://www.youtube.com/watch?v=U83AH-h3L9s&feature=player_embedded

    Gruß
    PES Fan
    T-Rex

  6. Servus,

    eine Exception sollte ausgelöst werden, wenn deine "API" keine andere Möglichkeit der Rückgabe und Auswertung von Fehlerfällen vorsieht.