Static Variables in PHP 7 Internals

In PHP, regular local variables are allocated on the zend_execute_data structure. Each time a zend_op_array is executed, a new zend_execute_data instance is created. Local variables are allocated at the start of execution and freed when execution ends—this defines thier lifetime.

Static variables are an exception: they persist beyond function execution. When control exits a function, static variables retain their values and remain available during subsequent calls.

A static variable is declared using the static keyword:

function counter() {
    static $value = 10;
    $value++;
    echo $value, "\n";
}

counter(); // outputs 11
counter(); // outputs 12

Storage Mechanism

Unlike regular locals, static variables are not stored in zend_execute_data. Instead, they reside in a hash table attached to the zend_op_array as static_variables. This ensures persistence across multiple invocations of the same function.

Initialization occurs once, during the compile phase, not at runtime. In the example above, $value = 10 is evaluated and inserted into static_variables when the script is compiled. Subsequent calls do not reinitialize the value, which explains the incrementing output.

Because initialization happens at compile time, the initial value must be a constant expression. Assigning a non-constant, such as another variable (static $x = $y;), results in a compile error.

Access and Compilation

During compilation, a static declraation is parsed into a ZEND_AST_STATIC AST node. The compiler processes this via zend_compile_static_var(), which evaluates any initializer into a zval and delegates to zend_compile_static_var_common().

This function ensures the static_variables hash table exists in the current zend_op_array, then inserts the variable using its name as the key:

if (!CG(active_op_array)->static_variables) {
    ALLOC_HASHTABLE(CG(active_op_array)->static_variables);
    zend_hash_init(CG(active_op_array)->static_variables, 8, NULL, ZVAL_PTR_DTOR, 0);
}
zend_hash_update(
    CG(active_op_array)->static_variables,
    Z_STR(var_node.u.constant),
    value
);

After insertion, two opcodes are emitted:

  1. ZEND_FETCH_W with extended_value = ZEND_FETCH_STATIC: This creates an IS_INDIRECT zval pointing to the entry in static_variables.
  2. ZEND_ASSIGN_REF: This binds the local variable (e.g., $value) as a reference to that indirect zval.

Thus, inside the function, $value is not the static variable itself—it is a local symbol that references the persistent entry in static_variables. Conceptually, the statement static $value = 10; translates at runtime to:

$value =& $op_array->static_variables['value'];

This indirection enables the familiar syntax while maintaining separation between transient local storage and persistent static state.

Tags: PHP PHP Internals Static Variables Zend Engine compilation

Posted on Thu, 01 Oct 2026 17:03:29 +0000 by ianr