15%

Ahorra 15%<\/span> en todos los servicios de hosting

Pon a prueba tus habilidades y obtén Descuento<\/span> en cualquier plan de hosting

Usa el código:

Skills
Comenzar
21.10.2024

Hooks de WordPress Explicados: Actions, Filters y Patrones de Uso Avanzado

Los hooks de WordPress son un mecanismo arquitectónico central que permite a los desarrolladores inyectar código personalizado en puntos de ejecución predefinidos dentro de WordPress — sin modificar los archivos del núcleo, temas o plugins de terceros. Existen exactamente dos tipos: action hooks, que activan funciones personalizadas en eventos específicos, y filter hooks, que interceptan y transforman datos antes de que se rendericen o persistan. Dominar ambos es imprescindible para cualquier trabajo serio de desarrollo en WordPress.

Esta guía va más allá de lo básico. Encontrará referencias de sintaxis precisas, casos extremos del mundo real, mecánicas de prioridad y patrones arquitectónicos que separan el código WordPress mantenible de los hacks frágiles y propensos a conflictos.

El sistema de hooks de WordPress: cómo funciona internamente

WordPress se ejecuta en una secuencia predecible — arrancando el núcleo, cargando plugins, cargando el tema activo y luego renderizando la página solicitada. A lo largo de este ciclo de vida, el motor llama a do_action() y apply_filters() en cientos de puntos predefinidos. Estas llamadas son los hooks.

Cuando registra un callback con add_action() o add_filter(), WordPress lo almacena en un array global $wp_filter, indexado por nombre de hook y prioridad. En tiempo de ejecución, cuando el hook se activa, WordPress itera a través de cada callback registrado en orden de prioridad y los ejecuta secuencialmente.

Esta arquitectura significa:

  • Nunca toca los archivos del núcleo de WordPress (wp-includes/, wp-admin/)
  • Sus personalizaciones sobreviven intactas a las actualizaciones del núcleo
  • Múltiples plugins pueden adjuntarse al mismo hook sin conflictos — siempre que las prioridades se gestionen correctamente

Todos los registros de hooks deben residir en un plugin personalizado o en el functions.php de su tema. Para entornos de producción que se ejecutan en un plan de VPS Hosting, se prefiere firmemente implementar las personalizaciones como un plugin independiente en lugar de functions.php, porque un cambio de tema no borrará silenciosamente su funcionalidad.

Action hooks vs. filter hooks: diferencias fundamentales

AtributoAction HooksFilter Hooks
Propósito principalEjecutar efectos secundarios en un evento específicoInterceptar y transformar datos
Valor de retorno requeridoNo — los callbacks no devuelven nadaSí — los callbacks DEBEN devolver un valor
Función del núcleo para activar`do_action()``apply_filters()`
Función del núcleo para registrar`add_action()``add_filter()`
Función de eliminación`remove_action()``remove_filter()`
Casos de uso típicosEncolar scripts, enviar correos, registrar eventosModificar contenido, alterar títulos, transformar argumentos de consulta
Datos pasados al callbackArgumentos contextuales opcionalesEl valor de datos que se está filtrando (obligatorio)
Comportamiento de encadenamientoLos callbacks se ejecutan en secuencia, de forma independienteCada callback recibe la salida del anterior

El error más común que cometen los desarrolladores es olvidar return un valor dentro de un callback de filtro. Si omite la declaración de retorno, el valor filtrado se convierte en null, lo que romperá silenciosamente la salida en el front end — un error notoriamente difícil de rastrear.

Action hooks: análisis en profundidad

Sintaxis y parámetros

add_action( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );
  • $hook_name — El nombre exacto del hook al que adjuntarse.
  • $callback — Cualquier callable PHP válido: una función con nombre, una función anónima, un método estático (['ClassName', 'method']) o un método de objeto ([$object, 'method']).
  • $priority — Orden de ejecución relativo a otros callbacks en el mismo hook. Los números más bajos se ejecutan primero. El valor predeterminado es 10. Use enteros negativos para ejecutarse antes que todos los callbacks predeterminados.
  • $accepted_args — Cuántos argumentos aceptará su callback del hook. Debe coincidir con lo que pasa do_action(), o recibirá una advertencia de PHP.

