67

In what order add_action hooks execute?

i.e.

init wp_head wp_footer after_theme_setup etc... ??? ??? ??? 



EDIT:

I've also posted my solution.

1
  • note, BACK-END vs FRONT-END data differs Commented Oct 4, 2024 at 17:45

6 Answers 6

123

"Data! Data! Data!" he cried impatiently. "I can't make bricks without clay."

Sherlock Holmes - The Adventure of the Copper Beeches

So let's gather some real data from a vanilla WordPress 5.7.2 install and the TwentyTwelve theme activated with only a single Text widget.

For the home page, the following do_action / do_action_ref_array calls are made in the following order (logged out):

[0] => mu_plugin_loaded [1] => muplugins_loaded [2] => registered_taxonomy [3] => registered_taxonomy [4] => registered_taxonomy [5] => registered_taxonomy [6] => registered_taxonomy [7] => registered_post_type [8] => registered_post_type [9] => registered_post_type [10] => registered_post_type [11] => registered_post_type [12] => registered_post_type [13] => registered_post_type [14] => registered_post_type [15] => registered_post_type [16] => registered_post_type [17] => plugins_loaded [18] => sanitize_comment_cookies [19] => wp_roles_init [20] => setup_theme [21] => unload_textdomain [22] => load_textdomain [23] => after_setup_theme [24] => load_textdomain [25] => load_textdomain [26] => auth_cookie_malformed [27] => set_current_user [28] => init [29] => registered_post_type [30] => registered_post_type [31] => registered_post_type [32] => registered_post_type [33] => registered_post_type [34] => registered_post_type [35] => registered_post_type [36] => registered_post_type [37] => registered_post_type [38] => registered_post_type [39] => registered_taxonomy [40] => registered_taxonomy [41] => registered_taxonomy [42] => registered_taxonomy [43] => registered_taxonomy [44] => widgets_init [45] => register_sidebar [46] => register_sidebar [47] => register_sidebar [48] => wp_register_sidebar_widget [49] => wp_register_sidebar_widget [50] => wp_register_sidebar_widget [51] => wp_register_sidebar_widget [52] => wp_register_sidebar_widget [53] => wp_register_sidebar_widget [54] => wp_register_sidebar_widget [55] => wp_register_sidebar_widget [56] => wp_register_sidebar_widget [57] => wp_register_sidebar_widget [58] => wp_register_sidebar_widget [59] => wp_register_sidebar_widget [60] => wp_register_sidebar_widget [61] => wp_register_sidebar_widget [62] => wp_register_sidebar_widget [63] => wp_register_sidebar_widget [64] => wp_default_scripts [65] => wp_register_sidebar_widget [66] => wp_register_sidebar_widget [67] => wp_register_sidebar_widget [68] => wp_register_sidebar_widget [69] => wp_register_sidebar_widget [70] => wp_register_sidebar_widget [71] => wp_register_sidebar_widget [72] => wp_register_sidebar_widget [73] => wp_register_sidebar_widget [74] => wp_register_sidebar_widget [75] => wp_register_sidebar_widget [76] => wp_register_sidebar_widget [77] => wp_register_sidebar_widget [78] => wp_register_sidebar_widget [79] => wp_register_sidebar_widget [80] => wp_register_sidebar_widget [81] => wp_register_sidebar_widget [82] => wp_register_sidebar_widget [83] => wp_register_sidebar_widget [84] => wp_register_sidebar_widget [85] => wp_register_sidebar_widget [86] => wp_register_sidebar_widget [87] => wp_register_sidebar_widget [88] => wp_register_sidebar_widget [89] => wp_register_sidebar_widget [90] => wp_register_sidebar_widget [91] => wp_register_sidebar_widget [92] => wp_register_sidebar_widget [93] => wp_register_sidebar_widget [94] => wp_sitemaps_init [95] => wp_loaded [96] => parse_request [97] => send_headers [98] => parse_tax_query [99] => parse_query [100] => pre_get_posts [101] => posts_selection [102] => wp [103] => template_redirect [104] => get_header [105] => wp_head [106] => wp_enqueue_scripts [107] => wp_default_styles [108] => enqueue_block_assets [109] => wp_print_styles [110] => wp_print_scripts [111] => wp_body_open [112] => parse_tax_query [113] => parse_query [114] => pre_get_posts [115] => parse_tax_query [116] => posts_selection [117] => parse_tax_query [118] => parse_query [119] => pre_get_posts [120] => parse_tax_query [121] => posts_selection [122] => parse_tax_query [123] => parse_query [124] => pre_get_posts [125] => parse_tax_query [126] => posts_selection [127] => parse_tax_query [128] => parse_query [129] => pre_get_posts [130] => parse_tax_query [131] => posts_selection [132] => parse_term_query [133] => pre_get_terms [134] => loop_start [135] => the_post [136] => get_template_part_content [137] => get_template_part [138] => parse_comment_query [139] => pre_get_comments [140] => parse_comment_query [141] => pre_get_comments [142] => comment_form_comments_closed [143] => loop_end [144] => get_sidebar [145] => dynamic_sidebar_before [146] => dynamic_sidebar [147] => dynamic_sidebar_after [148] => get_footer [149] => twentytwelve_credits [150] => wp_footer [151] => wp_print_footer_scripts [152] => shutdown 

