function ModuleHandler::getCombinedListeners

Builds a list of implementations for an alter hook.

Parameters

list<string> $hooks: The hooks passed to the ->alter() call.

Return value

list<callable> List of implementation callables.

1 call to ModuleHandler::getCombinedListeners()
ModuleHandler::alter in core/lib/Drupal/Core/Extension/ModuleHandler.php
Passes alterable variables to specific hook_TYPE_alter() implementations.

File

core/lib/Drupal/Core/Extension/ModuleHandler.php, line 473

Class

ModuleHandler
Class that manages modules in a Drupal installation.

Namespace

Drupal\Core\Extension

Code

protected function getCombinedListeners(array $hooks) : array {
  // Get implementation lists for each hook.
  /** @var list<\Drupal\Core\Hook\ImplementationList> $lists */
  $lists = array_map($this->getHookImplementationList(...), $hooks);
  // Remove empty lists.
  /** @var array<int, \Drupal\Core\Hook\ImplementationList> $lists */
  $lists = array_filter($lists, fn(ImplementationList $list) => $list->hasImplementations());
  if (!$lists) {
    // No implementations exist.
    return [];
  }
  if (array_keys($lists) === [
    0,
  ]) {
    // Only the first hook has implementations.
    return $lists[0]->listeners;
  }
  // Collect the lists from each hook and group the listeners by module.
  $listeners_by_identifier = [];
  $modules_by_identifier = [];
  $identifiers_by_module = [];
  foreach ($lists as $list) {
    foreach ($list->iterateByModule() as $module => $listener) {
      $identifier = is_array($listener) ? get_class($listener[0]) . '::' . $listener[1] : $listener;
      $other_module = $modules_by_identifier[$identifier] ?? NULL;
      if ($other_module !== NULL) {
        $this->triggerErrorForDuplicateAlterHookListener($hooks, $module, $other_module, $listener, $identifier);
        // Don't add the same listener more than once.
        continue;
      }
      $listeners_by_identifier[$identifier] = $listener;
      $modules_by_identifier[$identifier] = $module;
      $identifiers_by_module[$module][] = $identifier;
    }
  }
  // First we get the the modules in moduleList order, this order is module
  // weight then alphabetical. Then we apply legacy ordering using
  // hook_module_implements_alter(). Finally we order using order attributes.
  $modules = array_keys($identifiers_by_module);
  $modules = $this->reOrderModulesForAlter($modules, $hooks[0]);
  // Create a flat list of identifiers, using the new module order.
  $identifiers = array_merge(...array_map(fn(string $module) => $identifiers_by_module[$module], $modules));
  foreach ($hooks as $hook) {
    foreach ($this->getHookOrderingRules($hook) as $rule) {
      $rule->apply($identifiers, $modules_by_identifier);
      // Order operations must not:
      // - Insert duplicate keys.
      // - Change the array to be not a list.
      // - Add or remove values.
      assert($identifiers === array_unique($identifiers));
      assert(array_is_list($identifiers));
      assert(!array_diff($identifiers, array_keys($modules_by_identifier)));
      assert(!array_diff(array_keys($modules_by_identifier), $identifiers));
    }
  }
  return array_map(static fn(string $identifier) => $listeners_by_identifier[$identifier], $identifiers);
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.