Ejemplo básico: agregar contenido después de cada publicación

add_action( 'the_content', 'alexhost_append_cta', 20 );

function alexhost_append_cta( $content ) {
    if ( is_single() && in_the_loop() && is_main_query() ) {
        $content .= '<p class="post-cta">Enjoyed this article? Share it with your network.</p>';
    }
    return $content;
}

Observe las protecciones in_the_loop() y is_main_query(). Sin ellas, su callback se activa en cada llamada a the_content() — incluidas las áreas de widgets, los constructores de páginas y las respuestas de la REST API — produciendo una salida duplicada que es extremadamente difícil de depurar.

Ejemplo avanzado: enviar una notificación a Slack al publicar una entrada

add_action( 'transition_post_status', 'alexhost_notify_on_publish', 10, 3 );

function alexhost_notify_on_publish( $new_status, $old_status, $post ) {
    if ( 'publish' === $new_status && 'publish' !== $old_status && 'post' === $post->post_type ) {
        $webhook_url = defined( 'SLACK_WEBHOOK_URL' ) ? SLACK_WEBHOOK_URL : '';
        if ( empty( $webhook_url ) ) {
            return;
        }
        wp_remote_post( $webhook_url, [
            'body'        => wp_json_encode( [ 'text' => 'New post published: ' . get_permalink( $post ) ] ),
            'headers'     => [ 'Content-Type' => 'application/json' ],
            'data_format' => 'body',
        ] );
    }
}

Este patrón usa transition_post_status en lugar de publish_post porque le proporciona tanto el estado antiguo como el nuevo, lo que le permite distinguir una primera publicación de una actualización de una entrada ya publicada.

Eliminar una acción registrada por otro plugin

remove_action( 'wp_footer', 'some_plugin_footer_function', 10 );

El valor de prioridad en remove_action() debe coincidir exactamente con la prioridad utilizada en la llamada original a add_action(). Si no conoce la prioridad, inspeccione el código fuente del plugin o use una herramienta de depuración de hooks. Una discrepancia significa que la eliminación falla silenciosamente — la función sigue ejecutándose.

Filter hooks: análisis en profundidad

Sintaxis y parámetros

add_filter( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );

La firma es idéntica a add_action(). La diferencia de comportamiento crítica: su callback recibe el valor actual de los datos filtrados como primer argumento y debe devolver un valor.

Ejemplo básico: convertir títulos de entradas a formato de título

add_filter( 'the_title', 'alexhost_titlecase_post_title', 10, 2 );

function alexhost_titlecase_post_title( $title, $post_id ) {
    if ( is_admin() ) {
        return $title;
    }
    return mb_convert_case( $title, MB_CASE_TITLE, 'UTF-8' );
}

Usar mb_convert_case() en lugar de strtoupper() es el enfoque correcto para sitios multilingües. strtoupper() no es seguro para multibyte y corromperá caracteres en scripts no latinos.

Ejemplo avanzado: modificar los argumentos de la consulta principal

add_filter( 'pre_get_posts', 'alexhost_exclude_category_from_home' );

function alexhost_exclude_category_from_home( $query ) {
    if ( ! is_admin() && $query->is_main_query() && $query->is_home() ) {
        $query->set( 'category__not_in', [ 5, 12 ] );
    }
}

pre_get_posts es técnicamente un action hook (no requiere retorno), pero modifica el objeto WP_Query por referencia — haciéndolo comportarse como un filtro. Este es un punto común de confusión. Modifica $query directamente; no lo devuelve.

Encadenamiento de filtros: lo que los desarrolladores pasan por alto

Cuando múltiples callbacks se adjuntan al mismo filtro, cada uno recibe la salida del anterior. Si el callback A en la prioridad 10 transforma $content y el callback B en la prioridad 11 también transforma $content, B opera sobre la salida de A — no sobre el original. Este encadenamiento es poderoso pero requiere una planificación deliberada de prioridades cuando múltiples plugins tocan los mismos datos.

Prioridad y orden de ejecución: referencia práctica

