Hux
Permite implementar hooks usando atributos de PHP 8 sin necesidad de archivos .module, con soporte completo de inyección de dependencias.
hux
Install
composer require 'drupal/hux:^1.6'
Overview
Hux es un módulo enfocado en desarrolladores que revoluciona cómo se implementan los hooks en Drupal. En lugar de usar funciones procedurales tradicionales en archivos .module, Hux permite a los desarrolladores implementar hooks usando atributos de PHP 8.0+ en servicios basados en clases.
Este enfoque proporciona varias ventajas significativas: soporte completo de inyección de dependencias mediante autowiring o ContainerInjectionInterface, múltiples implementaciones de hooks por módulo, la capacidad de reemplazar o sobrescribir hooks de otros módulos, y una separación de responsabilidades más limpia. Las clases de hooks se descubren automáticamente en el directorio src/Hooks de cualquier módulo, requiriendo solo una limpieza inicial de caché.
Hux se sitúa entre los hooks procedurales tradicionales y las arquitecturas completamente orientadas a eventos como hook_event_dispatcher, ofreciendo los beneficios de la inyección de dependencias y la lógica basada en clases mientras mantiene las firmas y patrones de hooks familiares.
Features
- Implementa hooks usando atributos de PHP 8 (#[Hook], #[Alter]) sin archivos .module
- Soporte completo de inyección de dependencias mediante autowiring y ContainerInjectionInterface
- Múltiples implementaciones de hooks por módulo con ordenamiento por prioridad
- Reemplaza o sobrescribe hooks de otros módulos usando #[ReplaceOriginalHook]
- Accede a la implementación original del hook como callable usando el atributo de parámetro #[OriginalInvoker]
- Un solo método puede escuchar múltiples hooks usando atributos repetibles
- Descubrimiento automático de clases de hooks en el namespace src/Hooks sin registro de servicios
- Permite hacerse pasar por un módulo diferente al implementar hooks
- Modo optimizado para entornos de producción con descubrimiento de hooks pre-cacheado
- No es necesario limpiar la caché al agregar nuevos hooks a clases de hooks existentes
Use Cases
Implementar hooks de acceso a entidades con inyección de dependencias
Crea una clase de hook que implementa lógica de acceso a entidades mientras tiene acceso a servicios inyectados como el usuario actual, entity type manager o servicios personalizados. Coloca tu clase en src/Hooks/MyModuleHooks.php con un constructor que acepta dependencias mediante autowiring.
Reemplazar la implementación de hook de un módulo contribuido
Usa #[ReplaceOriginalHook] para sobrescribir completamente cómo otro módulo implementa un hook. Por ejemplo, reemplaza media_entity_access() con tu propia lógica mientras opcionalmente llamas a la implementación original mediante #[OriginalInvoker].
Múltiples implementaciones de hooks con prioridad
Implementa el mismo hook múltiples veces dentro de un solo módulo, cada uno con diferentes prioridades. Los hooks de mayor prioridad se ejecutan primero, permitiendo lógica en capas como pre-validación, procesamiento principal y post-procesamiento.
Escuchar múltiples hooks relacionados con un solo método
Usa atributos repetibles para que un método responda a múltiples hooks como entity_insert, entity_update y entity_delete para un manejo unificado del ciclo de vida de entidades.
Separación limpia de responsabilidades en módulos grandes
Organiza los hooks en múltiples clases basadas en funcionalidad (ej. AccessHooks.php, FormHooks.php, EntityHooks.php) dentro del directorio src/Hooks, todas descubiertas automáticamente.
Probar hooks con dependencias simuladas
Dado que las implementaciones de hooks son métodos de clase regulares con inyección en el constructor, pueden probarse fácilmente mediante pruebas unitarias simulando las dependencias a través del constructor.
Tips
- Coloca las clases de hooks en el directorio src/Hooks/ de tu módulo para descubrimiento automático - no se necesita registro de servicios
- Limpia la caché solo una vez al crear una clase de hook por primera vez; los nuevos hooks agregados a clases existentes se descubren automáticamente
- Usa el modo optimizado en producción (parameters.hux.optimize: true) para mejor rendimiento
- Implementa ContainerInjectionInterface para servicios que no pueden ser autowired
- Usa el parámetro de prioridad cuando necesites controlar el orden de ejecución de múltiples implementaciones del mismo hook
- El parámetro moduleName en #[Hook] permite implementar un hook como si viniera de un módulo diferente
- Usa #[OriginalInvoker] como atributo de parámetro en lugar del parámetro originalInvoker obsoleto en #[ReplaceOriginalHook]
- hook_theme y hook_module_implements_alter no están soportados debido a cómo Drupal core los invoca
Technical Details
Hooks 2
Any Drupal hook
Hux permite implementar cualquier hook de Drupal usando el atributo #[Hook], excepto hook_theme (que no está soportado debido a cómo se invoca).
Any Drupal alter
Hux permite implementar cualquier alter hook de Drupal usando el atributo #[Alter], excepto hook_module_implements_alter.
Troubleshooting 5
Limpia la caché de Drupal con 'drush cr'. Asegúrate de que tu clase de hook esté en el directorio src/Hooks con el namespace correcto Drupal\tu_modulo\Hooks\NombreClase. Verifica que la clase tenga al menos un método con un atributo #[Hook] o #[Alter].
Algunos servicios no son autowirables. Implementa ContainerInjectionInterface en tu clase de hook e inyecta los servicios manualmente mediante el método estático create().
hook_theme no está soportado por Hux porque Drupal core no lo invoca a través de ModuleHandler. Usa el enfoque tradicional con archivo .module para hook_theme.
Habilita el modo optimizado estableciendo 'parameters.hux.optimize: true' en tu archivo services.yml. Esto pre-cachea el descubrimiento de hooks durante la compilación del contenedor.
Esto es esperado en entornos de desarrollo. Habilita el modo optimizado en producción, o ignora la advertencia si estás en un entorno de desarrollo donde quieres que los hooks se descubran en tiempo de ejecución.
Security Notes 2
- Hux decora el servicio module_handler del núcleo de Drupal, interceptando todas las invocaciones de hooks. Asegúrate de confiar en todos los módulos habilitados ya que pueden implementar o reemplazar cualquier hook.
- Al usar #[ReplaceOriginalHook], ten cuidado de no deshabilitar inadvertidamente hooks críticos para la seguridad de otros módulos sin una reimplementación adecuada.