• Exceptions im Destruktor

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

    Kater von gestern überstanden? Ja? Dann kann es ja weiter gehen … Man lernt nie aus. Diese These hatte ich ja glaube ich schon mal aufgestellt, aber heute wurde sie mal wieder bestätigt. Ich kam heute endlich mal wieder zum Programmieren und wie das nun mal ist, passieren da die komischsten Dinge. Heute hat mein Programm auf einmal gestreikt und ich hatte keine Ahnung warum. Viel geändert seit dem letzen F5 hatte ich nämlich nicht.

    Die Fehlermeldung, die kam konnte ich auch nicht so recht interpretieren. Fatal error: Exception thrown without a stack frame in Unknown on line 0. Nachdem ich weiß, warum dieser Fehler auftrat, weiß ich auch, warum ich sie nicht interpretieren konnte. Sie macht keinen Sinn! Seid mal ehrlich, was würdet ihr denken, wenn auch euer Programm so um die Ohren fliegt? Ich hoffe mal ein großes Häääää?!? Zumindest ging es mir so. Was macht man in so einem Fall? Google fragen. Hat nicht lange gedauert, da wusste ich die Lösung.

    PHP gibt diesen Laut von sich, falls man versucht eine Exception im Destruktor zu werfen. Ich war mir zu dem Zeitpunkt noch gar nicht wirklich bewusst, dass ich das mache, aber eine von den Methoden, die ich verwendet habe, hat dies leider getan und ich habe sie nicht rechtzeitig gefangen. Die Fehlermeldung ist ein klares WTF. Dass PHP so eine Exception nicht zulässt ist vielleicht gar nicht so verkehrt. Hier aber erstmal schnell ein Beispiel, damit ihr es zuhause auch probieren könnt.

      class MyClass {
        function __destruct(){
          throw new Exception('fail');
        }
      }
      $n = new MyClass();
    

    Aber warum zum Teufel sollte ich keine Exceptions werfen können? Meine Theorie ist, es macht keinen Sinn. Der Destruktor wird vom Garbage Collector aufgerufen. Wann dies passiert, ist allein PHP überlassen (zumindest in den meisten Fällen). Ich kann zwar ein unset auf das Objekt machen, aber ob es dann in dem Augenblick auch vom GC in den Müll geworfen wird ist fraglich. Wie soll ich also diese Exception fangen, wenn ich nicht weiß, wo sie geworfen wird. Ein Handling dieser Ausnahme ist also nicht möglich. Ich muss dazusagen, dass ich mir nicht sicher bin, ob ich die PHP Internals genau genug kenne, um diese Theorie zu halten, aber vielleicht kennt ja jemand von euch die wahren Gründe. Könnte mir auch vorstellen, dass PHP da einfach beim Ausführen abschmiert und deswegen diese fiese Fehlermeldung kommt, da es aber nicht verkehrt ist, dass man mit dem Programm aufhört, hat man es gelassen.

    Ich bin mir auch gar nicht so sicher, ob PHP 5.3 mit dem gleichen Fehler rausknallt. Irgendwo habe ich was gelesen, dass sich da was verändert hat.

    Nils Langner

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

    Zum Profil von Nils Langner

    25 Kommentare »


    • Martin
      am 4. August 2009 um 08:46 Uhr

      Wirklich interessante Frage.
      Meine Argumenten sind eher allgemeiner Natur und lassen sich gut in folgendem Zitat aus dem C-Umfeld zusammenfassen:
      “Beware of throwing exceptions from a destructor
      The most plausible way to report a failure during object construction is by throwing an exception. However, this is not recommended for destructors. The problem is that a destructor may be invoked automatically when an uncaught exception is thrown in its scope. If the called destructor invoked due to an exception also throws an exception, the result is an infinite recursion.”
      (http://www.devx.com/tips/Tip/12464)

      “Infinite recursion” klingt jedenfalls suboptimal. Ob das bei php genauso ist, werde ich ja sicher gleich erfahren ;-)


    • Christian Kehres
      am 4. August 2009 um 08:46 Uhr

      “von Nils Langner am 4. August 2009 ”

      “Ich kam heute endlich mal wieder zum Programmieren”

      Wenn ich fragen darf, um wieviel Uhr begann der Porgrammiertag? :)
      Also mich würds morgends so früh nerven ne on Line 0 Meldung zu bekommen :)


    • Christian Kehres
      am 4. August 2009 um 08:48 Uhr

      okay, Frage schon beantwortet
      XING Nachricht => Datum: 04.08.2009, 06:55
      er beginnt früh :)


    • Daniel Gratzl
      am 4. August 2009 um 09:07 Uhr

      Gerade getestet auf PHP 5.3 dem Final Preview Package für Debian Lenny von Dotdeb.org.

      “Fatal error: Exception thrown without a stack frame in Unknown on line 0″

      Scheint sich dahingehend also nichts geändert zu haben.


    • Nils Langner
      am 4. August 2009 um 09:41 Uhr

      @Christian: Da muss ich dich leider enttäuschen. Den Artikel hatte ich vorgeschrieben :) Wenn ich jeden morgen um 6 Uhr anfangen würde zu programmieren, würde mich meine Freundin (verständlicherweise) töten.


    • Nils Langner
      am 4. August 2009 um 09:45 Uhr

      Update:
      Der Destruktor wird wohl beim unset( ) direkt aufgerufen. Also nicht vom GC oder der GC wird sofort drübergejagt.

      Destructors are functions that are called automatically when an object is destroyed, either with unset() or by simply going out of scope.

      class A {
        public function __destruct() {
          throw new Exception('Destruction!');
        }
      }
      
      function test() {
        new A();
      }
      
      try {
        test();
      }
      catch(Exception $ex) {
        echo 'Caught '.$ex->getMessage();
      }

    • Martin Kuckert
      am 4. August 2009 um 09:45 Uhr

      Um das Gerücht, Exceptions im Destruktor seien gar nicht möglich mal zu widerlegen verweise ich einfach mal dreist auf meinen Beitrag in den von Nils eröffneten Forenthread bei phpforum.de. ^^
      http://phpforum.de/forum/showthread.php?t=243078#post1331159

      Diese Fehlermeldung tritt nur auf, wenn die Ausnahme nicht gefangen wird, was zum Beispiel beim Aufräumen nach Skriptausführung der Fall sein kann.


    • Martin Kuckert
      am 4. August 2009 um 09:46 Uhr

      @Update: Oder Nils zitiert das Beispiel, auch ok ;)


    • Nils Langner
      am 4. August 2009 um 09:46 Uhr

      @Martin: Da waren wir fast Zeitgleich :) Hatte das Update auch gerade nachgeschoben mit dem Beispiel aus dem Forum.


    • Nils Langner
      am 4. August 2009 um 09:47 Uhr

      Und schon wieder Zeitgleich ;)


    • LudwigR
      am 4. August 2009 um 10:07 Uhr

      Das hatte ich vor ein paar wochen auch, und sogar länger recherchiert warum das so ist. hab auch ne relative gute und (zur damaligen zeit) einleuchtende erklärung gefunden. aber typisch, ich find den bookmark dazu wieder nicht *grmbl*

      AFAIR war es so, das werfen der Exception ist nicht das problem, aber das fangen (es ist möglich einen try/catch im destruct zu haben), da ja der GC ausserhalb deines Scopes läuft. (jemand mit mehr php internal erfahrung kann das sicher besser erklären)

      (aber nils, das hat dir doch phpDesaster eh im phpforum erklärt :-)
      http://www.phpforum.de/forum/showthread.php?t=243078

      my2c
      ludwig


    • LudwigR
      am 4. August 2009 um 10:09 Uhr

      …. grr ich sollte echt die page nochmal laden nachdem ich 30minuten mit kunde telefoniert hab. Sorry für unnötigen post :(


    • Ralf
      am 4. August 2009 um 10:28 Uhr

      Hmm, Nils, hattest Du die Meldung nicht schon vor ein paar Tagen? Ich kann mich daran erinnern, das was dazu durch Twitter gerauscht ist. :-)


    • Nils Langner
      am 4. August 2009 um 10:43 Uhr

      @Ralf: Glaubst du wirklich, dass die Artikel immer an den Tagen geschrieben werden an denen sie live gehen? Oft schreibe ich an ‘nem Samstag 2 oder 3, dann hab ich die Woche über weniger Stress. Aber ich hab gehört, dass man mit mehr Co-Autoren da was dran ändern könnte :) Wobei die jetzigen Co-Autoren echt gut sind!


    • Phil
      am 4. August 2009 um 11:26 Uhr

      Die selbe Fehlermeldung tritt übrigens auch auf, wenn im Exception Handler eine Exception geworfen wird.

      function exceptionHandler(Exception $e) {
          throw $e;
      }
      set_exception_handler('exceptionHandler');
      
      // Fatal error: Exception thrown without a stack frame in Unknown on line 0
      throw new Exception('Test');

    • Kevin
      am 4. August 2009 um 11:32 Uhr

      Zitat:
      “Aber warum zum Teufel sollte ich keine Exceptions werden können?”

      Hehehe, ja warum nur? Frag Deine Freundin, die hat Dich ja schon gefangen ;)


    • Nils Langner
      am 4. August 2009 um 11:39 Uhr

      @Kevin: Da musste ich erstmal grübeln was du meinst :) Hab den Fehler behoben ;)


    • Christian Wald
      am 4. August 2009 um 12:00 Uhr

      @Nils: Was hälst du von einem kleinen “Print”-Symbol für eine Druckansicht über jedem Artikel? Ich entdecke in meinem Urlaub gerade den Charme von ausgedruckten phphatesme-Artikeln :-)


    • Kevin
      am 4. August 2009 um 12:18 Uhr

      @Nils: Zugegeben, war ein bescheidenes Wortspiel … aber manchmal springt einem so ein Unsinn einfach ins Auge bzw. in den Kopf ;)

      @Christian+Nils: Ein einfaches Print Stylesheet würde ja schon reichen, wo z.B. das Menü auf display:none gesetzt wird.


    • Nils Langner
      am 4. August 2009 um 12:36 Uhr

      @Kevin + Christian: Da sprecht ihr meiner Freundin aus der Seele :) Ihre Idee war aber ein schönes PDF und ich muss ehrlich sagen, dass mir das auch besser gefällt :) Vielleicht kennt jemand von euch da ja ein schönes WordPress Plugin.


    • oliver
      am 4. August 2009 um 14:40 Uhr

      damit ist ja auch schon wieder ein thema aus der ideenschmiede geklärt ;)


    • Christian Wald
      am 4. August 2009 um 22:22 Uhr

      @Nils + Kevin: Das mit dem PDF hat wirklich mehr Charme als die einfache Lösung… Wenn mir ein Plugin über den Weg läuft oder ich eines Nachts eins geschrieben habe, komme ich darauf zurück…

      Vielleicht auch ein schöner, zukünftiger Artikel….


    • Marek Luthardt
      am 3. August 2010 um 11:11 Uhr

      Die gleiche Fehlermeldung hatte ich neulich auch. Bin durch Googlen auf die Stelle gekommen. Ich habe mir eine eigene Klasse rund um PDO gestrickt und diese für einen einfacheren Zugriff in die Session legen wollen. Und immer beim Zuweisen des Objektes an $_SESSION['db'] kam diese Exception. Der Tipp war, dass Klassen, die in die Session gelegt werden sollen, eine Funktion __sleep() definieren müssen. Hat bei mir aber auch nicht wirklich geholfen, ich habe dann auf die Klassenhaltung in der Session verzichtet. Aber auch bei mir war diese Meldung ein klassischer WTF-Moment …


    • Marek Luthardt
      am 3. August 2010 um 11:13 Uhr

      Achja, hier noch der Beitrag, der mir die Augen geöffnet hat: http://e-mats.org/2008/07/fatal-error-exception-thrown-without-a-stack-frame-in-unknown-on-line-0/


    • Nils W.
      am 28. August 2011 um 13:49 Uhr

      das Problem tritt meines Wissens nur auf, wenn noch eine weitere Referenz auf das betreffende Objekt besteht, wie z.B. in self::$instance. Ein unset() im globalen Kontext löscht dann nur die Referenz, da der Destruktor ja erst dann aufgerufen wird, wenn die letzte Referenz auf das Objekt gelöscht wird, was in diesem Fall dann nach dem Skriptablauf vom GC erfolgt. Ist im Destruktor dann ein Aufruf an ein referenziertes Objekt enthalten (z.B. speichern via Db-Objekt) und wurde dieses Objekt bereits entsorgt, gibt es dann diesen Fehler.
      Grundsätzlich kann man diese Ausgabe unterdrücken, in dem man im Destruktor Exceptions abfängt und mit einem leeren catch-Block einfach ignoriert, allerdings kann man sich den Code im Destruktor dann auch gleich sparen, wenn man sich nicht darauf verlassen kann, dass er auch wirklich ausgeführt wird.
      Als Ergebnis könnte man festhalten, dass diese Meldung in der Regel einen Designfehler offenbart. Um diesen zu vermeiden, sollte man entweder im Destruktor keinen Objektkontext verwenden oder die Instanzen (inklusive aller Referenzen darauf) aktiv löschen.

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

    Hinterlasse einen Kommentar

    Werbung
    PHP Magazin
    Ausgabe 02/2010

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

    t3n
    Ausgabe 19

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

    PHP Journal
    Ausgabe 2/2010

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

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