Valor de prioridadCuándo se ejecutaCaso de uso típico
`1` – `9`Antes de los valores predeterminados de WordPressAnular el comportamiento del núcleo de forma temprana
`10`PredeterminadoPersonalizaciones estándar de plugins/temas
`11` – `19`Después del predeterminado, antes de los hooks tardíosPosprocesar la salida de otro plugin
`20` – `99`Ejecución tardíaLimpieza, formato final
`PHP_INT_MAX`Absolutamente el últimoEjecución de último recurso garantizada
Negativo (p. ej., `-1`)Antes que todoTareas de preinicialización

Referencia de hooks esenciales de WordPress

Action hooks de alto valor

  • init — Se activa después de que WordPress se carga pero antes de que se envíen las cabeceras. Úselo para registrar tipos de entradas personalizados, taxonomías y reglas de reescritura. Evite usar plugins_loaded para el registro de CPT — se activa demasiado pronto.
  • wp_enqueue_scripts — El único lugar correcto para encolar CSS y JavaScript del front end. Nunca use wp_head directamente para la inyección de scripts.
  • admin_enqueue_scripts — Encole recursos exclusivamente en el panel de administración. Acepta un argumento $hook_suffix para apuntar a páginas de administración específicas.
  • wp_footer — Se activa justo antes de </body>. Ideal para fragmentos de análisis, scripts diferidos y marcado no crítico.
  • save_post — Se activa después de guardar una entrada. Úselo para activar la invalidación de caché, sincronizar datos con APIs externas o actualizar meta personalizado. Siempre verifique el nonce y compruebe wp_is_post_revision() para evitar doble activación.
  • template_redirect — Se activa antes de que WordPress determine qué plantilla cargar. Úselo para redirecciones personalizadas o control de acceso.
  • wp_login — Se activa en el inicio de sesión exitoso de un usuario. Útil para el registro de auditoría o la gestión de sesiones en sitios multiusuario.

Filter hooks de alto valor

  • the_content — Filtra el contenido de las entradas antes de mostrarlo. Tenga en cuenta: este hook se activa en cada llamada a get_the_content(), incluidas las respuestas de la REST API en WordPress 5.5+.
  • the_title — Filtra los títulos de entradas y páginas. Recibe tanto $title como $post_id como argumentos cuando $accepted_args está configurado en 2.
  • excerpt_length — Controla el recuento de palabras de los extractos generados automáticamente. Devuelve un entero.
  • upload_mimes — Filtra la lista de tipos MIME de carga permitidos. Úselo para habilitar cargas SVG (con la sanitización adecuada) o restringir las cargas a tipos de archivo específicos.
  • wp_nav_menu_items — Filtra la salida HTML de los menús de navegación. Útil para inyectar elementos dinámicos como enlaces de inicio/cierre de sesión.
  • body_class — Filtra el array de clases CSS aplicadas a la etiqueta <body>. Acepta un array, no una cadena — una fuente frecuente de errores.
  • cron_schedules — Agrega intervalos personalizados de WP-Cron. Esencial para tareas de procesamiento en segundo plano en sitios alojados en Servidores Dedicados donde también puede configurar un cron del sistema real como reemplazo.

Creación de hooks personalizados en plugins y temas

Los plugins bien arquitecturados exponen sus propios hooks para que otros desarrolladores puedan extenderlos sin bifurcar el código. Esta es la característica distintiva del desarrollo WordPress de nivel profesional.

Definir un action hook personalizado

// Inside your plugin's core function
function alexhost_process_order( $order_id ) {
    // ... processing logic ...

    // Fire a custom action so other code can react
    do_action( 'alexhost_order_processed', $order_id );
}

Definir un filter hook personalizado

function alexhost_get_product_price( $product_id ) {
    $base_price = get_post_meta( $product_id, '_price', true );

    // Allow other code to modify the price before returning it
    return apply_filters( 'alexhost_product_price', $base_price, $product_id );
}

Cualquier plugin o tema puede ahora engancharse a alexhost_product_price para aplicar descuentos, conversión de moneda o cálculos de impuestos — sin tocar el código fuente de su plugin.

Eliminar y reemplazar hooks: patrones avanzados

Eliminar un hook registrado dentro de una clase

Este es uno de los aspectos más incomprendidos del sistema de hooks. Si un plugin registra un método usando una instancia de objeto, no puede eliminarlo con una simple referencia de cadena.

