function public function ThemeRegistry::alter

8.x-3.x ThemeRegistry.php public ThemeRegistry::alter(&$cache, &$context1 = NULL, &$context2 = NULL)

Alters data for a specific hook_TYPE_alter() implementation.


mixed $data: The variable that will be passed to hook_TYPE_alter() implementations to be altered. The type of this variable depends on the value of the $type argument. For example, when altering a 'form', $data will be a structured array. When altering a 'profile', $data will be an object.

mixed $context1: (optional) An additional variable that is passed by reference.

mixed $context2: (optional) An additional variable that is passed by reference. If more context needs to be provided to implementations, then this should be an associative array as described above.

Overrides AlterInterface::alter


Extends the theme registry to override and use protected functions.



Source src/Plugin/Alter/ThemeRegistry.php (line 60)

public function alter(&$cache, &$context1 = NULL, &$context2 = NULL) {
  // Sort the registry alphabetically (for easier debugging).

  // Add extra variables to all theme hooks.
  $extra_variables = Bootstrap::extraVariables();
  foreach (array_keys($cache) as $hook) {
    // Skip theme hooks that don't set variables.
    if (!isset($cache[$hook]['variables'])) {
    $cache[$hook]['variables'] += $extra_variables;

  // Ensure paths to templates are set properly. This allows templates to
  // be moved around in a theme without having to constantly ensuring that
  // the theme's hook_theme() definitions have the correct static "path" set.
  foreach ($this->currentTheme->getAncestry() as $ancestor) {
    $current_theme = $ancestor->getName() === $this->currentTheme->getName();
    $theme_path = $ancestor->getPath();
    // Scan entire theme root path.
    // @see
    foreach ($ancestor->fileScan('/\.html\.twig$/') as $file) {
      $hook = str_replace('-', '_', str_replace('.html.twig', '', $file->filename));
      $path = dirname($file->uri);
      $incomplete = !isset($cache[$hook]) || strrpos($hook, '__');

      // Create a new theme hook. This primarily happens when theme hook
      // suggestion templates are created. To prevent the new hook from
      // inheriting parent hook's "template", it must be manually set here.
      // @see
      if (!isset($cache[$hook])) {
        $cache[$hook] = [
          'template' => str_replace('.html.twig', '', $file->filename),

      // Always ensure that "path", "type" and "theme path" are properly set.
      $cache[$hook]['path'] = $path;
      $cache[$hook]['type'] = $current_theme ? 'theme' : 'base_theme';
      $cache[$hook]['theme path'] = $theme_path;

      // Flag incomplete.
      if ($incomplete) {
        $cache[$hook]['incomplete preprocess functions'] = TRUE;

  // Discover all the theme's preprocess plugins.
  $preprocess_manager = new PreprocessManager($this->currentTheme);
  $plugins = $preprocess_manager->getDefinitions();
  ksort($plugins, SORT_NATURAL);

  // Iterate over the preprocess plugins.
  foreach ($plugins as $plugin_id => $definition) {
    $incomplete = !isset($cache[$plugin_id]) || strrpos($plugin_id, '__');
    if (!isset($cache[$plugin_id])) {
      $cache[$plugin_id] = [];
    array_walk($cache, function(&$info, $hook) use ($plugin_id, $definition) {
      if ($hook === $plugin_id || strpos($hook, $plugin_id . '__') === 0) {
        if (!isset($info['preprocess functions'])) {
          $info['preprocess functions'] = [];
        // Due to a limitation in \Drupal\Core\Theme\ThemeManager::render,
        // callbacks must be functions and not classes. We always specify
        // "bootstrap_preprocess" here and then assign the plugin ID to a
        // separate property that we can later intercept and properly invoke.
        // @todo Revisit if/when preprocess callbacks can be any callable.
        Bootstrap::addCallback($info['preprocess functions'], 'bootstrap_preprocess', $definition['replace'], $definition['action']);
        $info['preprocess functions'] = array_unique($info['preprocess functions']);
        $info['bootstrap preprocess'] = $plugin_id;

    if ($incomplete) {
      $cache[$plugin_id]['incomplete preprocess functions'] = TRUE;

  // Allow core to post process.
  $this->postProcessExtension($cache, $this->theme);