Índice de hooks de WordPress
Un índice consultable de las acciones y filtros de WordPress que los desarrolladores usan de verdad, con la firma exacta de add_action/add_filter, cuándo se ejecuta, un ejemplo funcional y el detalle que hace tropezar a la gente.
39 hooks, con la firma exacta de add_action / add_filter, cuándo se dispara, un ejemplo funcional y eso que siempre se le escapa a la gente. 1 están marcados como sin verificar: no hemos comprobado su número de argumentos contra el código fuente de WordPress, y preferimos advertirlo antes que presentar una suposición como un hecho.
Esto no es exhaustivo ni pretende serlo: WordPress tiene miles de hooks. Cubre los que la gente usa de verdad.
Se ejecuta después de que WordPress termina de cargar, pero antes de enviar ninguna cabecera. El lugar habitual para registrar tipos de contenido personalizados, taxonomías y reglas de reescritura.
Se dispara
En cada petición, tanto en el frontend como en el administrador, después de cargar el núcleo, los plugins y el tema.
Firma
add_action( 'init', 'my_callback' );Ejemplo
add_action( 'init', function () {
register_post_type( 'book', array(
'public' => true,
'label' => 'Books',
'show_in_rest' => true, // required for the block editor
) );
} );La trampa
init se ejecuta en CADA petición, incluidas las de AJAX, REST y cron. Hacer aquí cualquier cosa lenta (una petición HTTP, una consulta pesada) penaliza cada carga de página. Además: el usuario actual ya está disponible aquí, pero todavía no puedes imprimir nada; las cabeceras aún no se han enviado, pero están a punto de hacerlo.
Se ejecuta una vez cargado el tema. El lugar correcto para add_theme_support(), add_image_size() y registrar menús de navegación.
Se dispara
Después de cargar el functions.php del tema, ANTES de init.
Firma
add_action( 'after_setup_theme', 'my_callback' );Ejemplo
add_action( 'after_setup_theme', function () {
add_theme_support( 'post-thumbnails' );
add_theme_support( 'title-tag' );
add_image_size( 'card', 600, 400, true ); // true = hard crop
} );La trampa
Registrar los tamaños de imagen en init en lugar de aquí funciona por casualidad la mayoría de las veces y luego falla de forma misteriosa en otras. Usa after_setup_theme: es para lo que existe.
Se ejecuta una vez cargados todos los plugins activos. El primer punto seguro para interactuar con otro plugin.
Se dispara
Después de incluir todos los archivos de los plugins, antes de cargar el tema.
Firma
add_action( 'plugins_loaded', 'my_callback' );La trampa
El usuario actual TODAVÍA no está disponible aquí: wp_get_current_user() no funcionará de forma fiable. Si necesitas el usuario, usa init.
Se ejecuta después de que WordPress esté completamente cargado: los plugins, el tema, init y el usuario ya están listos.
Se dispara
Después de init y después de configurar el usuario actual.
Firma
add_action( 'wp_loaded', 'my_callback' );Se ejecuta al inicio de cada petición del administrador. Se usa para register_setting(), comprobaciones de permisos y redirecciones en el administrador.
Se dispara
En cada petición del lado del administrador.
Firma
add_action( 'admin_init', 'my_callback' );La trampa
También se activa en las peticiones a admin-ajax.php, que no son realmente «pantallas de administración». Si tu callback da por hecho que existe una pantalla, protégelo con wp_doing_ajax().
El único lugar correcto para encolar el CSS y el JavaScript del frontend.
Se dispara
En las cargas de página del frontend, antes de que se renderice el <head>.
Firma
add_action( 'wp_enqueue_scripts', 'my_callback' );Ejemplo
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_style(
'child-style',
get_stylesheet_uri(),
array( 'parent-style' ), // load AFTER the parent
wp_get_theme()->get( 'Version' ) // cache-bust on version change
);
} );La trampa
A pesar del nombre, gestiona tanto los ESTILOS como los scripts: no existe ningún hook wp_enqueue_styles, y la gente pierde un tiempo real buscándolo. Además: no se activa en el administrador (usa admin_enqueue_scripts) ni en el editor de bloques (usa enqueue_block_editor_assets).
Encola CSS/JS en el administrador. Recibe el sufijo de hook de la pantalla actual para que puedas cargar recursos en una sola pantalla.
Se dispara
En las cargas de páginas del administrador.
Firma
add_action( 'admin_enqueue_scripts', 'my_callback' ); // receives $hook_suffixEjemplo
add_action( 'admin_enqueue_scripts', function ( $hook ) {
if ( 'settings_page_my-plugin' !== $hook ) {
return; // do not load our JS on every admin screen
}
wp_enqueue_script( 'my-admin', plugins_url( 'admin.js', __FILE__ ), array(), '1.0', true );
} );La trampa
No comprobar $hook es la causa más común de que un plugin rompa pantallas de administración que no tienen nada que ver. Carga tus recursos donde hagan falta y en ningún otro sitio.
Encola recursos únicamente en el editor de bloques (Gutenberg): ni en el frontend ni en el resto del administrador.
Se dispara
Cuando se carga el editor de bloques.
Firma
add_action( 'enqueue_block_editor_assets', 'my_callback' );Imprime contenido dentro del <head>. Se usa para etiquetas meta y CSS crítico en línea.
Se dispara
Dentro del <head>, en el frontend.
Firma
add_action( 'wp_head', 'my_callback' );La trampa
NO encoles scripts ni estilos aquí: usa wp_enqueue_scripts, que permite a WordPress gestionar las dependencias y evitar duplicados. Imprimir una etiqueta <script> directamente en wp_head se salta todo eso y es la razón por la que tantos sitios cargan jQuery tres veces.
Imprime contenido justo antes de </body>. El lugar adecuado para fragmentos de analítica y marcado diferido.
Se dispara
Al final de la página del frontend.
Firma
add_action( 'wp_footer', 'my_callback' );La trampa
Un tema que se olvida de llamar a wp_footer() rompe la barra de administración, rompe cualquier plugin que encole un script en el pie de página y supone un rechazo automático en la revisión de temas de wordpress.org.
Filtra el contenido de la entrada justo antes de mostrarlo. El lugar habitual para añadir marcado al principio o al final de una entrada.
Se dispara
Cada vez que se llama a the_content().
Firma
add_filter( 'the_content', 'my_callback' ); // receives $contentEjemplo
add_filter( 'the_content', function ( $content ) {
if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
return $content; // <- the guard everyone forgets
}
return $content . '<p class="cta">Enjoyed this? Read more.</p>';
} );La trampa
Esto se ejecuta muchas más veces de lo que crees: en widgets, en respuestas REST, en la generación de extractos de algunos plugins y una vez por entrada dentro del bucle de un archivo. Sin la comprobación is_singular / in_the_loop / is_main_query, tu CTA aparece quince veces en el índice del blog y dentro del feed RSS. Es el filtro peor utilizado de WordPress.
Filtra el título de la entrada antes de mostrarlo.
Se dispara
Cada vez que se llama a the_title() o get_the_title().
Firma
add_filter( 'the_title', 'my_callback', 10, 2 ); // receives $title, $post_idLa trampa
También se activa para los títulos del menú de administración, de los menús de navegación y del <title> del documento. Filtrarlo sin condiciones cambiará el nombre de cosas en sitios que no pretendías.
Establece cuántas PALABRAS contiene un extracto generado automáticamente. El valor por defecto es 55.
Se dispara
Cuando WordPress genera un extracto para una entrada que no tiene un extracto manual.
Firma
add_filter( 'excerpt_length', 'my_callback' ); // receives $length (words)Ejemplo
add_filter( 'excerpt_length', function ( $length ) {
return 25;
}, 999 ); // high priority: many themes set this too, and last one winsLa trampa
No tiene ningún efecto en las entradas con un extracto MANUAL: esos se usan tal cual. Y muchos temas aplican este filtro por su cuenta, así que a menudo necesitas un número de prioridad alto para ganar.
Cambia la cadena que se añade a un extracto truncado. El valor por defecto es " […]".
Se dispara
Cuando se trunca un extracto generado automáticamente.
Firma
add_filter( 'excerpt_more', 'my_callback' ); // receives $moreModifica una consulta ANTES de que se ejecute. La forma correcta de cambiar lo que aparece en un archivo, mucho mejor que una segunda WP_Query.
Se dispara
Después de analizar las variables de la consulta, antes de construir el SQL.
Firma
add_action( 'pre_get_posts', 'my_callback' ); // receives WP_Query $query (by reference)Ejemplo
add_action( 'pre_get_posts', function ( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return; // <- both guards are mandatory
}
if ( $query->is_category() ) {
$query->set( 'posts_per_page', 12 );
}
} );La trampa
También se ejecuta en el ADMINISTRADOR, y lo hace en TODAS las consultas, incluidas las de los menús, los widgets y la biblioteca de medios. Si omites las comprobaciones is_admin() e is_main_query(), cambiarás en silencio lo que muestra la lista de entradas del administrador, un fallo realmente difícil de rastrear. Ten en cuenta también: modifica $query, no devuelvas nada.
Filtra la cláusula WHERE de SQL en bruto de una consulta.
Se dispara
Mientras se ensambla el SQL de la consulta.
Firma
add_filter( 'posts_where', 'my_callback', 10, 2 ); // receives $where, WP_Query $queryLa trampa
Estás escribiendo SQL en bruto. Protégelo siempre con $query->is_main_query() y usa siempre $wpdb->prepare() para cualquier entrada del usuario. Esta es la forma más rápida de introducir una inyección SQL en un sitio de WordPress.
Filtra el número total de entradas que encontró una consulta, que es a partir de lo que se calcula la paginación.
Se dispara
Después de ejecutar la consulta, antes de calcular la paginación.
Firma
add_filter( 'found_posts', 'my_callback', 10, 2 ); // receives $found_posts, WP_Query $queryEstablece el tamaño en píxeles por encima del cual WordPress reduce la escala de una subida y sirve una copia «-scaled» en lugar de tu original. Por defecto, 2560.
Se dispara
Al subir, cuando WordPress decide si una imagen es «grande».
Firma
add_filter( 'big_image_size_threshold', 'my_callback' ); // receives int $thresholdEjemplo
// Serve originals at full resolution — no -scaled copy.
add_filter( 'big_image_size_threshold', '__return_false' );
// Or just raise the ceiling:
add_filter( 'big_image_size_threshold', function () {
return 3840;
} );La trampa
Esta es la causa más común de «mi imagen se ve borrosa después de subirla a WordPress», y casi nadie sabe que existe. Tu original sigue en el disco: simplemente nunca se sirve. Ten en cuenta que se aplica al lado MÁS LARGO y solo afecta a las imágenes subidas después de activar el filtro.
Filtra el array de tamaños de imagen que WordPress está a punto de generar para una subida. Elimina un tamaño del array para dejar de generarlo.
Se dispara
Al subir, antes de crear los archivos derivados.
Firma
add_filter( 'intermediate_image_sizes_advanced', 'my_callback', 10, 2 ); // receives $sizes, $metadataEjemplo
add_filter( 'intermediate_image_sizes_advanced', function ( $sizes ) {
unset( $sizes['1536x1536'] );
unset( $sizes['2048x2048'] );
return $sizes;
} );La trampa
Eliminar un tamaño que tu tema sí USA hace que WordPress recurra al original a tamaño completo, lo cual es peor que el espacio en disco que ahorraste. Y esto solo evita que se creen archivos NUEVOS; los existentes se quedan hasta que los regeneres.
Establece la calidad JPEG con la que WordPress vuelve a codificar las imágenes subidas. Por defecto, 82.
Se dispara
Cada vez que WordPress escribe un JPEG.
Firma
add_filter( 'jpeg_quality', 'my_callback', 10, 2 ); // receives $quality, $contextLa trampa
La calidad 82 está bien para fotografías y es claramente mala para capturas de pantalla, logotipos y gráficos planos: estos tienen bordes definidos y no tienen ruido fotográfico donde ocultar los artefactos. Además: wp_editor_set_quality es el filtro más moderno y cubre directamente las clases del editor de imágenes; es habitual configurar ambos.
Establece la calidad de salida que usan las clases WP_Image_Editor (GD e Imagick).
Se dispara
Cuando una instancia del editor de imágenes establece su calidad.
Firma
add_filter( 'wp_editor_set_quality', 'my_callback', 10, 2 ); // receives $quality, $mime_typeAñade tus tamaños de imagen personalizados al desplegable de tamaños de la ventana de medios y del editor de bloques.
Se dispara
Cuando la interfaz de medios construye su selector de tamaños.
Firma
add_filter( 'image_size_names_choose', 'my_callback' ); // receives $sizesEjemplo
add_filter( 'image_size_names_choose', function ( $sizes ) {
return array_merge( $sizes, array( 'card' => __( 'Card', 'textdomain' ) ) );
} );La trampa
add_image_size() registra el tamaño, pero NO hace que se pueda seleccionar en el editor. Cada pregunta de «mi tamaño de imagen personalizado no aparece en el desplegable» se debe a que falta este filtro.
Inspecciona o rechaza un archivo ANTES de moverlo a la carpeta de subidas.
Se dispara
Durante la subida, antes de escribir el archivo.
Firma
add_filter( 'wp_handle_upload_prefilter', 'my_callback' ); // receives $fileLa trampa
Para rechazar un archivo, asigna a $file['error'] una cadena con el mensaje y devuelve $file. Devolver false o lanzar una excepción no hará lo que quieres.
Filtra el array de metadatos (incluidos los tamaños generados) después de procesar un adjunto.
Se dispara
Después de crear las imágenes derivadas al subir.
Firma
add_filter( 'wp_generate_attachment_metadata', 'my_callback', 10, 2 ); // receives $metadata, $attachment_idLa trampa
Este es el hook que usan los plugins de optimización de imágenes. Puede ser lento: se ejecuta después de escribir cada tamaño de imagen, y hacer una petición HTTP aquí hace que cada subida tenga que esperar por ella.
Filtra los candidatos de srcset que WordPress genera para una imagen adaptable.
Se dispara
Cuando se renderiza una imagen adaptable.
Firma
add_filter( 'wp_calculate_image_srcset', 'my_callback', 10, 5 );La trampa
Eliminar el tamaño medium_large (768w) en otro sitio degrada srcset en silencio: ese tamaño existe casi exclusivamente para servirlo.
No hemos verificado el número exacto de argumentos de este hook contra el código fuente de WordPress. Consulta developer.wordpress.org antes de fiarte de él. Preferimos señalar la incertidumbre antes que presentar en silencio una suposición como un hecho.
Registra páginas de menú y submenú en el administrador.
Se dispara
Cuando se está construyendo el menú del administrador.
Firma
add_action( 'admin_menu', 'my_callback' );Ejemplo
add_action( 'admin_menu', function () {
add_options_page(
'My Plugin',
'My Plugin',
'manage_options', // capability — NOT a role
'my-plugin',
'my_render_settings_page'
);
} );La trampa
El argumento de capacidad es una CAPACIDAD, no un rol. Pasar «administrator» parece funcionar (porque en algunas configuraciones los administradores tienen una capacidad con ese nombre) y luego concede acceso, sin avisar, a las personas equivocadas. Usa manage_options.
Imprime un aviso en la parte superior de una pantalla de administración.
Se dispara
Al renderizar una página del administrador.
Firma
add_action( 'admin_notices', 'my_callback' );La trampa
Se activa en TODAS las pantallas de administración. Un plugin que muestra sin condiciones un aviso de «¡gracias por instalar!» en todas ellas es el patrón más odiado del administrador de WordPress. Ponle una comprobación y haz que se pueda descartar.
Se ejecuta cada vez que se crea o actualiza una entrada. El lugar habitual para guardar los datos de una caja meta.
Se dispara
Después de escribir una entrada en la base de datos.
Firma
add_action( 'save_post', 'my_callback', 10, 3 ); // receives $post_id, $post, $updateEjemplo
add_action( 'save_post', function ( $post_id ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
if ( wp_is_post_revision( $post_id ) ) return;
if ( ! current_user_can( 'edit_post', $post_id ) ) return;
// ... now it is safe to save
}, 10, 1 );La trampa
Se activa en los AUTOGUARDADOS y en las REVISIONES, además de en los guardados reales. Sin las tres comprobaciones anteriores, tus metadatos se sobrescriben con valores vacíos cada vez que el editor hace un autoguardado, un fallo que parece que los datos desaparecen de forma aleatoria.
Registra cajas meta en la pantalla de edición de entradas.
Se dispara
Cuando se construye la pantalla de edición.
Firma
add_action( 'add_meta_boxes', 'my_callback', 10, 2 ); // receives $post_type, $postLa trampa
Las cajas meta clásicas no aparecen en el editor de bloques a menos que este recurra al modo de compatibilidad. Para un sitio nativo del editor de bloques, registra el meta de la entrada con show_in_rest y construye en su lugar un panel en la barra lateral.
Se ejecuta después de que un usuario inicia sesión correctamente.
Se dispara
Tras una autenticación correcta.
Firma
add_action( 'wp_login', 'my_callback', 10, 2 ); // receives $user_login, WP_User $userSe ejecuta justo después de crear un nuevo usuario.
Se dispara
En el registro.
Firma
add_action( 'user_register', 'my_callback', 10, 2 ); // receives $user_id, $userdataFiltra el resultado de la autenticación. Devuelve un WP_Error para bloquear un inicio de sesión.
Se dispara
Durante el intento de inicio de sesión.
Firma
add_filter( 'authenticate', 'my_callback', 30, 3 ); // receives $user, $username, $passwordLa trampa
Aquí la prioridad importa mucho. El núcleo de WordPress engancha sus propias comprobaciones en las prioridades 20 y 30: si te enganchas demasiado pronto, te ejecutas antes de que siquiera se haya comprobado la contraseña.
Registra rutas y campos de la REST.
Se dispara
Cuando se inicializa la REST API.
Firma
add_action( 'rest_api_init', 'my_callback' );Ejemplo
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/items', array(
'methods' => 'GET',
'callback' => 'my_get_items',
'permission_callback' => '__return_true', // BE DELIBERATE ABOUT THIS
) );
} );La trampa
permission_callback es OBLIGATORIO: desde WP 5.5, omitirlo lanza un aviso, y ponerlo a __return_true hace que el endpoint sea totalmente público. Eso está bien para datos realmente públicos y es un agujero serio para cualquier otra cosa. Decide, no lo dejes por defecto.
Añade un intervalo de recurrencia personalizado para wp_schedule_event().
Se dispara
Cuando WordPress construye su lista de programaciones disponibles.
Firma
add_filter( 'cron_schedules', 'my_callback' ); // receives $schedulesEjemplo
add_filter( 'cron_schedules', function ( $schedules ) {
$schedules['every_five_minutes'] = array(
'interval' => 300, // seconds
'display' => __( 'Every 5 minutes', 'textdomain' ),
);
return $schedules;
} );La trampa
WP-Cron no es un cron de verdad. Solo se activa cuando alguien visita el sitio, así que un sitio con poco tráfico no ejecutará una programación de cinco minutos cada cinco minutos. Si el momento exacto importa de verdad, desactiva WP-Cron (DISABLE_WP_CRON) y llama a wp-cron.php desde un cron real del sistema.
Se ejecuta justo después de guardar un comentario.
Se dispara
Al enviar un comentario.
Firma
add_action( 'comment_post', 'my_callback', 10, 3 ); // receives $comment_id, $approved, $commentdataFiltra los datos de un comentario antes de guardarlo. Se usa para comprobaciones de spam y validación.
Se dispara
Antes de escribir el comentario.
Firma
add_filter( 'preprocess_comment', 'my_callback' ); // receives $commentdataLa trampa
Para rechazar un comentario aquí, llama a wp_die() con un mensaje: devolver false no lo detiene.
Se ejecuta en la página de pedido recibido tras finalizar la compra con éxito.
Se dispara
Cuando el cliente llega a la página de agradecimiento.
Firma
add_action( 'woocommerce_thankyou', 'my_callback' ); // receives $order_idLa trampa
Este NO es un lugar fiable para desencadenar la preparación del pedido. Solo se activa si el cliente llega realmente a la página de agradecimiento: si cierra la pestaña después de pagar, nunca se ejecuta. Usa woocommerce_payment_complete o un hook de cambio de estado del pedido para cualquier cosa que tenga que ocurrir sí o sí.
Muestra marcado justo antes del botón de añadir al carrito en una página de producto.
Se dispara
Mientras se renderiza la página de producto.
Firma
add_action( 'woocommerce_before_add_to_cart_button', 'my_callback' );Añade, elimina o reordena campos en la página de pago clásica.
Se dispara
Cuando se construye el formulario de pago.
Firma
add_filter( 'woocommerce_checkout_fields', 'my_callback' ); // receives $fieldsLa trampa
Esto solo afecta al pago CLÁSICO (por shortcode). El nuevo pago basado en bloques lo ignora por completo y se personaliza mediante Slot & Fill en JavaScript. Esto hace tropezar a casi todo el mundo la primera vez.