Hux
Enables hook implementations using PHP 8 attributes without requiring .module files, with full dependency injection support.
hux
インストール
composer require 'drupal/hux:^1.6'
概要
Huxは、Drupalのhook実装方法を革新する開発者向けモジュールです。.moduleファイル内の従来の手続き型関数を使用する代わりに、クラスベースのserviceでPHP 8.0以上のattributeを使用してhookを実装できます。
このアプローチには、オートワイヤリングまたはContainerInjectionInterfaceによる完全なdependency injectionサポート、モジュールごとの複数hook実装、他のモジュールからのhookの置換やオーバーライド機能、より明確な関心の分離など、いくつかの重要な利点があります。hookクラスは任意のモジュールのsrc/Hooksディレクトリで自動的に検出され、最初のcacheクリアのみが必要です。
Huxは、従来の手続き型hookとhook_event_dispatcherのような完全なイベント駆動アーキテクチャの中間に位置し、慣れ親しんだhookのシグネチャとパターンを維持しながら、dependency injectionとクラスベースのロジックの利点を提供します。
Features
- PHP 8 attribute(#[Hook]、#[Alter])を使用して.moduleファイルなしでhookを実装
- オートワイヤリングとContainerInjectionInterfaceによる完全なdependency injectionサポート
- 優先度順序付けによるモジュールごとの複数hook実装
- #[ReplaceOriginalHook]を使用して他のモジュールのhookを置換またはオーバーライド
- #[OriginalInvoker]パラメータattributeを使用して元のhook実装をcallableとしてアクセス
- 繰り返し可能なattributeを使用して単一メソッドで複数hookをリッスン
- service登録なしでsrc/Hooks名前空間内のhookクラスを自動検出
- hookを実装する際に別のモジュールとして偽装
- hook検出を事前cacheする本番環境向けの最適化モード
- 既存のhookクラスに新しいhookを追加する際にcacheクリアが不要
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.