using the must-use plugin:

add_action( 'all', function ( $tag ) { static $hooks = array(); // Only do_action / do_action_ref_array hooks. if ( did_action( $tag ) ) { $hooks[] = $tag; } if ( 'shutdown' === $tag ) { print_r( $hooks ); } } ); 

that prints the collected action hooks, for the current page, in the last available core action (shutdown).

If you want to check the order of actions and how many times each one is fired, then you can use for example:

add_action ( 'shutdown', function(){ print_r ( $GLOBALS['wp_actions'] ); } ); 

or without explicit globals:

add_action ( 'shutdown', function() use ( &$wp_actions ) { print_r ( $wp_actions ); } ); 

that outputs the array:

[mu_plugin_loaded] => 1 [muplugins_loaded] => 1 [registered_taxonomy] => 10 [registered_post_type] => 20 [plugins_loaded] => 1 [sanitize_comment_cookies] => 1 [wp_roles_init] => 1 [setup_theme] => 1 [unload_textdomain] => 1 [load_textdomain] => 3 [after_setup_theme] => 1 [auth_cookie_malformed] => 1 [set_current_user] => 1 [init] => 1 [widgets_init] => 1 [register_sidebar] => 3 [wp_register_sidebar_widget] => 45 [wp_default_scripts] => 1 [wp_sitemaps_init] => 1 [wp_loaded] => 1 [parse_request] => 1 [send_headers] => 1 [parse_tax_query] => 9 [parse_query] => 5 [pre_get_posts] => 5 [posts_selection] => 5 [wp] => 1 [template_redirect] => 1 [get_header] => 1 [wp_head] => 1 [wp_enqueue_scripts] => 1 [wp_default_styles] => 1 [enqueue_block_assets] => 1 [wp_print_styles] => 1 [wp_print_scripts] => 1 [wp_body_open] => 1 [parse_term_query] => 1 [pre_get_terms] => 1 [loop_start] => 1 [the_post] => 1 [get_template_part_content] => 1 [get_template_part] => 1 [parse_comment_query] => 2 [pre_get_comments] => 2 [comment_form_comments_closed] => 1 [loop_end] => 1 [get_sidebar] => 1 [dynamic_sidebar_before] => 1 [dynamic_sidebar] => 1 [dynamic_sidebar_after] => 1 [get_footer] => 1 [twentytwelve_credits] => 1 [wp_footer] => 1 [wp_print_footer_scripts] => 1 [shutdown] => 1 

where we can get the total count with echo array_sum( $GLOBALS['wp_actions'] );

Here's a prettified version:

add_action ( 'shutdown', function() { foreach ( $GLOBALS['wp_actions'] as $action => $count ) { printf( '%s (%d) <br/>' . PHP_EOL, $action, $count ); } } ); 

or without explicit globals:

add_action ( 'shutdown', function() use ( &$wp_actions ) { foreach ( $wp_actions as $action => $count ) { printf( '%s (%d) <br/>' . PHP_EOL, $action, $count ); } } ); 

to get the following list:

mu_plugin_loaded (1) muplugins_loaded (1) registered_taxonomy (10) registered_post_type (20) plugins_loaded (1) sanitize_comment_cookies (1) wp_roles_init (1) setup_theme (1) unload_textdomain (1) load_textdomain (3) after_setup_theme (1) auth_cookie_malformed (1) set_current_user (1) init (1) widgets_init (1) register_sidebar (3) wp_register_sidebar_widget (45) wp_default_scripts (1) wp_sitemaps_init (1) wp_loaded (1) update_option (1) update_option__transient_doing_cron (1) updated_option (1) set_transient_doing_cron (1) setted_transient (1) requests-requests.before_request (1) requests-curl.before_request (1) http_api_curl (1) requests-curl.before_send (1) requests-curl.after_send (1) requests-curl.after_request (1) requests-requests.before_parse (1) http_api_debug (1) parse_request (1) send_headers (1) parse_tax_query (9) parse_query (5) pre_get_posts (5) posts_selection (5) wp (1) template_redirect (1) get_header (1) wp_head (1) wp_enqueue_scripts (1) wp_default_styles (1) enqueue_block_assets (1) wp_print_styles (1) wp_print_scripts (1) wp_body_open (1) parse_term_query (1) pre_get_terms (1) loop_start (1) the_post (1) get_template_part_content (1) get_template_part (1) parse_comment_query (2) pre_get_comments (2) comment_form_comments_closed (1) loop_end (1) get_sidebar (1) dynamic_sidebar_before (1) dynamic_sidebar (1) dynamic_sidebar_after (1) get_footer (1) twentytwelve_credits (1) wp_footer (1) wp_print_footer_scripts (1) shutdown (1) 

PS: You should also check out the great Query Monitor plugin by John Blackbourn. (I'm not related to this plugin)

12
  • 1
    Very nice indeed! Commented Mar 19, 2015 at 19:30
  • 1
    Thank you for mentioning Query Monitor. Seems to be a useful plugin on this case. Commented Oct 16, 2015 at 9:53
  • 1
    I liked the quote and wanted to see more context. And as I already had the link for myself why not just update here as well. :) Commented Mar 1, 2017 at 14:22
  • 1
    Over 4 yo post and still helpful. Thanks a lot! Commented Feb 5, 2019 at 10:15
  • 1
    Glad to hear it's still useful @Gas Commented Sep 29, 2022 at 11:00
28

Here is the WordPress load chart

WordPress Load Chart

Source by @Rarst

3
  • 10
    Add at least the source, or even better: find a duplicate for this question. Commented Sep 29, 2014 at 8:36
  • 3
    Actually I did not know where I got it from. I had this image saved on my PC. Otherwise I would have done that. Commented Sep 29, 2014 at 8:43
  • It's also published on Tom Mc Farlin homepage: The WordPress Page Lifecycle -> tommcfarlin.com/wordpress-page-lifecycle Commented Oct 16, 2015 at 10:05
3

Found Solution!

Thanks @birgire for nice answer. I will add to that, muplugins_loaded sometimes is not fired, so I will use plugins_loaded as the most first hook (but at that time, user-authorization is not done yet. If you want to check user's authorization, then init is the earliest for that)...

p.s. there exist excellent plugins:

1) Query Monitor - You can see everything what happens on the page-load, i.e. duration of each executed function and much more(view all screenshots on plugin page):

enter image description here

2) WP-DEBUG-BAR + WP-DEBUG-SLOW-ACTIONS:
a) debug hooks(actions) run list on your site.
b) See duration of each action (not function): enter image description here

