Hux
Enables hook implementations using PHP 8 attributes without requiring .module files, with full dependency injection support.
hux
Install
composer require 'drupal/hux:^1.6'
Overview
Hux is a developer-focused module that revolutionizes how Drupal hooks are implemented. Instead of using traditional procedural functions in .module files, Hux allows developers to implement hooks using PHP 8.0+ attributes in class-based services.
This approach provides several significant advantages: full dependency injection support through autowiring or ContainerInjectionInterface, multiple hook implementations per module, the ability to replace or override hooks from other modules, and cleaner separation of concerns. Hook classes are automatically discovered in the src/Hooks directory of any module, requiring only an initial cache clear.
Hux sits between traditional procedural hooks and full event-driven architectures like hook_event_dispatcher, offering the benefits of dependency injection and class-based logic while maintaining familiar hook signatures and patterns.
Features
- Implement hooks using PHP 8 attributes (#[Hook], #[Alter]) without .module files
- Full dependency injection support via autowiring and ContainerInjectionInterface
- Multiple hook implementations per module with priority ordering
- Replace or override hooks from other modules using #[ReplaceOriginalHook]
- Access original hook implementation as callable using #[OriginalInvoker] parameter attribute
- Single method can listen to multiple hooks using repeatable attributes
- Automatic discovery of hook classes in src/Hooks namespace without service registration
- Masquerade as a different module when implementing hooks
- Optimized mode for production environments with pre-cached hook discovery
- No need to clear cache when adding new hooks to existing hook classes
Use Cases
Implementing entity access hooks with dependency injection
Create a hook class that implements entity access logic while having access to injected services like the current user, entity type manager, or custom services. Place your class in src/Hooks/MyModuleHooks.php with a constructor that accepts dependencies via autowiring.
Replacing a contributed module's hook implementation
Use #[ReplaceOriginalHook] to completely override how another module implements a hook. For example, replace media_entity_access() with your own logic while optionally calling the original implementation via #[OriginalInvoker].
Multiple hook implementations with priority
Implement the same hook multiple times within a single module, each with different priorities. Higher priority hooks execute first, allowing for layered logic like pre-validation, main processing, and post-processing.
Listening to multiple related hooks with single method
Use repeatable attributes to have one method respond to multiple hooks like entity_insert, entity_update, and entity_delete for unified entity lifecycle handling.
Clean separation of concerns in large modules
Organize hooks into multiple classes based on functionality (e.g., AccessHooks.php, FormHooks.php, EntityHooks.php) within the src/Hooks directory, all automatically discovered.
Testing hooks with mocked dependencies
Since hook implementations are regular class methods with constructor injection, they can be easily unit tested by mocking dependencies through the constructor.
Tips
- Place hook classes in src/Hooks/ directory of your module for automatic discovery - no service registration needed
- Clear cache only once when first creating a hook class; new hooks added to existing classes are discovered automatically
- Use optimized mode in production (parameters.hux.optimize: true) for better performance
- Implement ContainerInjectionInterface for services that cannot be autowired
- Use priority parameter when you need to control execution order of multiple implementations of the same hook
- The moduleName parameter in #[Hook] allows implementing a hook as if it came from a different module
- Use #[OriginalInvoker] as a parameter attribute rather than the deprecated originalInvoker parameter in #[ReplaceOriginalHook]
- hook_theme and hook_module_implements_alter are not supported due to how Drupal core invokes them
Technical Details
Hooks 2
Any Drupal hook
Hux allows implementing any Drupal hook using the #[Hook] attribute, except for hook_theme (which is not supported due to how it is invoked).
Any Drupal alter
Hux allows implementing any Drupal alter hook using the #[Alter] attribute, except for hook_module_implements_alter.
Troubleshooting 5
Clear the Drupal cache with 'drush cr'. Ensure your hook class is in the src/Hooks directory with the correct namespace Drupal\your_module\Hooks\ClassName. Verify the class has at least one method with a #[Hook] or #[Alter] attribute.
Some services are not autowirable. Implement ContainerInjectionInterface on your hook class and inject services manually via the create() static method.
hook_theme is not supported by Hux because Drupal core does not invoke it through ModuleHandler. Use the traditional .module file approach for hook_theme.
Enable optimized mode by setting 'parameters.hux.optimize: true' in your services.yml file. This pre-caches hook discovery during container compilation.
This is expected in development environments. Enable optimized mode in production, or ignore the warning if you're in a development environment where you want hooks discovered at runtime.
Security Notes 2
- Hux decorates Drupal's core module_handler service, intercepting all hook invocations. Ensure you trust all enabled modules as they can implement or replace any hook.
- When using #[ReplaceOriginalHook], be careful not to inadvertently disable security-critical hooks from other modules without proper reimplementation.