The shutdown process in PHP consists of the following steps performed in the given order:
- Calling all functions registered using
register_shutdown_function()
- Calling all
__destruct()
methods - Emptying all output buffers
- Terminating all PHP extensions (e.g., sessions)
- Shutting down the output layer (sending HTTP headers, cleaning output handlers, etc.)
Let's focus more closely on step 2, the calling of destructors. It's important to note that even in the first step, when registered shutdown functions are called, object destruction can occur. For example, if one of the functions held the last reference to an object or if the shutdown function itself was an object.
Destructor calls proceed as follows:
- PHP first attempts to destroy objects in the global symbol table.
- Then it calls the destructors of all remaining objects.
- If execution is halted, e.g., due to
exit()
, the remaining destructors are not called.
ad 1) PHP iterates through the global symbol table in reverse order, starting with the most recently created variable and proceeding to the first created variable. During this iteration, it destroys all objects with a reference count of 1. This iteration continues as long as such objects exist.
Basically, it does the following: a) removes all unused objects in the global symbol table, b) if new unused objects appear, removes them as well, and c) continues this process. This method of destruction is used so that objects can depend on other objects in their destructor. This usually works well if objects in the global scope don't have complicated (e.g., circular) mutual dependencies.
Destruction of the global symbol table is significantly different from the destruction of other symbol tables. For the global symbol table, PHP uses a smarter algorithm that tries to respect object dependencies.
ad 2) Other objects are processed in the order they were created, and their
destructors are called. Yes, PHP merely calls __destruct
, but it
doesn't actually destroy the object (nor does it even change its reference
count). If other objects still refer to it, the object will remain available
(even though its destructor has already been called). In a sense, they will be
using a “half-destroyed” object.
ad 3) If execution is halted during the calling of destructors, e.g., due to
exit()
, the remaining destructors are not called. Instead, PHP
marks the objects as already destroyed. The important consequence is that
destructor calls are not guaranteed. While such cases are relatively rare, they
can happen.
Leave a comment