Shutdown order

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Thomas Mlynarczyk

    Shutdown order

    Hi,

    I have this code:

    class Test
    {
    public $status = 'dead';
    function __construct() { $this->status = 'alive'; }
    function __destruct() { echo '<br>__destruct ()'; }
    }
    $o = new Test;

    function shutdown()
    {
    echo '<br>shutdown() ';
    }
    register_shutdo wn_function('sh utdown');

    function obflush( $s )
    {
    global $o;
    return $s . '<br>obflush() ' . $o->status;
    }
    ob_start('obflu sh');

    Which (using PHP 5.1.4) produces this output:

    shutdown()
    __destruct()
    obflush() alive

    I have two questions:

    1) I have read that the order in which the three functions are called has
    changed previously and is likely to change again in future versions of PHP.
    Does this mean I cannot rely on this order at all?

    2) Why is $o still "alive" in obflush() even though its destructor has been
    called before? The destructor having been called, I would expect global $o
    to point to a no longer existing variable (thus, "null").

    Greetings,
    Thomas


  • Jerry Stuckle

    #2
    Re: Shutdown order

    Thomas Mlynarczyk wrote:
    Hi,
    >
    I have this code:
    >
    class Test
    {
    public $status = 'dead';
    function __construct() { $this->status = 'alive'; }
    function __destruct() { echo '<br>__destruct ()'; }
    }
    $o = new Test;
    >
    function shutdown()
    {
    echo '<br>shutdown() ';
    }
    register_shutdo wn_function('sh utdown');
    >
    function obflush( $s )
    {
    global $o;
    return $s . '<br>obflush() ' . $o->status;
    }
    ob_start('obflu sh');
    >
    Which (using PHP 5.1.4) produces this output:
    >
    shutdown()
    __destruct()
    obflush() alive
    >
    I have two questions:
    >
    1) I have read that the order in which the three functions are called has
    changed previously and is likely to change again in future versions of PHP.
    Does this mean I cannot rely on this order at all?
    >
    2) Why is $o still "alive" in obflush() even though its destructor has been
    called before? The destructor having been called, I would expect global $o
    to point to a no longer existing variable (thus, "null").
    >
    Greetings,
    Thomas
    >
    >
    It doesn't make any difference what order the functions are in - they
    are only called when your code calls them directly or indirectly (i.e. a
    constructor). So whatever order you call them in is the way they will
    be called.

    As for the variable - the object is destroyed, the destructor is called.
    But that's not what's happening here. You are calling the destructor,
    so it acts just like any other function.

    Rather, the destructor is called implicitly during script termination or
    when the variable goes out of scope. Like a constructor, you should not
    be calling a destructor.

    --
    =============== ===
    Remove the "x" from my email address
    Jerry Stuckle
    JDS Computer Training Corp.
    jstucklex@attgl obal.net
    =============== ===

    Comment

    • petersprc@gmail.com

      #3
      Re: Shutdown order

      Hi Thomas,

      The order in my version of PHP5.1.6 (main/main.c:php_requ est_shutdown):

      /* 1. Call all possible shutdown functions registered with
      register_shut down_function() */
      ....php_call_sh utdown_function s(TSRMLS_C);...

      /* 2. Call all possible __destruct() functions */
      ....zend_call_d estructors(TSRM LS_C);...

      /* 3. Flush all output buffers */
      ....php_end_ob_ buffers((zend_b ool)(SG(request _info).headers_ only?0:1)
      TSRMLS_CC)...

      [...more steps...]

      Maybe step 2 only invokes the destructor, leaving de-allocation to be
      done later? I don't know for certain. Here's some more comments about
      this:




      Thomas Mlynarczyk wrote:
      Hi,
      >
      I have this code:
      >
      class Test
      {
      public $status = 'dead';
      function __construct() { $this->status = 'alive'; }
      function __destruct() { echo '<br>__destruct ()'; }
      }
      $o = new Test;
      >
      function shutdown()
      {
      echo '<br>shutdown() ';
      }
      register_shutdo wn_function('sh utdown');
      >
      function obflush( $s )
      {
      global $o;
      return $s . '<br>obflush() ' . $o->status;
      }
      ob_start('obflu sh');
      >
      Which (using PHP 5.1.4) produces this output:
      >
      shutdown()
      __destruct()
      obflush() alive
      >
      I have two questions:
      >
      1) I have read that the order in which the three functions are called has
      changed previously and is likely to change again in future versions of PHP.
      Does this mean I cannot rely on this order at all?
      >
      2) Why is $o still "alive" in obflush() even though its destructor has been
      called before? The destructor having been called, I would expect global $o
      to point to a no longer existing variable (thus, "null").
      >
      Greetings,
      Thomas

      Comment

      • Thomas Mlynarczyk

        #4
        Re: Shutdown order

        Also sprach Jerry Stuckle:
        It doesn't make any difference what order the functions are in - they
        are only called when your code calls them directly or indirectly
        (i.e. a constructor). So whatever order you call them in is the way
        they will be called.
        But I am not /calling/ these functions - it is PHP that decides when to call
        them. I am merely registering two functions and instantiating an object.
        As for the variable - the object is destroyed, the destructor is
        called. But that's not what's happening here. You are calling the
        destructor, so it acts just like any other function.
        Where am *I* calling the destructor in my code? I am merely /defining/ the
        function __destruct() and I leave it up to PHP to decide when it is to be
        executed.
        Rather, the destructor is called implicitly during script termination
        or when the variable goes out of scope. Like a constructor, you
        should not be calling a destructor.
        But I am not calling it! Where in my code do I have a line that says
        "$o->__destruct();" ?

        Greetings,
        Thomas


        Comment

        • Thomas Mlynarczyk

          #5
          Re: Shutdown order

          Also sprach petersprc@gmail .com:

          [PHP 5.1.6:
          (1) registered shutdown functions,
          (2) Destructors,
          (3) ob buffers]
          Maybe step 2 only invokes the destructor, leaving de-allocation to be
          done later?
          In this case I should not rely upon the behaviour I observed? But as my
          object is in global scope, it would seem more logical to me to "keep it
          alive" until *after* the last of the three steps (as $GLOBALS should live
          that long), in which case its destructor should not be called before.
          http://www.zend.com/lists/php-dev/200509/msg00064.html
          Thank you for this link. I think, the behaviour described there refers to an
          older version (PHP 5.0.5) in which the destructors were called *before* the
          shutdown functions. So it does not explain why I should be able to access an
          object after its destructor has been called.

          Greetings,
          Thomas


          Comment

          • Jerry Stuckle

            #6
            Re: Shutdown order

            Thomas Mlynarczyk wrote:
            Also sprach Jerry Stuckle:
            >
            >
            >>It doesn't make any difference what order the functions are in - they
            >>are only called when your code calls them directly or indirectly
            >>(i.e. a constructor). So whatever order you call them in is the way
            >>they will be called.
            >
            >
            But I am not /calling/ these functions - it is PHP that decides when to call
            them. I am merely registering two functions and instantiating an object.
            >
            >
            >>As for the variable - the object is destroyed, the destructor is
            >called. But that's not what's happening here. You are calling the
            >>destructor, so it acts just like any other function.
            >
            >
            Where am *I* calling the destructor in my code? I am merely /defining/ the
            function __destruct() and I leave it up to PHP to decide when it is to be
            executed.
            >
            >
            >>Rather, the destructor is called implicitly during script termination
            >>or when the variable goes out of scope. Like a constructor, you
            >>should not be calling a destructor.
            >
            >
            But I am not calling it! Where in my code do I have a line that says
            "$o->__destruct();" ?
            >
            Greetings,
            Thomas
            >
            >
            Oops, I misread your code and your post.

            No, you cannot rely on the order in which things are cleaned up. It is
            always subject to change.

            This is no different than C++, Java or any other OO languages. The
            order in which things are cleaned up is never guaranteed.



            --
            =============== ===
            Remove the "x" from my email address
            Jerry Stuckle
            JDS Computer Training Corp.
            jstucklex@attgl obal.net
            =============== ===

            Comment

            • Thomas Mlynarczyk

              #7
              Re: Shutdown order

              Also sprach Jerry Stuckle:
              This is no different than C++, Java or any other OO languages. The
              order in which things are cleaned up is never guaranteed.
              But at least the order of "calling all shutdown functions", "calling all
              destructors" and "calling ob buffer function" should be well-determined,
              even if the order in which the destructors are called may not be guaranteed.

              And this still doesn't explain why I can access an object *after* its
              destructor has been called. Strange...

              Greetings,
              Thomas


              Comment

              • Jerry Stuckle

                #8
                Re: Shutdown order

                Thomas Mlynarczyk wrote:
                Also sprach Jerry Stuckle:
                >
                >
                >>This is no different than C++, Java or any other OO languages. The
                >>order in which things are cleaned up is never guaranteed.
                >
                >
                But at least the order of "calling all shutdown functions", "calling all
                destructors" and "calling ob buffer function" should be well-determined,
                even if the order in which the destructors are called may not be guaranteed.
                >
                And this still doesn't explain why I can access an object *after* its
                destructor has been called. Strange...
                >
                Greetings,
                Thomas
                >
                >
                Thomas,

                No, those are all undefined.

                And you can still access the object because its reference still points
                to the object. PHP will destroy the object, but will not alter the
                reference to the object. There is no need to - the reference will be
                destroyed soon, anyway.

                Destructors are meant to clean up the object itself - i.e. for a class
                which access a database you could want to close the database connection.
                It's never a good idea to have a destructor depend on the state of
                another object which itself may be destroyed. The same is true when you
                register a shutdown function - that function should not depend on the
                state of any objects.

                If you must do this, you need to change the destructor in your Test
                object to set the state to invalid.

                But that also doesn't mean it might not change again in the future. All
                of the processing order is undefined.

                --
                =============== ===
                Remove the "x" from my email address
                Jerry Stuckle
                JDS Computer Training Corp.
                jstucklex@attgl obal.net
                =============== ===

                Comment

                • Andy Hassall

                  #9
                  Re: Shutdown order

                  On Thu, 16 Nov 2006 21:28:48 +0100, "Thomas Mlynarczyk"
                  <thomas@mlynarc zyk-webdesign.dewro te:
                  >But at least the order of "calling all shutdown functions", "calling all
                  >destructors" and "calling ob buffer function" should be well-determined,
                  It looks, from the mailing list post from the other part of the thread, that
                  they have finally fixed this. It's been broken for a while, and was re-ordered
                  but was still broken, and now the order makes sense so long as you have quite a
                  recent version of PHP.
                  >even if the order in which the destructors are called may not be guaranteed.
                  >And this still doesn't explain why I can access an object *after* its
                  >destructor has been called. Strange...
                  The shutdown function ordering didn't make sense previously, so you were
                  seeing things you shouldn't have seen - objects who had their destructors
                  already called before the rest of the script had actually run yet.

                  Then again I suspect that the order of destruction of objects is still
                  undefined and not actually influenced by any sort of reference graph, so if you
                  reference other objects in destructors you're still in for an interesting time.

                  --
                  Andy Hassall :: andy@andyh.co.u k :: http://www.andyh.co.uk
                  http://www.andyhsoftware.co.uk/space :: disk and FTP usage analysis tool

                  Comment

                  • petersprc

                    #10
                    Re: Shutdown order

                    If the behavior did change in the future, you might be able to just
                    call flush() explicitly at the end of your script, to trigger your
                    output buffer callback before the global objects were finalized.

                    Thomas Mlynarczyk wrote:
                    Also sprach petersprc@gmail .com:
                    >
                    [PHP 5.1.6:
                    (1) registered shutdown functions,
                    (2) Destructors,
                    (3) ob buffers]
                    >
                    Maybe step 2 only invokes the destructor, leaving de-allocation to be
                    done later?
                    >
                    In this case I should not rely upon the behaviour I observed? But as my
                    object is in global scope, it would seem more logical to me to "keep it
                    alive" until *after* the last of the three steps (as $GLOBALS should live
                    that long), in which case its destructor should not be called before.
                    >
                    http://www.zend.com/lists/php-dev/200509/msg00064.html
                    >
                    Thank you for this link. I think, the behaviour described there refers to an
                    older version (PHP 5.0.5) in which the destructors were called *before* the
                    shutdown functions. So it does not explain why I should be able to access an
                    object after its destructor has been called.
                    >
                    Greetings,
                    Thomas

                    Comment

                    • mannerboy

                      #11
                      Re: Shutdown order

                      maybe i guess , once overload the function __destruct() , you should
                      destruct the object explicitly...

                      i havn't written a script to test this .....


                      "Thomas Mlynarczyk дµÀ£º
                      "
                      Hi,
                      >
                      I have this code:
                      >
                      class Test
                      {
                      public $status = 'dead';
                      function __construct() { $this->status = 'alive'; }
                      function __destruct() { echo '<br>__destruct ()'; }
                      }
                      $o = new Test;
                      >
                      function shutdown()
                      {
                      echo '<br>shutdown() ';
                      }
                      register_shutdo wn_function('sh utdown');
                      >
                      function obflush( $s )
                      {
                      global $o;
                      return $s . '<br>obflush() ' . $o->status;
                      }
                      ob_start('obflu sh');
                      >
                      Which (using PHP 5.1.4) produces this output:
                      >
                      shutdown()
                      __destruct()
                      obflush() alive
                      >
                      I have two questions:
                      >
                      1) I have read that the order in which the three functions are called has
                      changed previously and is likely to change again in future versions of PHP.
                      Does this mean I cannot rely on this order at all?
                      >
                      2) Why is $o still "alive" in obflush() even though its destructor has been
                      called before? The destructor having been called, I would expect global $o
                      to point to a no longer existing variable (thus, "null").

                      Greetings,
                      Thomas

                      Comment

                      • Thomas Mlynarczyk

                        #12
                        Re: Shutdown order

                        Also sprach Jerry Stuckle:

                        And you can still access the object because its reference still points
                        to the object. PHP will destroy the object, but will not alter the
                        reference to the object. There is no need to - the reference will be
                        destroyed soon, anyway.
                        So this is a bit like deleting a file on my harddisk - the space it occupied
                        is just marked as available, but as long as it is not physically overwritten
                        the file is still there?
                        Destructors are meant to clean up the object itself - i.e. for a class
                        which access a database you could want to close the database
                        connection. It's never a good idea to have a destructor depend on
                        the state of another object which itself may be destroyed. The same
                        is true when you register a shutdown function - that function should
                        not depend on the state of any objects.
                        In other words: I should always write my scripts to be completely
                        independent of the shutdown order.

                        Greetings,
                        Thomas


                        Comment

                        • Thomas Mlynarczyk

                          #13
                          Re: Shutdown order

                          Also sprach Andy Hassall:
                          It looks, from the mailing list post from the other part of the
                          thread, that they have finally fixed this. It's been broken for a
                          while, and was re-ordered but was still broken, and now the order
                          makes sense so long as you have quite a recent version of PHP.
                          I had read somewhere that there was a discussion about calling the ob
                          function before the shutdown function and destructors. This would make more
                          sense to me, but I don't know if they actually did it in PHP 5.1.6 or 5.2.0.
                          The shutdown function ordering didn't make sense previously, so you
                          were seeing things you shouldn't have seen - objects who had their
                          destructors already called before the rest of the script had actually
                          run yet.
                          Yes, but the ob handler is still called last, i.e. *after* the destructors.
                          When I write my own error handler which "collects" error messages during
                          script execution and writes them to a file when the scripts ends, I must
                          make sure this writing occurs as the last step in my script. There seems to
                          be no elegant solution to this.

                          Greetings,
                          Thomas


                          Comment

                          • Thomas Mlynarczyk

                            #14
                            Re: Shutdown order

                            Also sprach petersprc:
                            If the behavior did change in the future, you might be able to just
                            call flush() explicitly at the end of your script, to trigger your
                            output buffer callback before the global objects were finalized.
                            Indeed, that sounds like a possible solution. Thanks for the suggestion.

                            Greetings,
                            Thomas


                            Comment

                            Working...