// Plugin registers like this:
$plugin_instance = new SomePlugin();
add_action( 'init', [ $plugin_instance, 'setup' ] );

// To remove it, you need access to the same object instance.
// One approach: hook into plugins_loaded and use the global instance if exposed.
add_action( 'plugins_loaded', function() {
    global $some_plugin;
    if ( isset( $some_plugin ) && is_a( $some_plugin, 'SomePlugin' ) ) {
        remove_action( 'init', [ $some_plugin, 'setup' ] );
    }
}, 20 );

Si el plugin no expone su instancia globalmente, debe iterar directamente sobre $GLOBALS['wp_filter'] — un enfoque frágil que indica que el plugin objetivo tiene una arquitectura deficiente.

Usar has_action() y has_filter() de forma defensiva

if ( has_action( 'wp_footer', 'some_third_party_function' ) ) {
    remove_action( 'wp_footer', 'some_third_party_function' );
}

has_action() devuelve la prioridad del callback registrado (un entero) si se encuentra, o false si no. Este valor de retorno se usa con frecuencia de forma incorrecta — los desarrolladores comprueban if ( has_action(...) ) esperando un booleano, pero recibir 0 (una prioridad válida) se evalúa como falso. Use siempre !== false para una comprobación confiable:

if ( false !== has_action( 'wp_footer', 'some_third_party_function' ) ) {
    remove_action( 'wp_footer', 'some_third_party_function', 0 );
}

Consideraciones de rendimiento para entornos de producción

Los hooks añaden una sobrecarga mínima individualmente, pero los callbacks mal escritos se acumulan en una latencia medible. Patrones clave a seguir:

  • Proteja las operaciones costosas con condicionales. Las consultas a la base de datos, las llamadas a APIs remotas y las operaciones de E/S de archivos dentro de los callbacks de hooks deben estar envueltas en comprobaciones condicionales (is_single(), is_admin(), is_main_query()) para evitar que se ejecuten en cada carga de página.
  • Use caché de objetos. Si un callback de hook obtiene datos de la base de datos, envuelva el resultado en un transitorio o use wp_cache_get() / wp_cache_set(). En un VPS con cPanel correctamente configurado o en un servidor que ejecute Redis, esto reduce drásticamente los viajes de ida y vuelta a la base de datos.
  • Evite las funciones anónimas cuando necesite eliminar hooks. No puede llamar a remove_action() en una función anónima porque no tiene referencia a ella. Use siempre funciones con nombre o referencias almacenadas para los callbacks que pueda necesitar desregistrar.
  • Audite la carga de hooks con Query Monitor. El plugin Query Monitor proporciona un panel dedicado de “Hooks & Actions” que muestra cada hook que se activó durante una solicitud, los callbacks adjuntos y su tiempo de ejecución. Esto es indispensable para diagnosticar regresiones de rendimiento en sitios de alto tráfico.

Consideraciones de seguridad

Los hooks son una superficie de ataque común en los plugins mal escritos. Riesgos específicos a comprender:

  • Entrada no validada en callbacks save_post. Siempre verifique el nonce (check_admin_referer()), confirme current_user_can() y sanitice todos los datos de $_POST antes de procesarlos.
  • Escalada de privilegios a través de hooks init. El código que modifica roles o capacidades de usuario dentro de init sin una comprobación de capacidad puede ser activado por solicitudes no autenticadas.
  • Inyección de filtros. Si un callback de filtro genera datos directamente en la página sin escaparlos, se convierte en un vector XSS. Los filtros deben transformar datos; el escapado debe ocurrir en el punto de salida usando esc_html(), esc_attr() o wp_kses_post().
  • SSRF a través de solicitudes HTTP activadas por hooks. Los callbacks que realizan llamadas wp_remote_get() basadas en URLs proporcionadas por el usuario (p. ej., en save_post) deben validar y sanitizar la URL con esc_url_raw() e idealmente restringir los hosts permitidos.

Para sitios que manejan datos sensibles o transacciones de comercio electrónico, combinar su instalación de WordPress con una configuración adecuada de Certificados SSL es un requisito básico — los hooks que transmiten datos a endpoints externos a través de conexiones no cifradas son una vulnerabilidad crítica.

