Drupal is a registered trademark of Dries Buytaert
Protected Pages 3.0.0 Major update available for module protected_pages (3.0.0). Commerce Core 3.3.8 Minor update available for module commerce (3.3.8). Search API HTML Element Filter 1.0.7 Minor update available for module search_api_html_element_filter (1.0.7). Layout Builder Reorder 2.0.1 Minor update available for module layout_builder_reorder (2.0.1). Ban 1.1.0 Minor update available for module ban (1.1.0). Field Formatter Range 2.0.0 Major update available for module field_formatter_range (2.0.0). Field Formatter Range 8.x-1.8 Minor update available for module field_formatter_range (8.x-1.8). Varbase Media Header 9.2.1 Minor update available for module varbase_media_header (9.2.1). Search API Solr 4.3.11 Module search_api_solr updated after 14 months of inactivity (4.3.11). Provus Mega Menu Module provus_mega_menu crossed 1,000 active installs.

Hux

822 sites Security covered
View on drupal.org

Hux allows developers to implement Drupal hooks using classes and methods tagged with attributes, eliminating the need for .module files. This approach provides the benefits of dependency injection and class-driven logic for hook implementations, and allows for multiple implementations of the same hook.

Read the blog post: A modern alternative to Hooks

Hux is a project specifically designed for developers, allowing hook implementations without needing to define a .module file or any kind of proxy class/service features.

There are a few projects out there that try to introduce an event subscriber driven way of working. Hux is an in between solution, allowing the full benefits of dependency injection and class driven logic without going fully in with events.

Hooks are implemented with classes, wherein methods tagged with attributes are used for discovery. Methods have the same parameter/return signature as original hook implementations. Discovery is automatic, requiring a cache clear for each new class added.

You can also define multiple of the same hook per module!

Requires Drupal 9.4 or later, and PHP 8.0 or later. (Drupal 9.3 users may use a patch in #2616814: Delegate all hook invocations to ModuleHandler)

Usage Examples (See README.md for more)


declare(strict_types = 1);

namespace Drupal\my_module;

use Drupal\hux\Attribute\Alter;
use Drupal\hux\Attribute\Hook;
use Drupal\hux\Attribute\ReplaceOriginalHook;

/**
 * Usage examples.
 */
final class MyModuleHooks {

  public function __construct(
    // Autowiring is enabled.
    // \Drupal\Core\DependencyInjection\ContainerInjectionInterface, or manual service definitions are available for older Drupal installs or usage of non-autowirable services.
    private readonly \Drupal\Component\Datetime\TimeInterface $time,
  )

  #[Hook('entity_access')]
  public function myEntityAccess(EntityInterface $entity, string $operation, AccountInterface $account): AccessResultInterface {
    // A barebones implementation.
    return AccessResult::neutral();
  }

  #[Hook('entity_access', priority: 100)]
  public function myEntityAccess2(EntityInterface $entity, string $operation, AccountInterface $account): AccessResultInterface {
    // You can set priority if you have multiple of the same hook!
    return AccessResult::neutral();
  }

  #[Hook('entity_access', moduleName: 'a_different_module', priority: 200)]
  public function myEntityAccess3(EntityInterface $entity, string $operation, AccountInterface $account): AccessResultInterface {
    // You can masquerade as a different module!
    return AccessResult::neutral();
  }

  #[ReplaceOriginalHook(hook: 'entity_access', moduleName: 'media')]
  public function myEntityAccess4(EntityInterface $entity, string $operation, AccountInterface $account): AccessResultInterface {
    // You can override hooks for other modules! E.g \media_entity_access()
    return AccessResult::neutral();
  }


  #[ReplaceOriginalHook(hook: 'entity_access', moduleName: 'media')]
  public function myEntityAccess5(EntityInterface $entity, string $operation, AccountInterface $account, #[OriginalInvoker] callable $originalInvoker): AccessResultInterface {
    // If you override a hook for another module, you can have the original
    // implementation passed to you as a callable!
    $originalResult = $originalInvoker($entity, $operation, $account);
    // Do something...
    return AccessResult::neutral();
  }

  #[Alter('user_format_name')]
  public function myCustomAlter(string &$name, AccountInterface $account): void {
    $name .= ' altered!'; 
  }

  #[
    Hook('entity_insert'),
    Hook('entity_delete'),
  ]
  public function myEntityInsertOrDelete(EntityInterface $entity): void {
    // Associate with multiple!
    // Also works with Alters and Replacements.
  }

}

Contribution tips

Only Merge Requests are accepted. Patches are not accepted.

Tests use a customized Gitlab template with custom PHP Code Sniffer and PHPStan ruleset. You can fix these issues by running linting locally or wait for errors after pushing your work to an MR.

Similar projects

Versions

  • 1.1.x is compatible with Drupal 9 and 10.
  • 1.2.x, Drupal ^10.0
  • 1.3.x, Drupal ^10.1
  • 1.4.x, Drupal ^10.2
  • 1.5.x, Drupal ^10.3
  • 1.6.x, Drupal ^10.3 || ^11
  • 1.7.x, Drupal 11.0 minimum
  • 1.8.x, Drupal 11.1 minimum

Actual constraints may vary. Older incompatible releases may match an untested version of core.

Activity

Total releases
5
First release
Jan 2025
Latest release
1 year ago
Releases (12 mo)
0 ▼ from 5
Maintenance
Dormant

Release Timeline

Releases

Version Type Release date
1.6.1 Stable May 15, 2025
1.7.1 Stable May 15, 2025
1.8.1 Stable May 15, 2025
1.8.0 Stable Jan 31, 2025
1.8.x-dev Dev Jan 31, 2025