function ModuleHandler::alter

Same name in other branches
  1. 8.9.x core/lib/Drupal/Core/Extension/ModuleHandler.php \Drupal\Core\Extension\ModuleHandler::alter()
  2. 10 core/lib/Drupal/Core/Extension/ModuleHandler.php \Drupal\Core\Extension\ModuleHandler::alter()
  3. 11.x core/lib/Drupal/Core/Extension/ModuleHandler.php \Drupal\Core\Extension\ModuleHandler::alter()

Overrides ModuleHandlerInterface::alter

2 calls to ModuleHandler::alter()
ModuleHandler::alterDeprecated in core/lib/Drupal/Core/Extension/ModuleHandler.php
Passes alterable variables to deprecated hook_TYPE_alter() implementations.
ModuleHandler::buildImplementationInfo in core/lib/Drupal/Core/Extension/ModuleHandler.php
Builds hook implementation information for a given hook name.

File

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

Class

ModuleHandler
Class that manages modules in a Drupal installation.

Namespace

Drupal\Core\Extension

Code

public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
    // Most of the time, $type is passed as a string, so for performance,
    // normalize it to that. When passed as an array, usually the first item in
    // the array is a generic type, and additional items in the array are more
    // specific variants of it, as in the case of array('form', 'form_FORM_ID').
    if (is_array($type)) {
        $cid = implode(',', $type);
        $extra_types = $type;
        $type = array_shift($extra_types);
        // Allow if statements in this function to use the faster isset() rather
        // than !empty() both when $type is passed as a string, or as an array
        // with one item.
        if (empty($extra_types)) {
            unset($extra_types);
        }
    }
    else {
        $cid = $type;
    }
    // Some alter hooks are invoked many times per page request, so store the
    // list of functions to call, and on subsequent calls, iterate through them
    // quickly.
    if (!isset($this->alterFunctions[$cid])) {
        $this->alterFunctions[$cid] = [];
        $hook = $type . '_alter';
        $modules = array_keys($this->getImplementationInfo($hook));
        if (!isset($extra_types)) {
            // For the more common case of a single hook, we do not need to call
            // function_exists(), since $this->getImplementationInfo() returns only
            // modules with implementations.
            foreach ($modules as $module) {
                $this->alterFunctions[$cid][] = $module . '_' . $hook;
            }
        }
        else {
            // For multiple hooks, we need $modules to contain every module that
            // implements at least one of them.
            $extra_modules = [];
            foreach ($extra_types as $extra_type) {
                $extra_modules[] = array_keys($this->getImplementationInfo($extra_type . '_alter'));
            }
            $extra_modules = array_merge([], ...$extra_modules);
            // If any modules implement one of the extra hooks that do not implement
            // the primary hook, we need to add them to the $modules array in their
            // appropriate order. $this->getImplementationInfo() can only return
            // ordered implementations of a single hook. To get the ordered
            // implementations of multiple hooks, we mimic the
            // $this->getImplementationInfo() logic of first ordering by
            // $this->getModuleList(), and then calling
            // $this->alter('module_implements').
            if (array_diff($extra_modules, $modules)) {
                // Merge the arrays and order by getModuleList().
                $modules = array_intersect(array_keys($this->moduleList), array_merge($modules, $extra_modules));
                // Since $this->getImplementationInfo() already took care of loading the
                // necessary include files, we can safely pass FALSE for the array
                // values.
                $implementations = array_fill_keys($modules, FALSE);
                // Let modules adjust the order solely based on the primary hook. This
                // ensures the same module order regardless of whether this if block
                // runs. Calling $this->alter() recursively in this way does not
                // result in an infinite loop, because this call is for a single
                // $type, so we won't end up in this code block again.
                $this->alter('module_implements', $implementations, $hook);
                $modules = array_keys($implementations);
            }
            foreach ($modules as $module) {
                // Since $modules is a merged array, for any given module, we do not
                // know whether it has any particular implementation, so we need a
                // function_exists().
                $function = $module . '_' . $hook;
                if (function_exists($function)) {
                    $this->alterFunctions[$cid][] = $function;
                }
                foreach ($extra_types as $extra_type) {
                    $function = $module . '_' . $extra_type . '_alter';
                    if (function_exists($function)) {
                        $this->alterFunctions[$cid][] = $function;
                    }
                }
            }
        }
    }
    foreach ($this->alterFunctions[$cid] as $function) {
        $function($data, $context1, $context2);
    }
}

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