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.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
“Data! Data! Data!” he cried impatiently. “I can’t make bricks without
clay.”
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)
Method 2
Here is the WordPress load chart

Method 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):
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):

Method 4
The basic sequence can also be found on the official docs:
https://codex.wordpress.org/Plugin_API/Action_Reference
Method 5
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:
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.
Method 6
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 a line to the start of the do_action function in wp-includes/plugin.php which logs the $tag, e.g.:
if (isset($some_get_or_post_trigger_var)) file_put_contents(ABSPATH . 'action.log', "$tagn", FILE_APPEND);
You might want to do the same to apply_filters function in the same file. I haven’t found a better way to get the complete chronological sequence.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

