Loading core/lib/Drupal/Core/Hook/HookCollectorPass.php +1 −1 Original line number Diff line number Diff line Loading @@ -287,7 +287,7 @@ protected function calculateImplementations(): array { } foreach ($moduleImplements as $module => $v) { if (is_string($hook) && str_starts_with($hook, 'preprocess_') && str_contains($hook, '__')) { $this->preprocessForSuggestions[$module . '_' . $hook] = TRUE; $this->preprocessForSuggestions[$module . '_' . $hook] = 'module'; } foreach (array_keys($implementationsByHookOrig[$hook], $module, TRUE) as $identifier) { $implementationsByHook[$hook][$identifier] = $module; Loading core/lib/Drupal/Core/Hook/ThemeHookCollectorPass.php +1 −1 Original line number Diff line number Diff line Loading @@ -133,7 +133,7 @@ protected function calculateImplementations(): array { foreach ($implementationsByHookOrig as $hook => $hookImplementations) { if (is_string($hook) && str_starts_with($hook, 'preprocess_') && str_contains($hook, '__')) { foreach ($hookImplementations as $theme) { $this->preprocessForSuggestions[$theme . '_' . $hook] = TRUE; $this->preprocessForSuggestions[$theme . '_' . $hook] = 'theme'; } } } Loading core/lib/Drupal/Core/Theme/Registry.php +11 −6 Original line number Diff line number Diff line Loading @@ -184,7 +184,7 @@ class Registry implements DestructableInterface { * These are stored to prevent adding preprocess suggestions to the invoke map * that are not discovered in modules. * * @var array<string, true> * @var array<string, string> */ protected ?array $preprocessForSuggestions = NULL; Loading Loading @@ -833,6 +833,8 @@ protected function postProcessExtension(array &$cache, ActiveTheme $theme) { // Collect all variable preprocess functions in the correct order. $suggestion_level = []; $invokes = []; $preprocess_extension_types = $this->getPreprocessForSuggestions(); // Look for functions named according to the pattern and add them if they // have matching hooks in the registry. foreach ($prefixes as $prefix) { Loading @@ -849,9 +851,12 @@ protected function postProcessExtension(array &$cache, ActiveTheme $theme) { if (isset($cache[$matches[2]])) { $level = substr_count($matches[1], '__'); $suggestion_level[$level][$candidate] = $matches[1]; $module_preprocess_function = $prefix . '_preprocess_' . $matches[1]; if (isset($this->getPreprocessForSuggestions()[$module_preprocess_function])) { $invokes[$candidate] = ['module' => $prefix, 'hook' => 'preprocess_' . $matches[1]]; $preprocess_function = $prefix . '_preprocess_' . $matches[1]; if (\array_key_exists($preprocess_function, $preprocess_extension_types)) { $invokes[$candidate] = [ $preprocess_extension_types[$preprocess_function] => $prefix, 'hook' => 'preprocess_' . $matches[1], ]; } } } Loading Loading @@ -1033,8 +1038,8 @@ protected function hasThemeHookImplementation(string $theme, string $hook): bool /** * Returns discovered preprocess suggestions. * * @return array<string, true> * Preprocess suggestions discovered in modules. * @return array<string, string> * Preprocess suggestions discovered in themes and modules. */ protected function getPreprocessForSuggestions(): array { if ($this->preprocessForSuggestions === NULL) { Loading core/tests/Drupal/KernelTests/Core/Theme/RegistryTest.php +15 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ use Drupal\Core\Path\CurrentPathStack; use Drupal\Core\Path\PathMatcherInterface; use Drupal\Core\Theme\Registry; use Drupal\Core\Theme\ThemeInitializationInterface; use Drupal\Core\Utility\ThemeRegistry; use Drupal\KernelTests\KernelTestBase; use PHPUnit\Framework\Attributes\DataProvider; Loading Loading @@ -115,6 +116,8 @@ public function testSuggestionPreprocessFunctions(): void { $theme_handler = \Drupal::service('theme_handler'); \Drupal::service('theme_installer')->install(['test_theme']); \Drupal::theme()->setActiveTheme(\Drupal::service(ThemeInitializationInterface::class)->initTheme('test_theme')); $extension_list = $this->container->get('extension.list.module'); assert($extension_list instanceof ModuleExtensionList); $registry_theme = new Registry($this->root, \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), \Drupal::service('cache.bootstrap'), $extension_list, \Drupal::service('kernel'), 'test_theme', \Drupal::service('keyvalue')); Loading @@ -131,8 +134,20 @@ public function testSuggestionPreprocessFunctions(): void { $expected_preprocess_functions[] = "test_theme_preprocess_$hook"; $preprocess_functions = $registry_theme->get()[$hook]['preprocess functions']; $this->assertSame($expected_preprocess_functions, $preprocess_functions, "$hook has correct preprocess functions."); // Ensure the invoke map has the expected structure. $expected_invoke_map = [ 'theme' => 'test_theme', 'hook' => "preprocess_$hook", ]; $this->assertEquals($expected_invoke_map, $registry_theme->get()['preprocess invokes']["test_theme_preprocess_$hook"], "$hook has correct invokes."); } while ($suggestion = array_shift($suggestions)); // Ensure the theme preprocess for the suggestion runs and sets the bar // variable. $output = \Drupal::theme()->render('theme_test_preprocess_suggestions__kitten__flamingo', []); $this->assertStringContainsString('Flamingo', (string) $output); $expected_preprocess_functions = [ 'theme_test_preprocess_theme_test_preprocess_suggestions', 'test_theme_preprocess_theme_test_preprocess_suggestions', Loading Loading
core/lib/Drupal/Core/Hook/HookCollectorPass.php +1 −1 Original line number Diff line number Diff line Loading @@ -287,7 +287,7 @@ protected function calculateImplementations(): array { } foreach ($moduleImplements as $module => $v) { if (is_string($hook) && str_starts_with($hook, 'preprocess_') && str_contains($hook, '__')) { $this->preprocessForSuggestions[$module . '_' . $hook] = TRUE; $this->preprocessForSuggestions[$module . '_' . $hook] = 'module'; } foreach (array_keys($implementationsByHookOrig[$hook], $module, TRUE) as $identifier) { $implementationsByHook[$hook][$identifier] = $module; Loading
core/lib/Drupal/Core/Hook/ThemeHookCollectorPass.php +1 −1 Original line number Diff line number Diff line Loading @@ -133,7 +133,7 @@ protected function calculateImplementations(): array { foreach ($implementationsByHookOrig as $hook => $hookImplementations) { if (is_string($hook) && str_starts_with($hook, 'preprocess_') && str_contains($hook, '__')) { foreach ($hookImplementations as $theme) { $this->preprocessForSuggestions[$theme . '_' . $hook] = TRUE; $this->preprocessForSuggestions[$theme . '_' . $hook] = 'theme'; } } } Loading
core/lib/Drupal/Core/Theme/Registry.php +11 −6 Original line number Diff line number Diff line Loading @@ -184,7 +184,7 @@ class Registry implements DestructableInterface { * These are stored to prevent adding preprocess suggestions to the invoke map * that are not discovered in modules. * * @var array<string, true> * @var array<string, string> */ protected ?array $preprocessForSuggestions = NULL; Loading Loading @@ -833,6 +833,8 @@ protected function postProcessExtension(array &$cache, ActiveTheme $theme) { // Collect all variable preprocess functions in the correct order. $suggestion_level = []; $invokes = []; $preprocess_extension_types = $this->getPreprocessForSuggestions(); // Look for functions named according to the pattern and add them if they // have matching hooks in the registry. foreach ($prefixes as $prefix) { Loading @@ -849,9 +851,12 @@ protected function postProcessExtension(array &$cache, ActiveTheme $theme) { if (isset($cache[$matches[2]])) { $level = substr_count($matches[1], '__'); $suggestion_level[$level][$candidate] = $matches[1]; $module_preprocess_function = $prefix . '_preprocess_' . $matches[1]; if (isset($this->getPreprocessForSuggestions()[$module_preprocess_function])) { $invokes[$candidate] = ['module' => $prefix, 'hook' => 'preprocess_' . $matches[1]]; $preprocess_function = $prefix . '_preprocess_' . $matches[1]; if (\array_key_exists($preprocess_function, $preprocess_extension_types)) { $invokes[$candidate] = [ $preprocess_extension_types[$preprocess_function] => $prefix, 'hook' => 'preprocess_' . $matches[1], ]; } } } Loading Loading @@ -1033,8 +1038,8 @@ protected function hasThemeHookImplementation(string $theme, string $hook): bool /** * Returns discovered preprocess suggestions. * * @return array<string, true> * Preprocess suggestions discovered in modules. * @return array<string, string> * Preprocess suggestions discovered in themes and modules. */ protected function getPreprocessForSuggestions(): array { if ($this->preprocessForSuggestions === NULL) { Loading
core/tests/Drupal/KernelTests/Core/Theme/RegistryTest.php +15 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ use Drupal\Core\Path\CurrentPathStack; use Drupal\Core\Path\PathMatcherInterface; use Drupal\Core\Theme\Registry; use Drupal\Core\Theme\ThemeInitializationInterface; use Drupal\Core\Utility\ThemeRegistry; use Drupal\KernelTests\KernelTestBase; use PHPUnit\Framework\Attributes\DataProvider; Loading Loading @@ -115,6 +116,8 @@ public function testSuggestionPreprocessFunctions(): void { $theme_handler = \Drupal::service('theme_handler'); \Drupal::service('theme_installer')->install(['test_theme']); \Drupal::theme()->setActiveTheme(\Drupal::service(ThemeInitializationInterface::class)->initTheme('test_theme')); $extension_list = $this->container->get('extension.list.module'); assert($extension_list instanceof ModuleExtensionList); $registry_theme = new Registry($this->root, \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), \Drupal::service('cache.bootstrap'), $extension_list, \Drupal::service('kernel'), 'test_theme', \Drupal::service('keyvalue')); Loading @@ -131,8 +134,20 @@ public function testSuggestionPreprocessFunctions(): void { $expected_preprocess_functions[] = "test_theme_preprocess_$hook"; $preprocess_functions = $registry_theme->get()[$hook]['preprocess functions']; $this->assertSame($expected_preprocess_functions, $preprocess_functions, "$hook has correct preprocess functions."); // Ensure the invoke map has the expected structure. $expected_invoke_map = [ 'theme' => 'test_theme', 'hook' => "preprocess_$hook", ]; $this->assertEquals($expected_invoke_map, $registry_theme->get()['preprocess invokes']["test_theme_preprocess_$hook"], "$hook has correct invokes."); } while ($suggestion = array_shift($suggestions)); // Ensure the theme preprocess for the suggestion runs and sets the bar // variable. $output = \Drupal::theme()->render('theme_test_preprocess_suggestions__kitten__flamingo', []); $this->assertStringContainsString('Flamingo', (string) $output); $expected_preprocess_functions = [ 'theme_test_preprocess_theme_test_preprocess_suggestions', 'test_theme_preprocess_theme_test_preprocess_suggestions', Loading