diff --git a/core/lib/Drupal/Core/Controller/ControllerResolver.php b/core/lib/Drupal/Core/Controller/ControllerResolver.php index a8f2b3c..c0e4b5b 100644 --- a/core/lib/Drupal/Core/Controller/ControllerResolver.php +++ b/core/lib/Drupal/Core/Controller/ControllerResolver.php @@ -64,7 +64,7 @@ public function __construct(HttpMessageFactoryInterface $http_message_factory, C * {@inheritdoc} */ public function getControllerFromDefinition($controller, $path = '') { - if (is_array($controller) || (is_object($controller) && method_exists($controller, '__invoke'))) { + if (is_array($controller) || (is_object($controller) && method_exists($controller, '__invoke')) || function_exists($controller)) { return $controller; } diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php index e2a89a9..cc8a938 100644 --- a/core/lib/Drupal/Core/Theme/Registry.php +++ b/core/lib/Drupal/Core/Theme/Registry.php @@ -429,7 +429,15 @@ protected function processExtension(array &$cache, $name, $type, $theme, $path) // Invoke the hook_theme() implementation, preprocess what is returned, and // merge it into $cache. $function = $name . '_theme'; - if (function_exists($function)) { + + $theme_engine_service = NULL; + if ($type == 'theme_engine' && \Drupal::service('theme_engine.' . $name)) { + /** @var \Drupal\Core\Theme\ThemeEngineInterface|null $theme_engine_service */ + $theme_engine_service = \Drupal::hasService('theme_engine.' . $name) ? \Drupal::service('theme_engine.' . $name) : NULL; + $function = [$theme_engine_service, 'theme']; + } + + if (is_callable($function)) { $result = $function($cache, $type, $theme, $path); foreach ($result as $hook => $info) { // When a theme or engine overrides a module's theme function @@ -572,6 +580,9 @@ protected function processExtension(array &$cache, $name, $type, $theme, $path) if (isset($info['template']) && function_exists($name . '_preprocess')) { $cache[$hook]['preprocess functions'][] = $name . '_preprocess'; } + if (isset($info['template']) && $type == 'theme_engine' && $theme_engine_service) { + $cache[$hook]['preprocess functions'][] = "theme_engine.$name:preprocess"; + } if (function_exists($name . '_preprocess_' . $hook)) { $cache[$hook]['preprocess functions'][] = $name . '_preprocess_' . $hook; $cache[$hook]['theme path'] = $path; diff --git a/core/lib/Drupal/Core/Theme/ThemeEngineCollection.php b/core/lib/Drupal/Core/Theme/ThemeEngineCollection.php new file mode 100644 index 0000000..1db96f0 --- /dev/null +++ b/core/lib/Drupal/Core/Theme/ThemeEngineCollection.php @@ -0,0 +1,30 @@ +engines[$name] = $theme_engine; + $this->serviceIDs[$name] = $service_id; + } + + public function getThemeEngine($name) { + return isset($this->engines[$name]) ? $this->engines[$name] : NULL; + } + + public function getServiceID($name) { + return isset($this->serviceIDs[$name]) ? $this->serviceIDs[$name] : NULL; + } + +} diff --git a/core/lib/Drupal/Core/Theme/ThemeEngineInterface.php b/core/lib/Drupal/Core/Theme/ThemeEngineInterface.php new file mode 100644 index 0000000..4c507e2 --- /dev/null +++ b/core/lib/Drupal/Core/Theme/ThemeEngineInterface.php @@ -0,0 +1,60 @@ +root . '/' . $active_theme->getOwner(); - if (function_exists($theme_engine . '_init')) { + /** @var \Drupal\Core\Theme\ThemeEngineInterface|null $theme_engine_service */ + $theme_engine_service = \Drupal::hasService('theme_engine.' . $theme_engine) ? \Drupal::service('theme_engine.' . $theme_engine) : NULL; + if ($theme_engine_service) { + foreach ($active_theme->getBaseThemes() as $base) { + $theme_engine_service->initTheme($base->getExtension()); + } + $theme_engine_service->initTheme($active_theme->getExtension()); + } + elseif (function_exists($theme_engine . '_init')) { foreach ($active_theme->getBaseThemes() as $base) { call_user_func($theme_engine . '_init', $base->getExtension()); } diff --git a/core/lib/Drupal/Core/Theme/ThemeManager.php b/core/lib/Drupal/Core/Theme/ThemeManager.php index d4c6a2c..16c5ecd 100644 --- a/core/lib/Drupal/Core/Theme/ThemeManager.php +++ b/core/lib/Drupal/Core/Theme/ThemeManager.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Theme; use Drupal\Component\Render\MarkupInterface; +use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Render\Markup; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\StackedRouteMatchInterface; @@ -61,6 +62,13 @@ class ThemeManager implements ThemeManagerInterface { protected $root; /** + * The controller resolver. + * + * @var \Drupal\Core\Controller\ControllerResolverInterface + */ + protected $controllerResolver; + + /** * Constructs a new ThemeManager object. * * @param string $root @@ -72,13 +80,16 @@ class ThemeManager implements ThemeManagerInterface { * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver + * The controller resolver. */ - public function __construct($root, ThemeNegotiatorInterface $theme_negotiator, ThemeInitializationInterface $theme_initialization, RequestStack $request_stack, ModuleHandlerInterface $module_handler) { + public function __construct($root, ThemeNegotiatorInterface $theme_negotiator, ThemeInitializationInterface $theme_initialization, RequestStack $request_stack, ModuleHandlerInterface $module_handler, ControllerResolverInterface $controller_resolver = NULL) { $this->root = $root; $this->themeNegotiator = $theme_negotiator; $this->themeInitialization = $theme_initialization; $this->requestStack = $request_stack; $this->moduleHandler = $module_handler; + $this->controllerResolver = $controller_resolver ?: \Drupal::service('controller_resolver'); } /** @@ -289,8 +300,11 @@ public function render($hook, array $variables) { } if (isset($info['preprocess functions'])) { foreach ($info['preprocess functions'] as $preprocessor_function) { - if (function_exists($preprocessor_function)) { - $preprocessor_function($variables, $hook, $info); + // Allow preprocess functions from services. + if (strpos($preprocessor_function, ':') === FALSE || $preprocessor_function = $this->controllerResolver->getControllerFromDefinition($preprocessor_function)) { + if (is_callable($preprocessor_function)) { + $preprocessor_function($variables, $hook, $info); + } } } // Allow theme preprocess functions to set $variables['#attached'] and @@ -332,10 +346,19 @@ public function render($hook, array $variables) { $theme_engine = $active_theme->getEngine(); if (isset($theme_engine)) { if ($info['type'] != 'module') { - if (function_exists($theme_engine . '_render_template')) { + $theme_engine_service = \Drupal::hasService('theme_engine.' . $theme_engine) ? \Drupal::service('theme_engine.' . $theme_engine) : NULL; + if ($theme_engine_service) { + $render_function = [$theme_engine_service, 'renderTemplate']; + } + elseif (function_exists($theme_engine . '_render_template')) { $render_function = $theme_engine . '_render_template'; } - $extension_function = $theme_engine . '_extension'; + if ($theme_engine_service) { + $extension_function = [$theme_engine_service, 'getExtension']; + } + else { + $extension_function = $theme_engine . '_extension'; + } if (function_exists($extension_function)) { $extension = $extension_function(); } diff --git a/core/modules/system/tests/themes/engines/nyan_cat/nyan_cat.engine b/core/modules/system/tests/themes/engines/nyan_cat/nyan_cat.engine deleted file mode 100644 index 04666dc..0000000 --- a/core/modules/system/tests/themes/engines/nyan_cat/nyan_cat.engine +++ /dev/null @@ -1,55 +0,0 @@ -load(); -} - -/** - * Implements hook_theme(). - */ -function nyan_cat_theme($existing, $type, $theme, $path) { - $templates = drupal_find_theme_functions($existing, array($theme)); - $templates += drupal_find_theme_templates($existing, '.nyan-cat.html', $path); - return $templates; -} - -/** - * Implements hook_extension(). - */ -function nyan_cat_extension() { - return '.nyan-cat.html'; -} - -/** - * Implements hook_render_template(). - * - * @param string $template_file - * The filename of the template to render. - * @param mixed[] $variables - * A keyed array of variables that will appear in the output. - * - * @return string - * The output generated by the template. - */ -function nyan_cat_render_template($template_file, $variables) { - $output = str_replace('div', 'nyancat', file_get_contents(\Drupal::root() . '/' . $template_file)); - foreach ($variables as $key => $variable) { - if (strpos($output, '9' . $key) !== FALSE) { - $output = str_replace('9' . $key, theme_render_and_autoescape($variable), $output); - } - } - return $output; -} diff --git a/core/modules/system/tests/themes/engines/nyan_cat/nyat_cat.services.yml b/core/modules/system/tests/themes/engines/nyan_cat/nyat_cat.services.yml new file mode 100644 index 0000000..bac750c --- /dev/null +++ b/core/modules/system/tests/themes/engines/nyan_cat/nyat_cat.services.yml @@ -0,0 +1,2 @@ +theme_engine.nyan_cat: + class: Drupal\nyan_cat\NyanCatThemeEngine diff --git a/core/modules/system/tests/themes/engines/nyan_cat/src/NyanCatThemeEngine.php b/core/modules/system/tests/themes/engines/nyan_cat/src/NyanCatThemeEngine.php new file mode 100644 index 0000000..f3a682e --- /dev/null +++ b/core/modules/system/tests/themes/engines/nyan_cat/src/NyanCatThemeEngine.php @@ -0,0 +1,63 @@ +load(); + } + + /** + * {@inheritdoc} + */ + public function theme($existing, $type, $theme, $path) { + $templates = drupal_find_theme_functions($existing, array($theme)); + $templates += drupal_find_theme_templates($existing, '.nyan-cat.html', $path); + return $templates; + } + + /** + * {@inheritdoc} + */ + public function getExtension() { + return '.nyan-cat.html'; + } + + /** + * {@inheritdoc} + */ + public function renderTemplate($template_file, $variables) { + $output = str_replace('div', 'nyancat', file_get_contents(\Drupal::root() . '/' . $template_file)); + foreach ($variables as $key => $variable) { + if (strpos($output, '9' . $key) !== FALSE) { + $output = str_replace('9' . $key, theme_render_and_autoescape($variable), $output); + } + } + return $output; + } + + /** + * {@inheritdoc} + */ + public function preprocess() { + } + +}