Acciones de WordPress Explicadas: La Guía Completa del Desarrollador para la API de Hooks
Las Acciones de WordPress son un componente central de la API de Hooks que permite a los desarrolladores ejecutar funciones personalizadas en puntos precisamente definidos durante el ciclo de vida de la solicitud de WordPress — sin tocar nunca los archivos del núcleo. Cuando se activa un hook de acción, cada función registrada en ese hook se ejecuta en orden de prioridad, lo que permite una personalización modular, mantenible y segura ante actualizaciones.
Si estás creando un plugin, desarrollando un tema o gestionando una instalación de WordPress autoalojada en un entorno de VPS Hosting, dominar las Acciones no es negociable. Son el mecanismo principal mediante el cual el propio WordPress está arquitecturado — no solo una herramienta de extensión para terceros.
Cómo Funcionan Realmente las Acciones de WordPress por Dentro
WordPress mantiene un registro global llamado $wp_filter, que almacena todos los hooks registrados — tanto acciones como filtros. Cuando se llama a do_action(), WordPress busca la entrada de ese hook en $wp_filter, ordena los callbacks registrados por prioridad y los ejecuta secuencialmente.
La distinción crítica que muchos tutoriales pasan por alto: Las Acciones son de tipo “disparar y olvidar”. Ejecutan callbacks pero descartan cualquier valor de retorno. Si necesitas interceptar y modificar datos, ese es el trabajo de los Filtros, no de las Acciones. Confundir ambos es uno de los errores arquitectónicos más comunes en el desarrollo de plugins de WordPress.
El flujo de ejecución es el siguiente:
- El núcleo de WordPress (o un plugin/tema) llama a
do_action( 'hook_name', ...$args ). - WordPress resuelve todos los callbacks registrados en
hook_namedesde$wp_filter. - Los callbacks se ordenan por su valor
$priority(ascendente — los números más bajos se ejecutan primero). - Cada callback se invoca con los argumentos proporcionados.
- Los valores de retorno se ignoran silenciosamente.
- La ejecución continúa en el código de origen después de que
do_action()retorna.
Esta arquitectura significa que un callback mal escrito registrado en un hook temprano como init puede bloquear toda la solicitud. Siempre perfila los callbacks de hooks en staging antes de desplegarlos en producción.
Registrar Acciones con add_action()
La función add_action() registra un callable de PHP en un hook de acción con nombre. Su firma completa es:
add_action( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 ): trueParámetros explicados:
$hook_name — El identificador de cadena exacto del hook de acción (p. ej., wp_login, save_post).
$callback — Cualquier callable válido de PHP: una función con nombre, una función anónima, un método estático array( 'MyClass', 'method' ), o un método de objeto array( $object, 'method' ).
$priority — Orden de ejecución relativo a otros callbacks en el mismo hook. Por defecto 10. Los callbacks con la misma prioridad se ejecutan en orden de registro.
$accepted_args — Cuántos argumentos de do_action() pasar a tu callback. Por defecto 1. Debes configurar esto correctamente o tu callback recibirá listas de argumentos truncadas.
Ejemplo Básico: Engancharse en wp_login
function notify_admin_on_login( string $user_login, WP_User $user ): void {
$admin_email = get_option( 'admin_email' );
$subject = 'Login Alert: ' . $user_login;
$message = sprintf(
'User "%s" (ID: %d) logged in at %s.',
$user_login,
$user->ID,
current_time( 'mysql' )
);
wp_mail( $admin_email, $subject, $message );
}
// wp_login passes two arguments: $user_login (string) and $user (WP_User object)
add_action( 'wp_login', 'notify_admin_on_login', 10, 2 );
Observa que accepted_args está configurado en 2. Omitir esto y dejar el valor predeterminado 1 significa que tu callback recibe solo $user_login y nunca ve el objeto WP_User — un error silencioso que es notoriamente difícil de rastrear.
Usar Métodos de Objeto y Closures
El desarrollo moderno de WordPress favorece encapsular la lógica dentro de clases:
class My_Plugin {
public function __construct() {
add_action( 'init', array( $this, 'register_post_types' ) );
add_action( 'save_post', array( $this, 'handle_save' ), 20, 3 );
}
public function register_post_types(): void {
register_post_type( 'product', array( /* args */ ) );
}
public function handle_save( int $post_id, WP_Post $post, bool $update ): void {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
// Custom save logic here
}
}
new My_Plugin();
Los closures anónimos también funcionan, pero no pueden eliminarse con remove_action() posteriormente porque PHP no puede comparar de forma fiable referencias a funciones anónimas. Usa métodos con nombre cuando necesites la capacidad de desregistrar.
Eliminar Acciones con remove_action()
Puedes desregistrar cualquier callback — incluidos los añadidos por otros plugins o temas — usando remove_action(). La prioridad debe coincidir exactamente con la que se usó en add_action():
remove_action( 'wp_head', 'wp_generator' ); // Removes WordPress version meta tag
Para métodos de objeto, necesitas una referencia a la misma instancia del objeto:
// Inside a plugin that exposes its instance
global $my_plugin_instance;
remove_action( 'save_post', array( $my_plugin_instance, 'handle_save' ), 20 );
Un error común: llamar a remove_action() antes de que el add_action() original haya ejecutado. Siempre engancha tu eliminación en una acción que se active después de que el plugin objetivo se cargue — típicamente plugins_loaded o after_setup_theme.
Referencia de Hooks de Acción Principales de WordPress
La siguiente tabla cubre los hooks de acción más significativos operacionalmente, su contexto de activación y sus casos de uso prácticos.
Nombre del Hook
Se Activa Cuando
Casos de Uso Típicos
Args Pasados
muplugins_loaded
Plugins de uso obligatorio cargados
Bootstrapping temprano, constantes
0
plugins_loaded
Todos los plugins cargados
Verificaciones de compatibilidad entre plugins
0
init
Después de que WP carga, antes de las cabeceras
Registrar CPTs, taxonomías, reglas de reescritura
0
wp_loaded
Después de init, todo cargado
Registro de REST API, tareas de inicio tardío
0
wp_enqueue_scripts
Carga de recursos del front-end
Encolar CSS/JS para temas y plugins
0
wp_head
Dentro de la etiqueta <head>
Meta etiquetas, estilos en línea, fragmentos de analítica
0
wp_footer
Antes de </body>
Scripts diferidos, píxeles de seguimiento
0
admin_init
Cargas de páginas de administración
Registro de Settings API, verificaciones de capacidad
0
admin_enqueue_scripts
Carga de recursos de administración
Encolar CSS/JS solo para administración
1 ($hook_suffix)
save_post
Post guardado o actualizado
Actualizaciones de meta, sincronización con API externa, notificaciones
3
wp_login
Usuario se autentica
Registro de auditoría, gestión de sesiones
2
user_register
Nuevo usuario creado
Correos de bienvenida, sincronización con CRM, asignación de roles
1 ($user_id)
wp_logout
Usuario cierra sesión
Limpieza de sesión, eventos de analítica
1 ($user_id)
switch_theme
Cambio de tema activo
Purga de caché, migración de opciones
2
wp_trash_post
Post movido a la papelera
Limpieza de datos relacionados, sincronización externa
1 ($post_id)
rest_api_init
REST API se inicializa
Registrar rutas REST personalizadas
0
template_redirect
Antes de que cargue la plantilla
Redirecciones, control de acceso
0
do_action() vs do_action_ref_array(): Cuándo Aplica Cada Uno
Ambas funciones activan un hook de acción, pero difieren en cómo se pasan los argumentos.
do_action()
La función estándar. Los argumentos se pasan por valor — los callbacks reciben copias.
do_action( 'my_plugin_after_import', $import_id, $result_count, $errors );
do_action_ref_array()
Los argumentos se pasan como un array, y los objetos dentro de ese array se pasan por referencia (los objetos PHP siempre son similares a referencias por defecto desde PHP 5). Esto es principalmente útil para compatibilidad con código heredado o cuando necesitas explícitamente que los callbacks muten una estructura de datos compartida.
$context = array(
'post_id' => 42,
'meta' => array(),
);
do_action_ref_array( 'my_plugin_process_context', array( &$context ) );
// $context['meta'] may now be populated by hooked callbacks
Orientación práctica: En PHP moderno (7.4+), la diferencia de comportamiento entre los dos es mínima para objetos. Usa do_action() por defecto. Recurre a do_action_ref_array() solo cuando integres con código heredado que lo espera explícitamente, o cuando necesites que los callbacks muten un valor primitivo (como un entero) pasado por referencia.
Crear y Documentar Hooks de Acción Personalizados
Al crear un plugin o tema destinado a ser extendido por otros, debes definir tus propios hooks de acción. Así es como expones una superficie de API sin dar a terceros acceso directo a tus elementos internos.
/**
* Fires after a custom import process completes.
*
* @since 1.0.0
*
* @param int $import_id The ID of the completed import batch.
* @param int $record_count Total number of records processed.
* @param array $errors Array of WP_Error objects, empty on full success.
*/
do_action( 'my_plugin_import_complete', $import_id, $record_count, $errors );
Siempre documenta tus hooks con un DocBlock directamente encima de do_action(). Esto es lo que impulsa el generador de documentación para desarrolladores de WordPress y hace que tu plugin sea de nivel profesional. Los desarrolladores de terceros pueden entonces engancharse limpiamente:
add_action( 'my_plugin_import_complete', function( int $import_id, int $count, array $errors ) {
if ( ! empty( $errors ) ) {
// Log errors to an external monitoring service
error_log( "Import {$import_id} completed with " . count( $errors ) . ' errors.' );
}
}, 10, 3 );
Acciones vs Filtros: Una Comparación Definitiva
Esta es la distinción conceptual más importante en toda la API de Hooks de WordPress.
Dimensión
Acciones
Filtros
Propósito principal
Ejecutar efectos secundarios en un punto del tiempo
Interceptar y modificar datos
Valor de retorno
Ignorado completamente
Debe retornar el valor (modificado)
Función principal
do_action()
apply_filters()
Registro
add_action()
add_filter()
Uso típico
Enviar correo, escribir en DB, encolar recursos
Modificar contenido de posts, alterar argumentos de consulta
¿Puede cortocircuitar?
No (todos los callbacks siempre se ejecutan)
No (pero puede retornar anticipadamente dentro del callback)
Mutación de datos
No es el patrón previsto
Propósito principal
Regla arquitectónica clave: Si estás usando add_filter() pero no retornas un valor desde tu callback, has introducido un error — el valor filtrado se convertirá en null o false dependiendo del contexto. Por el contrario, si estás usando add_action() y dependes de un valor de retorno, estás haciendo un uso incorrecto de la API.
Conflictos de Prioridad y Orden de Ejecución
La gestión de prioridades se vuelve crítica en ecosistemas de plugins complejos. Considera un escenario donde WooCommerce, un plugin de caché y tu código personalizado se enganchan en save_post:
// WooCommerce hooks at priority 10 (default)
// Your inventory sync needs WooCommerce data to already be saved
add_action( 'save_post_product', 'sync_inventory_to_erp', 99 );
// A cache purge should happen last, after all data is written
add_action( 'save_post', 'purge_varnish_cache', 9999 );
Casos extremos que debes conocer:
Las prioridades negativas son válidas en WordPress. add_action( 'init', 'my_func', -1 ) se activa antes que cualquier cosa en prioridad 0 o 10.
Misma prioridad, múltiples callbacks — se ejecutan en el orden en que se llamó a add_action(). El orden de carga de plugins (determinado alfabéticamente por nombre de archivo, o plugins Plugin Order) por tanto afecta el comportamiento.
Hooks recursivos — llamar a do_action() para el mismo hook dentro de un callback de ese hook es técnicamente posible pero crea bucles infinitos. WordPress no protege contra esto.
Registro tardío de hooks — si llamas a add_action( 'init', ... ) después de que init ya se haya activado, tu callback nunca se ejecutará en esa solicitud. Esta es una fuente frecuente de errores en código que se ejecuta condicionalmente.
Patrones de Producción del Mundo Real
Patrón 1: Sistema de Eventos Desacoplado
Usa acciones personalizadas para desacoplar componentes de plugins, evitando llamadas directas a funciones entre módulos:
// In your order processing module
do_action( 'my_shop_order_paid', $order_id, $payment_method, $amount );
// In your email module (separate file, no direct dependency)
add_action( 'my_shop_order_paid', 'send_payment_confirmation_email', 10, 3 );
// In your analytics module (separate file, no direct dependency)
add_action( 'my_shop_order_paid', 'track_conversion_event', 20, 3 );
Este patrón significa que puedes deshabilitar el módulo de analítica completamente sin tocar el código de procesamiento de pedidos.
Patrón 2: Registro Condicional de Hooks
Evita registrar hooks incondicionalmente en el nivel superior. Limítalos al contexto donde son necesarios:
add_action( 'admin_init', function() {
// Only register these hooks in the admin context
add_action( 'admin_enqueue_scripts', 'my_plugin_admin_assets' );
add_action( 'save_post', 'my_plugin_validate_custom_fields', 10, 2 );
} );
Patrón 3: Acciones de Una Sola Vez con remove_action() Dentro del Callback
function my_plugin_run_once(): void {
// Do something that must only happen once per request
update_option( 'my_plugin_initialized', true );
// Deregister itself to prevent re-execution if the hook fires again
remove_action( 'wp_loaded', 'my_plugin_run_once' );
}
add_action( 'wp_loaded', 'my_plugin_run_once' );
Consideraciones de Rendimiento en Entornos Gestionados
En instalaciones de WordPress con alto tráfico — ya sea ejecutándose en un plan de VPS Hosting o en un Servidor Dedicado — el costo acumulativo de hooks mal optimizados es medible.
Herramientas de perfilado a usar:
Plugin Query Monitor: Muestra cada hook que se activó, cada callback que se ejecutó y el tiempo de ejecución por callback.
Xdebug + KCacheGrind: Para perfilado profundo de rutas de ejecución de callbacks.
New Relic APM: Para monitoreo de rendimiento de hooks a nivel de producción.
Principios de optimización:
Mueve las operaciones costosas (consultas a base de datos, solicitudes HTTP, E/S de archivos) fuera de hooks que se activan en cada carga de página (init, wp_head) y hacia hooks que se activan solo cuando es necesario (save_post, user_register).
Usa transients o caché de objetos para memoizar resultados de cálculos costosos dentro de callbacks de hooks.
Evita registrar cientos de llamadas add_action() incondicionalmente en el momento de carga del plugin. Registra hooks de forma diferida solo cuando la página de administración o el contexto relevante esté activo.
En servidores con OPcache habilitado (que todos los entornos PHP correctamente configurados deberían tener), la sobrecarga del registro de hooks en sí es insignificante — el costo está en lo que los callbacks *hacen*, no en el registro.
Si gestionas tu propio stack de WordPress, asegurarte de que tu servidor ejecute PHP 8.1+ con OPcache, una caché de bytecode y una caché de objetos como Redis o Memcached tendrá un impacto en el rendimiento mucho mayor que micro-optimizar las prioridades de los hooks.
Acciones de WordPress en el Contexto del Desarrollo de Plugins y Temas
Al desarrollar un plugin destinado al repositorio de WordPress.org o distribución comercial, tu uso de la API de Hooks determina directamente la calidad del código y las calificaciones de compatibilidad.
Mejores prácticas para el uso de hooks de nivel de producción:
Siempre verifica DOING_AUTOSAVE dentro de los callbacks de save_post para evitar ejecutar lógica costosa en autoguardados.
Verifica nonces y capacidades dentro de cualquier callback de hook que procese datos enviados por el usuario. Nunca confíes en que el hook en sí proporciona seguridad.
Usa current_action() para determinar qué hook específico activó tu callback cuando la misma función está registrada en múltiples hooks.
Usa espacios de nombres en tus nombres de hooks personalizados para evitar colisiones: my_plugin_event_name, no event_name.
Versiona tus hooks en la documentación para que los desarrolladores sepan cuándo se introdujo un hook y cuándo puede quedar obsoleto.
Para equipos que despliegan WordPress en infraestructura con Paneles de Control VPS, mantener entornos de staging que reflejen la producción es esencial para probar de forma segura las interacciones de hooks antes del despliegue.
Asegurar tu Instalación de WordPress Junto con Acciones Personalizadas
Los hooks de acción personalizados que procesan datos externos, gestionan eventos de autenticación o interactúan con el sistema de archivos introducen superficie de ataque de seguridad. Combina el desarrollo de tus hooks con un entorno de alojamiento correctamente asegurado.
Asegúrate de que tu instalación de WordPress use HTTPS — un Certificado SSL es obligatorio para cualquier sitio que gestione autenticación de usuarios o envíos de formularios. Los callbacks de hooks que se activan en wp_login o user_register están procesando datos sensibles a través de la red; sin TLS, esos datos están expuestos independientemente de lo bien que esté escrito tu código PHP.
Además, si tu plugin usa wp_mail() dentro de callbacks de acción (un patrón común para notificaciones), la configuración y reputación del correo de tu servidor afectan directamente la entregabilidad. Considera una solución dedicada de Alojamiento de Correo con registros SPF, DKIM y DMARC adecuados en lugar de depender del sendmail a nivel de servidor.
Matriz de Decisión Técnica y Conclusiones Clave
Usa esta lista de verificación al implementar Acciones de WordPress en cualquier proyecto:
Registro de hooks:
Establece $accepted_args explícitamente siempre que el hook pase más de un argumento
Usa valores de prioridad deliberadamente — documenta por qué se eligen prioridades no predeterminadas
Registra hooks dentro de constructores de clases o métodos register_hooks() dedicados, nunca en el ámbito global de un archivo de plugin
Limita el registro de hooks al contexto donde es necesario (administración, front-end, REST API)
Implementación de callbacks:
Siempre verifica DOING_AUTOSAVE, DOING_CRON y wp_is_json_request() donde sea relevante
Verifica nonces con check_admin_referer() o check_ajax_referer() antes de procesar cualquier entrada del usuario
Nunca dependas de valores de retorno de do_action() — usa filtros si necesitas datos de vuelta
Usa current_action() cuando un callback sirve a múltiples hooks
Diseño de hooks personalizados:
Usa espacios de nombres en todos los nombres de hooks personalizados con el prefijo de tu plugin/tema
Documenta cada hook personalizado con un DocBlock completo encima de do_action()do_action() sobre do_action_ref_array() a menos que tengas una razón específicaRendimiento y mantenimiento:
- Perfila los callbacks de hooks con Query Monitor antes de desplegar en producción
- Evita consultas a base de datos dentro de hooks que se activan en cada solicitud
- Usa
remove_action()para limpiar hooks de terceros que entren en conflicto con tu implementación - Prueba las interacciones de hooks en un entorno de staging que refleje la infraestructura de producción
Preguntas Frecuentes
¿Cuál es la diferencia entre add_action() y add_filter() en WordPress?
add_action() registra un callback para ejecutar efectos secundarios en un punto específico de la ejecución — el valor de retorno se descarta. add_filter() registra un callback para recibir, modificar y retornar un valor. Usar uno donde el otro es apropiado es un error funcional: un callback de filtro que no retorna un valor anulará los datos filtrados.
¿Por qué no se activa mi callback de add_action()?
Las causas más comunes son: (1) el nombre del hook está mal escrito, (2) add_action() se llama después de que el hook ya se ha activado en la solicitud actual, (3) $accepted_args es demasiado bajo y el callback recibe datos incorrectos causando un error silencioso de PHP, o (4) una verificación condicional dentro del callback está impidiendo la ejecución. Usa Query Monitor para verificar si el hook se activó y si tu callback fue registrado.
¿Puedo usar add_action() dentro de otro callback de acción?
Sí, y este es un patrón común. Por ejemplo, registrar hooks dentro de plugins_loaded o init para asegurar que las dependencias estén disponibles. Sin embargo, el hook interno debe activarse después del externo para que el registro surta efecto.
¿Qué controla realmente el parámetro $priority?
Controla el orden en que se ejecutan múltiples callbacks registrados en el mismo hook. Los números más bajos se activan primero. El valor predeterminado es 10. Los valores válidos incluyen enteros negativos. Cuando dos callbacks comparten la misma prioridad, se ejecutan en el orden en que fueron registrados mediante add_action().
¿Cómo elimino de forma segura una acción añadida por un plugin de terceros?
Engancha tu llamada a remove_action() en una acción que se active después de que el plugin se haya cargado — típicamente plugins_loaded con alta prioridad, o after_setup_theme. Debes coincidir exactamente con el nombre del hook, la referencia del callback y la prioridad usada en la llamada original a add_action(). Para métodos de objeto, necesitas acceso a la misma instancia del objeto, que los plugins de buena reputación exponen a través de una variable global o un método estático get_instance().
