/** * Builds the theme registry cache. * * Theme hook definitions are collected in the following order: * - Modules * - Base theme engines * - Base themes * - Theme engine * - Theme * * All theme hook definitions are essentially just collated and merged in the * above order. However, various extension-specific default values and * customizations are required; e.g., to record the effective file path for * theme template. Therefore, this method first collects all extensions per * type, and then dispatches the processing for each extension to * processExtension(). * * After completing the collection, modules are allowed to alter it. Lastly, * any derived and incomplete theme hook definitions that are hook suggestions * for base hooks (e.g., 'block__node' for the base hook 'block') need to be * determined based on the full registry and classified as 'base hook'. * * See the @link themeable Default theme implementations topic @endlink for * details. * * @return \Drupal\Core\Utility\ThemeRegistry * The build theme registry. * * @see hook_theme_registry_alter() */ protected function build() { $cache = array(); // First, preprocess the theme hooks advertised by modules. This will // serve as the basic registry. Since the list of enabled modules is the // same regardless of the theme used, this is cached in its own entry to // save building it for every theme. if ($cached = $this->cache->get('theme_registry:build:modules')) { $cache = $cached->data; } else { foreach ($this->moduleHandler->getImplementations('theme') as $module) { $this->processExtension($cache, $module, 'module', $module, $this->getPath($module)); } // Only cache this registry if all modules are loaded. if ($this->moduleHandler->isLoaded()) { $this->cache->set("theme_registry:build:modules", $cache, Cache::PERMANENT, array('theme_registry')); } } // Process each base theme. // Ensure that we start with the root of the parents, so that both CSS files // and preprocess functions comes first. foreach (array_reverse($this->theme->getBaseThemes()) as $base) { // If the base theme uses a theme engine, process its hooks. $base_path = $base->getPath(); if ($this->theme->getEngine()) { $this->processExtension($cache, $this->theme->getEngine(), 'base_theme_engine', $base->getName(), $base_path); } $this->processExtension($cache, $base->getName(), 'base_theme', $base->getName(), $base_path); } // And then the same thing, but for the theme. if ($this->theme->getEngine()) { $this->processExtension($cache, $this->theme->getEngine(), 'theme_engine', $this->theme->getName(), $this->theme->getPath()); } // Hooks provided by the theme itself. $this->processExtension($cache, $this->theme->getName(), 'theme', $this->theme->getName(), $this->theme->getPath()); // Discover and add all preprocess functions for theme hook suggestions. $this->postProcessExtension($cache, $this->theme); // Let modules and themes alter the registry. $this->moduleHandler->alter('theme_registry', $cache); $this->themeManager->alterForTheme($this->theme, 'theme_registry', $cache); // @todo Implement more reduction of the theme registry entry. // Optimize the registry to not have empty arrays for functions. foreach ($cache as $hook => $info) { if (empty($info['preprocess functions'])) { unset($cache[$hook]['preprocess functions']); } } $this->registry[$this->theme->getName()] = $cache; return $this->registry[$this->theme->getName()]; }
/** * Discovers files relevant to theme hooks. * * @param array $cache * The theme registry, as documented in * \Drupal\Core\Theme\Registry::processExtension(). * @param \Drupal\Core\Theme\ActiveTheme $theme * Current active theme. * * @see \Drupal\Core\Theme\Registry::processExtension() */ protected function discoverFiles(array &$cache, ActiveTheme $theme) { $name = $theme->getName(); $path = $theme->getPath(); // Find theme hook files. foreach (_bootstrap_file_scan_directory($path, '/(\\.func\\.php|\\.vars\\.php|\\.html\\.twig)$/') as $file) { // Transform "-" in file names to "_" to match theme hook naming scheme. $hook = strtr($file->name, '-', '_'); // Strip off the extension. if (($pos = strpos($hook, '.')) !== FALSE) { $hook = substr($hook, 0, $pos); } // File to be included by core when a theme hook is invoked. if (isset($cache[$hook])) { // Due to the order in which templates are discovered, a theme's // templates are first discovered while in the twig engine's // hook_theme() invocation. Correct the path to the template here. if (preg_match('/twig$/', $file->uri)) { $cache[$hook]['path'] = dirname($file->uri); } else { include_once DRUPAL_ROOT . '/' . $file->uri; if (!isset($cache[$hook]['includes'])) { $cache[$hook]['includes'] = array(); } if (!in_array($file->uri, $cache[$hook]['includes'])) { $cache[$hook]['includes'][] = $file->uri; } } if (!isset($cache[$hook]['preprocess functions'])) { $cache[$hook]['preprocess functions'] = array(); } if (isset($cache[$hook]['template']) && function_exists($name . '_preprocess')) { $cache[$hook]['preprocess functions'][] = $name . '_preprocess'; } if (function_exists($name . '_preprocess_' . $hook)) { $cache[$hook]['preprocess functions'][] = $name . '_preprocess_' . $hook; $cache[$hook]['theme path'] = $path; } } } }