Blog / WordPress/ How to Prevent Performance Loss from Multiple Executions of WordPress init Hook

How to Prevent Performance Loss from Multiple Executions of WordPress init Hook

WordPress init 如何防止多次执行造成的性能损失?

Problem Background

In WordPress development, the init hook is a core action hook used to execute code after the WordPress environment is initialized. However, if callback functions from plugins or themes are incorrectly attached to the init hook multiple times, or if the function itself is called repeatedly, it can lead to serious performance issues such as duplicate database queries, redundant resource loading, and even logical errors.

Solution: Using Static Variables or Flags

The most direct and effective method is to use a static variable or class property inside the callback function to track whether it has already executed, thereby preventing duplicate runs.

Method 1: Using a Static Variable in a Regular Function

function my_custom_init_action() {
    static $has_run = false;
    
    if ( $has_run ) {
        return;
    }
    $has_run = true;
    
    // Your actual initialization code goes here
    // e.g., register custom post types, add shortcodes
    // This code will only run once
    register_post_type( 'my_product', array(
        'public' => true,
        'label'  => 'Products'
    ) );
}
add_action( 'init', 'my_custom_init_action' );

How it works: The static variable $has_run is initialized to false on the first function call and set to true after the core code executes. If WordPress's init hook triggers this function multiple times unexpectedly, subsequent calls will return early due to the condition check, protecting the core logic.

Method 2: Using a Property in a Class Method

If your code is organized in an object-oriented (OOP) style, you can use a private class property to achieve the same goal.

class MyPluginInit {
    private $has_run = false;
    
    public function setup() {
        add_action( 'init', array( $this, 'init_action' ) );
    }
    
    public function init_action() {
        if ( $this->has_run ) {
            return;
        }
        $this->has_run = true;
        
        // Your actual initialization code
        // This code will only run once
        flush_rewrite_rules(); // Example: flush rewrite rules
    }
}
$my_plugin = new MyPluginInit();
$my_plugin->setup();

Checking Hook Attachment Count

Sometimes the issue isn't inside the function but because the add_action statement is executed multiple times, attaching the same callback to the hook repeatedly. You can debug this with:

// Check how many times a callback is attached to the 'init' hook
$hook_count = has_action( 'init', 'my_custom_init_action' );
if ( $hook_count ) {
    error_log( 'my_custom_init_action attached to init hook count: ' . $hook_count );
}

If the count is greater than 1, your add_action call might be in a file that loads multiple times (e.g., inside a template loop) or the plugin is initialized repeatedly. Ensure initialization code (like add_action) runs only once, typically in the main plugin file or theme's functions.php.

Advanced Pattern: Using WordPress's did_action Function

WordPress provides did_action( $hook_name ), which returns how many times a given hook has been fired. While init usually fires once per request, it might fire multiple times in special cases (e.g., AJAX, REST API requests). You can use it for finer control.

function my_conditional_init_action() {
    // Ensure execution only on the main request's init, avoid duplicates in AJAX, etc.
    if ( did_action( 'init' ) > 1 ) {
        return;
    }
    // Your initialization code
    // ...
}
add_action( 'init', 'my_conditional_init_action' );

Note: This method suits scenarios needing strict separation between 'first main request initialization' and 'subsequent sub-requests'. For preventing duplicate execution within the same page request, the static variable method is preferred.

Best Practices & Summary

  • Single Responsibility: Ensure functions attached to init only handle initialization; keep logic simple.
  • Defensive Programming: Always include 'run once' protection in critical one-time operation functions.
  • Code Placement: Place add_action( 'init', ... ) calls in the root scope of your main plugin file or theme's functions.php to prevent duplicate execution from conditionals or loops.
  • Performance Monitoring: Use debugging plugins like Query Monitor to check the number and execution time of callbacks on the init hook.

By implementing these strategies, you can effectively avoid performance loss and potential errors caused by multiple executions of init hook callbacks, making your WordPress plugin or theme run more efficiently and stably.

Post a Comment

Your email will not be published. Required fields are marked with *.