Lista de verificación de mejores prácticas

  • Use nombres de función únicos con espacio de nombres (p. ej., myplugin_functionname) para evitar colisiones con el núcleo, los temas y otros plugins.
  • Especifique siempre $accepted_args cuando su callback necesite más de un argumento del hook.
  • Nunca use echo dentro de un callback de filtro — solo return.
  • Coloque los registros de hooks dentro de una comprobación condicional o función de inicialización, no en el ámbito global de un archivo que pueda incluirse varias veces.
  • Documente cada hook personalizado que exponga con bloques de documentación @hook para que otros desarrolladores puedan descubrirlos.
  • Pruebe la eliminación de hooks con coincidencia exacta de prioridad — una discrepancia es un fallo silencioso.
  • Use current_filter() dentro de un callback para confirmar qué hook lo activó cuando una sola función está adjunta a múltiples hooks.

Matriz de decisión práctica: cuándo usar cada tipo de hook

EscenarioTipo de hookHook recomendado
Agregar píxel de seguimiento antes de `</body>`Action`wp_footer`
Modificar el contenido de la entrada antes de mostrarloFilter`the_content`
Registrar un tipo de entrada personalizadoAction`init`
Restringir tipos de archivos de cargaFilter`upload_mimes`
Enviar correo electrónico cuando se completa un pedidoActionAcción personalizada en la función de procesamiento de pedidos
Cambiar el recuento de palabras del extractoFilter`excerpt_length`
Redirigir usuarios no conectadosAction`template_redirect`
Agregar clase CSS a la etiqueta bodyFilter`body_class`
Encolar una hoja de estilos personalizadaAction`wp_enqueue_scripts`
Modificar WP_Query antes de la ejecuciónAction (por referencia)`pre_get_posts`

Preguntas frecuentes

¿Cuál es la diferencia entre do_action() y apply_filters() en WordPress?

do_action() activa un action hook — ejecuta todos los callbacks registrados en ese punto pero no devuelve un valor de retorno al código que lo llama. apply_filters() activa un filter hook — pasa un valor a través de todos los callbacks registrados en secuencia y devuelve el valor final transformado al llamador. Las acciones producen efectos secundarios; los filtros transforman datos.

¿Puede un filter hook de WordPress usarse como un action hook?

Técnicamente, add_action() es un envoltorio alrededor de add_filter() en el núcleo de WordPress. Sin embargo, usar un filter hook como una acción (sin devolver un valor) hará que el valor filtrado se convierta en null, rompiendo los datos que se estaban procesando. Use siempre la función semánticamente correcta para el propósito previsto.

¿Por qué remove_action() a veces falla al eliminar un hook?

La causa más común es una discrepancia de prioridad — la prioridad pasada a remove_action() debe coincidir exactamente con la prioridad utilizada en la llamada original a add_action(). La segunda causa común es el momento: remove_action() debe llamarse después de que el hook haya sido registrado pero antes de que se active. Si el registro original ocurre dentro de un constructor de clase o un hook de activación tardía, su llamada de eliminación puede ejecutarse demasiado pronto.

¿Cuál es el lugar más seguro para agregar hooks personalizados de WordPress en un entorno de producción?

Un plugin independiente y de propósito específico es la ubicación más segura. A diferencia de functions.php, un plugin persiste a través de los cambios de tema y es más fácil de controlar por versiones, probar e implementar de forma independiente. En entornos de VPS Hosting gestionados, almacenar plugins personalizados en un repositorio Git privado e implementarlos a través de pipelines CI/CD es el estándar de nivel de producción.

¿Cómo depuro qué hooks se están activando en una página específica de WordPress?

Instale el plugin Query Monitor y navegue a la página de destino mientras está conectado como administrador. La pestaña “Hooks & Actions” lista cada hook que se activó, cada callback adjunto y el tiempo de ejecución por callback. Para la depuración basada en CLI en un servidor, wp hook list --format=table a través de WP-CLI proporciona un inventario estático de todos los hooks registrados sin cargar un navegador.

15%

Ahorra 15%<\/span> en todos los servicios de hosting

Pon a prueba tus habilidades y obtén Descuento<\/span> en cualquier plan de hosting

Usa el código:

Skills
Comenzar