Si alguna vez te has preguntado cómo es posible que un plugin modifique el comportamiento de WordPress sin tocar ni una línea de su código fuente, la respuesta está en los hooks de WordPress. Los hooks (ganchos, en español) son el mecanismo de extensión sobre el que se construye todo el ecosistema: temas, plugins y hasta el propio núcleo los usan constantemente para insertar funcionalidad o alterar datos en puntos concretos de la ejecución. Entenderlos es la frontera que separa a quien copia y pega fragmentos de código de quien realmente sabe qué está haciendo con su web. En esta guía vamos a ver qué son exactamente los actions y los filters, en qué se diferencian, cómo usarlos con ejemplos reales y funcionales, cómo controlar prioridades y argumentos, y cómo crear tus propios hooks para que tu código también sea extensible. Todo con código PHP probado que puedes adaptar a tu proyecto hoy mismo.
Qué son los hooks de WordPress y por qué existen
WordPress se diseñó desde el principio con una regla de oro: nunca modificar el núcleo. Si cada desarrollador editase los archivos de wp-includes para cambiar un comportamiento, cada actualización borraría esos cambios y mantener una web sería una pesadilla. La solución fue sembrar el código del núcleo con miles de puntos de enganche: momentos concretos de la ejecución en los que WordPress se detiene un instante y pregunta «¿alguien quiere hacer algo aquí?».
Eso es un hook: un punto de extensión con nombre propio. Cuando WordPress carga una página ejecuta, en orden, hooks como init, wp_loaded, template_redirect o wp_head. Tu código puede «engancharse» a cualquiera de ellos registrando una función de callback, y WordPress la ejecutará en ese momento exacto sin que tengas que tocar nada más.
El sistema descansa sobre dos familias:
- Actions (acciones): ejecutan código en un momento dado. No devuelven nada; sirven para hacer cosas (enviar un email, encolar un script, registrar un post type).
- Filters (filtros): reciben un dato, lo modifican y lo devuelven. Sirven para transformar información (cambiar el texto de un extracto, alterar un título, modificar una consulta).
Esta distinción es la primera pregunta que deberías hacerte ante cualquier hook: ¿estoy haciendo algo o estoy modificando algo?
Actions: ejecutar código en el momento justo
Un action se registra con add_action(), que necesita como mínimo dos parámetros: el nombre del hook y la función que quieres ejecutar. Veamos el ejemplo clásico, cargar una hoja de estilos de la forma correcta:
<?php
function biblioweb_cargar_estilos() {
wp_enqueue_style(
'biblioweb-personalizado',
get_stylesheet_directory_uri() . '/css/personalizado.css',
array(),
'1.0.0'
);
}
add_action( 'wp_enqueue_scripts', 'biblioweb_cargar_estilos' );
Aquí le decimos a WordPress: «cuando llegue el momento de encolar scripts y estilos del frontend (wp_enqueue_scripts), ejecuta mi función». Otro ejemplo muy habitual es reaccionar a un evento, como enviar un aviso cuando se publica una entrada:
<?php
function biblioweb_avisar_publicacion( $post_id, $post ) {
// Evitamos revisiones y autoguardados.
if ( wp_is_post_revision( $post_id ) ) {
return;
}
$asunto = 'Nueva entrada publicada: ' . $post->post_title;
$mensaje = 'Se ha publicado una entrada. Revísala aquí: ' . get_permalink( $post_id );
wp_mail( 'editor@tudominio.com', $asunto, $mensaje );
}
add_action( 'publish_post', 'biblioweb_avisar_publicacion', 10, 2 );
Fíjate en los dos últimos parámetros de add_action(): el 10 es la prioridad y el 2 indica cuántos argumentos acepta nuestra función. Los veremos en detalle más adelante, porque son la causa del 90 % de los errores con hooks.
Actions imprescindibles que conviene conocer
init: WordPress ya está cargado; aquí se registran custom post types, taxonomías y shortcodes.wp_enqueue_scripts: punto correcto para encolar CSS y JS del frontend.admin_inityadmin_menu: equivalentes para el área de administración.wp_headywp_footer: imprimen contenido en el<head>y antes de cerrar el<body>.save_post: se dispara al guardar cualquier entrada; ideal para procesar metadatos.wp_loginyuser_register: reaccionan al inicio de sesión y al alta de usuarios.
Filters: modificar datos antes de que se usen
Un filter funciona igual que un action con una diferencia crucial: tu función recibe un valor y debe devolverlo, modificado o no. Si olvidas el return, el dato llega vacío a su destino y romperás algo (extractos en blanco, títulos desaparecidos…). Es el error más común al empezar con los hooks de WordPress.
Ejemplo real: cambiar la longitud del extracto y el texto de «leer más»:
<?php
function biblioweb_longitud_extracto( $longitud ) {
return 30; // palabras
}
add_filter( 'excerpt_length', 'biblioweb_longitud_extracto' );
function biblioweb_leer_mas( $more ) {
return '… <a href="' . esc_url( get_permalink() ) . '">Seguir leyendo</a>';
}
add_filter( 'excerpt_more', 'biblioweb_leer_mas' );
Otro caso muy útil: añadir automáticamente un aviso al final del contenido de cada entrada, solo en el frontend:
<?php
function biblioweb_nota_final( $content ) {
if ( is_singular( 'post' ) && in_the_loop() && is_main_query() ) {
$nota = '<p class="nota-final">¿Te ha resultado útil? Compártelo o déjanos un comentario.</p>';
return $content . $nota;
}
return $content;
}
add_filter( 'the_content', 'biblioweb_nota_final' );
Observa las comprobaciones condicionales: the_content se aplica en muchos contextos (feeds, widgets, páginas), y filtrar sin condiciones acaba mostrando tu nota donde no debe. Devolver siempre $content al final, aunque no lo modifiques, es obligatorio.
Filters que usarás una y otra vez
the_contentythe_title: el cuerpo y el título de las entradas.excerpt_lengthyexcerpt_more: control del extracto.body_class: añade clases CSS al<body>.upload_mimes: permite (o bloquea) tipos de archivo en la mediateca.wp_mail_fromywp_mail_from_name: remitente de los correos de WordPress.login_errors: mensaje de error del login (útil para no dar pistas a atacantes).
Prioridad y argumentos: los dos números que lo cambian todo
Tanto add_action() como add_filter() aceptan cuatro parámetros:
add_filter( 'nombre_del_hook', 'mi_funcion', $prioridad, $num_argumentos );
| Parámetro | Valor por defecto | Qué controla |
|---|---|---|
| Prioridad | 10 | El orden de ejecución: menor número, antes se ejecuta. Con la misma prioridad, gana el que se registró primero. |
| Nº de argumentos | 1 | Cuántos parámetros del hook recibe tu callback. Si el hook pasa 3 y tú declaras 1, solo recibirás el primero. |
La prioridad importa cuando varios plugins tocan el mismo dato. Si tu filtro sobre the_content debe ejecutarse después de que otro plugin haya hecho lo suyo, usa una prioridad alta (por ejemplo 99). Si necesitas adelantarte a todos, usa 1. El número de argumentos, por su parte, debe coincidir con lo que declares en la firma de tu función: si pones 10, 3 pero tu función solo acepta un parámetro, PHP lanzará un error fatal.
Para desenganchar una función existe remove_action() y remove_filter(), con una condición estricta: hay que pasar exactamente el mismo nombre de función y la misma prioridad con la que se registró:
<?php
// Quitar el generador de versión del head (lo añade el core con prioridad por defecto).
remove_action( 'wp_head', 'wp_generator' );
Cómo crear tus propios hooks personalizados
Aquí está el salto de nivel: además de consumir hooks, puedes crearlos. Si desarrollas un tema o un plugin, sembrar tu código con do_action() y apply_filters() permite que otros (o tú mismo, en el futuro) lo extiendan sin tocarlo. Así funciona WooCommerce, y por eso su ecosistema es gigante.
Un action propio se crea con do_action():
<?php
// En tu plugin, tras procesar un pedido de ejemplo:
function biblioweb_procesar_solicitud( $solicitud_id ) {
// ... lógica principal ...
// Punto de extensión: cualquiera puede engancharse aquí.
do_action( 'biblioweb_solicitud_procesada', $solicitud_id );
}
// Otro desarrollador (u otro archivo) se engancha:
add_action( 'biblioweb_solicitud_procesada', function ( $solicitud_id ) {
error_log( 'Solicitud procesada: ' . $solicitud_id );
} );
Y un filter propio, con apply_filters(), que define el valor por defecto y lo ofrece a quien quiera cambiarlo:
<?php
function biblioweb_obtener_limite() {
$limite = 25;
// Nombre del filtro, valor por defecto y argumentos extra opcionales.
return apply_filters( 'biblioweb_limite_resultados', $limite );
}
// Cualquier otro código puede ajustarlo sin tocar tu función:
add_filter( 'biblioweb_limite_resultados', function ( $limite ) {
return 50;
} );
Dos buenas prácticas: prefija siempre tus hooks con el nombre de tu proyecto para evitar colisiones, y documenta qué argumentos pasa cada uno. Un hook sin documentar es un hook que nadie usará.
Dónde colocar el código de tus hooks
Tienes tres opciones razonables, de menos a más recomendable según el caso:
- El functions.php de un tema hijo: válido para ajustes ligados al diseño (extractos, clases del body). Si cambias de tema, los pierdes.
- Un plugin de snippets: la opción más cómoda para la mayoría. Herramientas como WPCode, el gestor de snippets que analizamos a fondo, te permiten activar y desactivar cada fragmento por separado, con validación de sintaxis que evita tumbar la web por un punto y coma.
- Un plugin propio: lo correcto para funcionalidad que debe sobrevivir al tema: custom post types, integraciones, lógica de negocio.
Lo que nunca debes hacer es editar el functions.php del tema padre ni, por supuesto, los archivos del núcleo. Si estás empezando y aún no controlas los plugins esenciales para arrancar con WordPress, empieza por ahí antes de meterte con código.
Errores comunes al trabajar con hooks
- Olvidar el return en un filter. El síntoma típico: contenido o títulos que desaparecen. Todo filter devuelve siempre un valor.
- Engancharse demasiado pronto. Si llamas a funciones de usuario antes de
inito a funciones de plantilla antes dewp, obtendrás errores o valores vacíos. Cada hook tiene su momento en el ciclo de carga. - No declarar los argumentos. Registrar
add_action( 'save_post', 'mi_funcion', 10, 3 )con una función que solo acepta un parámetro provoca un error fatal en PHP 8. - Bucles infinitos. Llamar a
wp_update_post()dentro de un callback desave_postvuelve a dispararsave_post. Se evita quitando el hook antes de actualizar y volviéndolo a añadir después. - Callbacks anónimos que luego no puedes quitar. Las funciones anónimas son cómodas, pero
remove_filter()no puede eliminarlas fácilmente. Para código que otros deban poder desactivar, usa funciones con nombre.
Para explorar qué hooks existen y qué argumentos pasan, la referencia oficial de developer.wordpress.org sobre hooks es el mapa definitivo: incluye el listado completo de actions y filters del núcleo con su documentación.
Preguntas frecuentes
¿Cuál es la diferencia real entre un action y un filter?
Un action ejecuta código en un punto del flujo y no devuelve nada: sirve para hacer cosas. Un filter recibe un dato, puede modificarlo y está obligado a devolverlo: sirve para transformar información. Internamente ambos usan el mismo sistema (de hecho add_action() llama a add_filter()), pero conceptualmente conviene tratarlos como herramientas distintas.
¿Puedo usar hooks sin saber programar?
Puedes usar fragmentos ya escritos copiándolos en un plugin de snippets como WPCode, que valida la sintaxis antes de guardar. Ahora bien, entender al menos qué hace cada línea te evitará pegar código malicioso u obsoleto. Los hooks de WordPress son PHP: un mínimo de base te dará mucha autonomía.
¿Cuántos hooks tiene WordPress?
El núcleo supera los 3.000 entre actions y filters, y la cifra crece en cada versión. A ellos se suman los que registran tus plugins y tu tema: WooCommerce, por ejemplo, añade cientos propios. No hay que memorizarlos: basta con conocer la docena habitual y saber buscar el resto en la documentación oficial.
¿Los hooks afectan al rendimiento de mi web?
El sistema en sí es muy eficiente; lo que penaliza es lo que hagas dentro del callback. Una consulta pesada enganchada a init se ejecutará en cada carga de página. Engancha tu código al hook más específico posible y usa condicionales para salir pronto cuando no toque actuar.
Conclusión
Los hooks de WordPress son el idioma en el que se comunican el núcleo, los temas y los plugins. Dominar la pareja actions y filters te permite personalizar prácticamente cualquier comportamiento sin tocar el código fuente y sin miedo a las actualizaciones: encolar recursos, reaccionar a eventos, transformar contenido y, cuando des el salto a desarrollar, ofrecer tus propios puntos de extensión con do_action() y apply_filters(). Empieza por reproducir los ejemplos de esta guía en un entorno de pruebas o mediante un gestor de snippets, presta atención a las prioridades y al return de los filters, y consulta la documentación oficial cuando dudes de qué argumentos pasa un hook. A partir de ahí, cada problema de personalización que te encuentres en WordPress tendrá casi siempre la misma respuesta: hay un hook para eso.