¿Cómo obtener la secuencia de ejecución de hooks/acciones de WordPress?
¿En qué orden se ejecutan los hooks de add_action
?
es decir,
init
wp_head
wp_footer
after_theme_setup
etc...
???
???
???
EDICIÓN:
También he publicado mi solución.

"¡Datos! ¡Datos! ¡Datos!" gritó impacientemente. "No puedo hacer ladrillos sin arcilla."
Así que vamos a recopilar algunos datos reales de una instalación limpia de WordPress 5.7.2 con el tema TwentyTwelve activado y solo un widget de texto.
Para la página de inicio, se realizan las siguientes llamadas do_action / do_action_ref_array
en el siguiente orden (sin sesión iniciada):
[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
usando el plugin must-use:
add_action( 'all', function ( $tag ) {
static $hooks = array();
// Solo hooks do_action / do_action_ref_array.
if ( did_action( $tag ) ) {
$hooks[] = $tag;
}
if ( 'shutdown' === $tag ) {
print_r( $hooks );
}
} );
que imprime los hooks de acción recopilados, para la página actual, en la última acción disponible del núcleo (shutdown).
Si quieres comprobar el orden de las acciones y cuántas veces se dispara cada una, entonces puedes usar por ejemplo:
add_action ( 'shutdown', function(){
print_r ( $GLOBALS['wp_actions'] );
} );
o sin globals explícitos:
add_action ( 'shutdown', function() use ( &$wp_actions ) {
print_r ( $wp_actions );
} );
que devuelve el 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
donde podemos obtener el conteo total con echo array_sum( $GLOBALS['wp_actions'] );
Aquí hay una versión más bonita:
add_action ( 'shutdown', function() {
foreach ( $GLOBALS['wp_actions'] as $action => $count ) {
printf( '%s (%d) <br/>' . PHP_EOL, $action, $count );
}
} );
o sin globals explícitos:
add_action ( 'shutdown', function() use ( &$wp_actions ) {
foreach ( $wp_actions as $action => $count ) {
printf( '%s (%d) <br/>' . PHP_EOL, $action, $count );
}
} );
para obtener la siguiente lista:
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)
PD: También deberías echar un vistazo al excelente plugin Query Monitor de John Blackbourn. (No tengo relación con este plugin)

Gracias por mencionar Query Monitor. Parece ser un plugin útil en este caso.

@kraftner gracias por la actualización, siempre tuve planeado (pero lo olvidé) enlazar directamente a la historia misma como fuente adecuada, obviamente mis habilidades de búsqueda estilo Sherlock Holmes no eran muy buenas en ese momento ;-)

Me gustó la cita y quería ver más contexto. Y como ya tenía el enlace para mí, ¿por qué no actualizarlo aquí también? :)

Esto no obtiene la secuencia exacta debido a la agrupación por nombre de hook.

@Walf Por favor, consulta la respuesta actualizada para WP 5.7.2 y el mu-plugin.

Tu respuesta actualizada es una mejora significativa. Sin embargo, puede que no genere ningún resultado si un error detiene la ejecución. Por supuesto, un error te indica dónde ocurrió, pero conocer los eventos anteriores puede ser extremadamente útil. Además, el uso de declaraciones print/dump probablemente interferirá con la solicitud, o se anulará por completo si finalizas la solicitud antes de tiempo.

WordPress registra el hook de acción shutdown como una función de shutdown con PHP register_shutdown_function( 'shutdown_action_hook' )
, por lo que se ejecutará al final incluso si, por ejemplo, el callback de otro hook llama a exit()
, provoca un error con trigger_error()
o lanza un Error no capturado. Según un comentario en la documentación de PHP, también debería ejecutarse después de un timeout. Sí, no se mostrará si fastcgi_finish_request lo corta, pero creo que se podría registrar en un log si imprimir es un problema.


Añade al menos la fuente, o mejor aún: encuentra un duplicado para esta pregunta.

En realidad no sabía de dónde lo obtuve. Tenía esta imagen guardada en mi PC. De lo contrario lo habría hecho.

También está publicado en la página de inicio de Tom Mc Farlin: The WordPress Page Lifecycle -> https://tommcfarlin.com/wordpress-page-lifecycle/

¡Solución encontrada!
Gracias @birgire por la excelente respuesta. Añadiré a eso que muplugins_loaded
a veces no se ejecuta, así que usaré plugins_loaded
como el hook más temprano (pero en ese momento, la autorización de usuario aún no se ha realizado. Si deseas verificar la autorización del usuario, entonces init
es el más temprano para eso)...
P.D. existen excelentes plugins:
1) Query Monitor - Puedes ver todo lo que ocurre durante la carga de la página, es decir, la duración de cada función ejecutada y mucho más (ver todas las capturas de pantalla en la página del plugin):
2) WP-DEBUG-BAR + WP-DEBUG-SLOW-ACTIONS:
a) lista de depuración de hooks(acciones) ejecutados en tu sitio.
b) Ver la duración de cada acción (no función):

La secuencia básica también se puede encontrar en la documentación oficial:

Esto es similar a la respuesta de @birgire, pero proporciona un informe detallado que se puede mostrar para cualquier URL en un sitio WordPress. Necesitarás estar logueado como un usuario de nivel Administrador, luego agregar ?wp-hooks
al final de la URL que deseas probar.
/**
* Referencia de Hooks de WordPress
*
* Muestra todos los hooks de acción y filtro al final de cualquier página
* agregando ?wp-hooks al final de la URL mientras estás logueado
* como un usuario de nivel Administrador.
*/
function kevinlearynet_hooks_reference() {
// Solo se muestra para usuarios de nivel Administrador cuando se agrega ?list-wp-hooks a la URL
$trigger = isset( $_GET['wp-hooks'] ) && current_user_can( 'manage_options' );
if ( ! $trigger ) return;
// Captura y ordena filtros y hooks
$filters = array_keys( $GLOBALS['wp_filter'] );
sort( $filters );
$actions = array_keys( $GLOBALS['wp_actions'] );
// Genera el template básico
ob_start();
?>
<section class="wp-hooks">
<h1 class="wp-hooks__h1">Referencia de Hooks de WordPress</h1>
<div class="wp-hooks__lists">
<div class="wp-hooks__col">
<h2 class="wp-hooks__h2">Acciones</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">Filtros</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' );
La salida se ve así:
Escribí sobre esto en mi propio blog, aquí está la fuente original como referencia. Incluye un poco más de detalle sobre las decisiones detrás del ordenamiento y funcionalidad.

No hay dos solicitudes exactamente iguales.
Una forma rápida y sucia pero muy precisa de descubrir lo que está sucediendo es temporalmente agregar líneas al inicio de las funciones do_action
y do_action_ref_array
en wp-includes/plugin.php
para registrar cada hook, por ejemplo:
if (isset($some_trigger_from_get_post_head_etc)) file_put_contents(ABSPATH . 'hooks.log', "$hook_name\n", FILE_APPEND);
Puede que quieras hacer lo mismo con las funciones apply_filters
y apply_filters_ref_array
en el mismo archivo.
No he encontrado una mejor manera de obtener la secuencia completa y cronológica, incluyendo hooks que se ejecutan antes de que incluso se carguen los plugins must-use y después de shutdown
.
