Ankündigung

Einklappen
Keine Ankündigung bisher.

[Erledigt] Eigene Exceptions

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • [Erledigt] Eigene Exceptions

    Wenn ich mich manchmal durch manch fremden Code wühle, fällt mir immer wieder auf, dass die Coder eigene Exceptions erstellen, wie z.B.
    PHP-Code:
    <?php

    class MyException extends Exception{}

    ?>
    und ohne Signatur, Attribute, Methoden, ...
    Den Sinn jedoch habe ich davon noch nicht verstanden. Wäre jemand vllt so freundlich mir das näherzubringen?


  • #2
    Abgesehen davon, dass MyException ein dummer Name ist, hat das durchaus einen Zweck.

    Damit lassen sich Fehler kategorisieren und entsprechend darauf reagieren.
    GitHub.com - ChrisAndChris - RowMapper und QueryBuilder für MySQL-Datenbanken

    Kommentar


    • #3
      Um dir den Sinn (wie ChristianK) Ihn erleutert hat nochmal etwas zu verdeutlichen, könnte es in folgendem Beispiel sinnvoll sein seine "eigenen" Exceptions zu erstellen:

      Man hat eine Webanwendung in der es Fehler gibt die z.B. das System zum stoppen bringen sollen weil es keinen Sinn macht, das es durch Fehler X weiterläuft - diese nennt man z.B.
      PHP-Code:
      class SystemException extends Exception {} 
      Dann hat man ggf. noch Szenarien wo man den User zwar hinweisen möchte das ein Fehler aufgetreten ist und macht folgendes:
      PHP-Code:
      class SystemWarningException extends Exception {} 
      Durch die Ableitung von der Klasse Exception enthält die Klasse schon alle notwendigen Methoden - im Zweifel könnte man hier aber nun unterschiedliches Handling dieser Exceptions einbauen, falls man mit der Standard-Art und Weiße nicht glücklich ist bzw. etwas anderes braucht.
      Gruß,
      SebTM

      Kommentar


      • #4
        Ja, das ist die vertikale Variante, die nach Schweregrad kategorisiert.

        Es ist aber häufig auch so, dass Exceptions horizontal nach einzelnen Komponenten des Codes kategorisiert werden. Bei eingebundenen Fremdkomponenten und auf Wiederverwendbarkeit ausgelegten eigenen Komponenten gehört das sozusagen zum guten Stil.

        Das dient zum Beispiel dem Zweck, problemlos erkennen zu können, aus welchem Teil der Anwendung die Exception kommt. Das ist für den Entwickler beim Debugging hilfreich und es ermöglicht auch, im Code auf einen Fehler aus Komponente A anders reagieren zu können als auf einen Fehler aus Komponente B.

        PHP-Code:
        try {
            
        // ...
        } catch (FooException $e) {
            
        // Fehler in Komponente Foo
        } catch (BarException $e) {
            
        // Fehler in Komponente Bar

        Wären das alles nur Exception-Instanzen, könnte man diese Unterscheidung nicht so wirklich treffen.

        Die Unterscheidung ist aber mitunter wichtig, weil manche Fehler eben von übergeordneten Codeteilen behandelbar sind, ohne gleich zum Absturz oder anderweitigen Beenden der Anwendung führen zu müssen.

        Kommentar


        • #5
          Um abgeleitete Exception Klassen zu nutzen müssen erstmal die Exceptions geworfen werden. Hier liegt das eigentliche Problem, dies auch richtig und an einer geeigneten Stelle zu tun.

          Es bringt als Beispiel nichts, wenn der ursächliche Fehler aus der Tiefe des Systems kommt und ich den Fehler fange und einen neuen Fehler mit meiner abgeleiteten Klasse werfe, ohne diesen sinnvoll zu behandeln:

          PHP-Code:
              try{
                
          $r $this->
                   :
              } catch(
          Exception $e) {
                 throw new 
          BarException('Fehler Methode xy');
              } 
          Ich erhalte hier zwar eine Meldung wie
          Fatal error: Uncaught exception 'BarException' with message 'Fehler Methode xy..,
          kann aber den Fehler nicht weiter zurückverfolgen. Ohne den Fehler zu fangen hätte ich mehr Informationen zur Verfügung.


          Zitat von mermshaus Beitrag anzeigen
          Wären das alles nur Exception-Instanzen, könnte man diese Unterscheidung nicht so wirklich treffen.
          Man muß keine abgeleitete Exception-Klasse benutzen, um beim werfen einer normalen Exception diese unterscheiden zu können. Dazu kann beim Erzeugen ein zweiter Parameter (Code-Nr) mitgegeben werden:
          PHP-Code:
          throw new Exception('Fehler class test',176); 
          Dieser Code kann beim Fangen mit
          PHP-Code:
          $code $e->getCode(); 
          geholt und ausgewertet werden. Nachteilig ist, es wird nur Code vom Typ long akzeptiert und Zahlen sind weniger aussagekräftig als Namen.

          Oft wird an Stellen wie oben die Exception einfach mit
          PHP-Code:
          throw $e
          weitergeworfen, wenn keine sinnvolle Behandlung erfolgen kann.

          Man kann jedoch eine eigene Exception werfen und gleichzeitig weiterwerfen:
          PHP-Code:
          throw new Exception('Fehler class test',176,$e); 
          Damit setze ich gewissermaßen eine neue Exceptionstufe. Vorteil ist, ich habe den gesamten Stack trace verfügbar, kann also den Fehler bis zum Ursprung zurückverfolgen.

          LG jspit
          PHP-Klassen auf github

          Kommentar


          • #6
            Anzumerken sei auch das man Exceptions nicht nur feiner granulieren kann indem man die Exception-Klasse extended. Du kannst auch Interfaces bei dieser Extension setzen und im catch()-Block auf diese Verweisen.
            [URL="https://gitter.im/php-de/chat?utm_source=share-link&utm_medium=link&utm_campaign=share-link"]PHP.de Gitter.im Chat[/URL] - [URL="https://raindrop.io/user/32178"]Meine öffentlichen Bookmarks[/URL] ← Ich habe dir geholfen ? [B][URL="https://www.amazon.de/gp/wishlist/348FHGUZWTNL0"]Beschenk mich[/URL][/B].

            Kommentar


            • #7
              Perfekt. Mein Dank gebührt allen.

              Kommentar


              • #8
                Zitat von jspit Beitrag anzeigen
                Um abgeleitete Exception Klassen zu nutzen müssen erstmal die Exceptions geworfen werden. Hier liegt das eigentliche Problem, dies auch richtig und an einer geeigneten Stelle zu tun.
                Ja, das ist ein guter Punkt. Dazu gibt es hier ja auch längere Diskussionen:

                - http://www.php.de/software-design/99...ueckgeben.html

                Es bringt als Beispiel nichts, wenn der ursächliche Fehler aus der Tiefe des Systems kommt und ich den Fehler fange und einen neuen Fehler mit meiner abgeleiteten Klasse werfe, ohne diesen sinnvoll zu behandeln:

                PHP-Code:
                    try{
                      
                $r $this->
                         :
                    } catch(
                Exception $e) {
                       throw new 
                BarException('Fehler Methode xy');
                    } 
                Ich erhalte hier zwar eine Meldung wie
                Fatal error: Uncaught exception 'BarException' with message 'Fehler Methode xy..,
                kann aber den Fehler nicht weiter zurückverfolgen. Ohne den Fehler zu fangen hätte ich mehr Informationen zur Verfügung.
                Dass da im try-Teil Code fehlt, ist ein Versehen, oder? Jedenfalls kann (und vermutlich: sollte) man so was machen:

                PHP-Code:
                class ComponentInitException extends Exception {
                    
                // public constructors etc as in Exception
                }

                class 
                Component {
                    public function 
                __construct() {
                        try {
                            
                $connect = new CONNECT($db$user$password$driver$host);
                        }
                        catch (
                Exception $e) {
                            throw new 
                ComponentInitException($e->getMessage$e->getCode$e);
                        }
                    }

                Exception chaining/wrapping. Via: http://stackoverflow.com/questions/5...ing-exceptions (Bin mir gerade nicht sicher, dass der Demo-Code syntaktisch korrekt ist, aber Idee sollte klar sein.)

                - http://en.wikipedia.org/wiki/Exception_chaining

                Das zeigst du weiter unten im Beitrag aber selbst auch noch.

                Edit: Wobei man das (allgemein jetzt, nicht auf deinen Post bezogen) schon noch mal einschränken sollte. Es ist nicht unbedingt notwendig oder sinnvoll, jede Exception auf diese Weise zu wrappen/chainen. Es ist auch durchaus legitim, zu sagen: „Hier ist das und das passiert (ComponentException). Was noch an Sub-Exceptions zu diesem Resultat geführt hat, braucht den aufrufenden Code nicht zu interessieren.“ Das sagt dann mehr oder weniger, dass die Komponente weiß, wie mit dem internen Fehler umzugehen ist.

                Man muß keine abgeleitete Exception-Klasse benutzen, um beim werfen einer normalen Exception diese unterscheiden zu können. Dazu kann beim Erzeugen ein zweiter Parameter (Code-Nr) mitgegeben werden:
                PHP-Code:
                throw new Exception('Fehler class test',176); 
                […]

                Nachteilig ist, es wird nur Code vom Typ long akzeptiert und Zahlen sind weniger aussagekräftig als Namen.
                Das konzeptionelle Problem mit den Zahlen ist, dass du sämtlichen Code kontrollieren musst, um zu verhindern, dass Zahlen doppelt genutzt werden. Das ist gerade beim Einsatz von Fremdkomponenten nicht (so richtig) praktikabel.

                Kommentar


                • #9
                  Es ist richtig, wenn ich eine Exception fange und in der Lage bin die Situation zu bereinigen, dann kann ich mir ein weiterwerfen sparen. Beim Lesen der letzten Beiträge ist mir noch eine denkbare Situation in den Kopf gekommen:
                  Beim Aufruf einer Methode mit einem Parameter tritt ein Fehler auf und es wird eine Exception geworfen. Ich fange die Exception und kann den Fehler im catch-Zweig an dieser Stelle problemlos behandeln. Angedeutet so:
                  PHP-Code:
                  <?PHP
                  class foo {

                    public function 
                  getStartDate($p){
                      try{
                        
                  $r $this->Val($p);
                      } catch(
                  Exception $e) {
                         
                  //Fehler behandeln
                         
                  $r = new DateTime('Now');
                      }
                      return 
                  $r;
                    }
                    
                    private function 
                  Val($p){
                      
                  $r = new DateTime($p);  
                      return 
                  $r;
                    }
                  }

                  $t = new foo;

                  $r $t->getStartDate("Mo 1.12");
                  var_dump($r);
                  Mein Skript liefert nun zu den Parameter die aktuelle Zeit ohne eine Notiz oder Warnung. Dies kann als Warnung ausgegeben werden, und als Information nutzen wir genau das, was auch bei einer Exception erscheinen würde:
                  PHP-Code:
                  <?PHP
                   
                  class foo {

                    public function 
                  getStartDate($p){
                      try{
                        
                  $r $this->Val($p);
                      } catch(
                  Exception $e) {
                         
                  //Fehler behandeln
                         
                  $r = new DateTime('Now');
                         
                  //warnung mit max.Information werfen
                         
                  trigger_error((string)$eE_USER_WARNING);

                      }
                      return 
                  $r;
                    }
                    
                    private function 
                  Val($p){
                      
                  $r = new DateTime($p);  
                      return 
                  $r;
                    }
                  }

                  $t = new foo;

                  $r $t->getStartDate("Mo 1.12");
                  var_dump($r);
                  Der Ausdruck (string)$e bedient sich der __toString() Methode der Exception Klasse. Die Warnung enthält alle wichtigen Informationen und sieht dann so aus (Beispiel aus PHPTester):
                  Code:
                  WARNING exception 'Exception' with message 'DateTime::__construct() [datetime.--construct]: Failed to parse time string (Mo 1.12) at position 0 (M): The timezone could not be found in the database' in /home/phptest/public_html/code.php53(5) : eval()'d code:18 Stack trace: #0 /home/phptest/public_html/code.php53(5) : eval()'d code(18): DateTime->__construct('Mo 1.12') #1 /home/phptest/public_html/code.php53(5) : eval()'d code(6): foo->Val('Mo 1.12') #2 /home/phptest/public_html/code.php53(5) : eval()'d code(25): foo->getStartDate('Mo 1.12') #3 /home/phptest/public_html/code.php53(5): eval() #4 {main} on line number 11
                  LG jspit
                  PHP-Klassen auf github

                  Kommentar


                  • #10
                    Oder alternativ die Exception werfen und dem aufrufenden Code die Entscheidung überlassen, was zu tun ist, wenn ein String übergeben wird, der nicht sinnvoll in ein Datum umgewandelt werden kann.

                    Statt trigger_error könnte man auch eine (psr3-kompatible) Logger-Klasse verwenden.

                    - http://www.php-fig.org/psr/psr-3/
                    - https://github.com/Seldaek/monolog
                    - https://github.com/katzgrau/KLogger

                    Wie meist: Es gibt viele Wege, irgendwas zu machen.

                    Kommentar

                    Lädt...
                    X