2

The basic sequence can also be found on the official docs:

https://codex.wordpress.org/Plugin_API/Action_Reference

2

This is similar to the @birgire's answer, but it provides a nice report that can be displayed for any URL in a WordPress site. You'll need to be logged-in as an Admin level user, then add ?wp-hooks onto the end of a URL you want to test.

/** * WordPress Hooks Reference * * Dump all action and filter hooks at the bottom of any page * by adding ?wp-hooks onto the end of the URL while logged-in * as an Administrator level user. */ function kevinlearynet_hooks_reference() { // Only shown for Administrator level users when ?list-wp-hooks is added to the URL $trigger = isset( $_GET['wp-hooks'] ) && current_user_can( 'manage_options' ); if ( ! $trigger ) return; // Capture and sort filters and hooks $filters = array_keys( $GLOBALS['wp_filter'] ); sort( $filters ); $actions = array_keys( $GLOBALS['wp_actions'] ); // Output rough template ob_start(); ?> <section class="wp-hooks"> <h1 class="wp-hooks__h1">WordPress Hooks Reference</h1> <div class="wp-hooks__lists"> <div class="wp-hooks__col"> <h2 class="wp-hooks__h2">Actions</h2> <?php foreach ( $actions as $hook ) : ?> <p class="wp-hooks__hook"><?php echo $hook; ?></p> <?php endforeach; ?> </div> <div class="wp-hooks__col"> <h2 class="wp-hooks__h2">Filters</h2> <?php foreach ( $filters as $hook ) : ?> <p class="wp-hooks__hook"><?php echo $hook; ?></p> <?php endforeach; ?> </div> </div> </section> <style> .wp-hooks { padding: 30px; margin: 30px; border-radius: 4px; background: white; font-size: 16px; line-height: 1.4; height: 50vh; min-height: 500px; overflow-y: scroll; } .wp-hooks__lists { display: flex; } .wp-hooks__col { flex: 1; width: 50%; } .wp-hooks__h1 { margin: 0 0 20px; } .wp-hooks__h2 { line-height: 1; font-size: 18px; margin: 0 0 10px; } .wp-hooks__hook { padding: 0; margin: 0; } </style> <?php ob_end_flush(); } add_action( 'shutdown', 'kevinlearynet_hooks_reference' ); 

The output looks like this:

enter image description here

I wrote about this on my own, so here's the original source for reference. This includes a little more detail on decisions behind sorting and functionality.

1

No two requests are exactly the same. A quick-and-dirty but very accurate way to find out what's going on is to temporarily add lines to the start of the do_action & do_action_ref_array functions in wp-includes/plugin.php to log every hook, e.g.:

if (isset($some_trigger_from_get_post_head_etc)) file_put_contents(ABSPATH . 'hooks.log', "$hook_name\n", FILE_APPEND); 

You might want to do the same to the apply_filters & apply_filters_ref_array functions in the same file. I haven't found a better way to get the complete, chronological sequence, including hooks that fire before even must-use plugins load and after shutdown.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.