phpphp-internalsapcubitweaver

Invoking a PHP function after all objects have been destroyed


I have seen several answers on object destruction order, and all point out that order is not guaranteed. Since I cannot control the order, I would like to invoke a function after all objects have been destroyed.

register_shutdown_function is invoked prior to object destruction, thus not an option. I have looked at tricks like set_error_handler using the object so it is invoked "late", but that is not sufficient.

Some background on the problem, this is a complex CMS with dozens of separate files for routes (view) layer. There is a common boot include, but not a common one run at shutdown. I am using APCu object caching through a common inherited base class, and need to make sure an object is purged. It is possible that for any two instances of the same object created during a page load, one might want to purge itself, and the other might want to cache itself. Obviously purge trumps all else, so I need to call apc_delete on a global set of cache keys to purge one all __destruct()'ion is complete.


Solution

  • As I said in my comment above, the mixed state of multiple object instances sounds like a design flaw. I think you always want all instances of an object to be the latest, or at least not touch the cache if they aren't. I would think your caching and/or object loading layer could handle this.


    To answer your underlying question, I wrote a script to test PHP's shutdown sequence:

    <?php  /* Adapted from https://evertpot.com/160/ */
    
        ob_start(
            function($buffer) {
                return $buffer . "output buffer flushed\n";
            }
        );
    
        $empty = function() {
            return true;
        };
    
        $close = function() {
            echo "session close\n";
            return true;
        };
    
        $write = function() {
            echo "session write\n";
            return true;
        };
    
        session_set_save_handler($empty, $close, $empty, $write, $empty, $empty);
        session_start();
    
        register_shutdown_function(
            function() {
                echo "register_shutdown_function\n";
            }
        );
    
        class MyClass {
            function __destruct() {
               echo "object destructor\n";
            }
        }
        $myObject = new MyClass;
    

    The output:

    register_shutdown_function
    object destructor
    output buffer flushed
    session write
    session close
    

    It seems the flush of output buffering and session write/close are 2 places where you know all of your objects have been destroyed. (This also assumes you're not using the newer form of session_set_save_handler that can register itself as a shutdown function.)