diff --git a/core/core.services.yml b/core/core.services.yml index 317af02..1a0c3b4 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -11,7 +11,7 @@ services: class: Drupal\Core\Cache\CacheBackendInterface tags: - { name: cache.bin } - factory_method: get + factory_method: geet factory_service: cache_factory arguments: [bootstrap] cache.config: @@ -175,6 +175,9 @@ services: arguments: ['@csrf_token', '@config.factory'] tags: - { name: theme_negotiator, priority: 1000 } + plugin.manager.theme_engine: + class: Drupal\Core\Theme\ThemeEngineManager + parent: default_plugin_manager container.namespaces: class: ArrayObject arguments: [ '%container.namespaces%' ] diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 7ad60e5..362742e 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -247,13 +247,12 @@ function _drupal_theme_initialize($theme, $base_theme = array()) { // Include the engine. include_once DRUPAL_ROOT . '/' . $theme->owner; - $theme_engine = $theme->engine; - if (function_exists($theme_engine . '_init')) { - foreach ($base_theme as $base) { - call_user_func($theme_engine . '_init', $base); - } - call_user_func($theme_engine . '_init', $theme); + /** @var \Drupal\Core\Theme\ThemeEngineInterface $theme_engine */ + $theme_engine = \Drupal::service('plugin.manager.theme_engine')->createInstance($theme->engine); + foreach ($base_theme as $base) { + $theme_engine->initTheme($base); } + $theme_engine->initTheme($theme); } else { // include non-engine theme files @@ -683,22 +682,9 @@ function _theme($hook, $variables = array()) { } } else { - $render_function = 'twig_render_template'; - $extension = '.html.twig'; - // The theme engine may use a different extension and a different renderer. + /** @var \Drupal\Core\Theme\ThemeEngineInterface $theme_engine */ global $theme_engine; - if (isset($theme_engine)) { - if ($info['type'] != 'module') { - if (function_exists($theme_engine . '_render_template')) { - $render_function = $theme_engine . '_render_template'; - } - $extension_function = $theme_engine . '_extension'; - if (function_exists($extension_function)) { - $extension = $extension_function(); - } - } - } // In some cases, a template implementation may not have had // template_preprocess() run (for example, if the default implementation is @@ -731,7 +717,7 @@ function _theme($hook, $variables = array()) { } // Render the output using the template file. - $template_file = $info['template'] . $extension; + $template_file = $info['template'] . $theme_engine->getExtension(); if (isset($info['path'])) { $template_file = $info['path'] . '/' . $template_file; } @@ -745,7 +731,7 @@ function _theme($hook, $variables = array()) { if (isset($theme_hook_suggestion)) { $variables['theme_hook_suggestion'] = $theme_hook_suggestion; } - $output = $render_function($template_file, $variables); + $output = $theme_engine->renderTemplate($template_file, $variables); } // restore path_to_theme() diff --git a/core/lib/Drupal/Core/Annotation/ThemeEngine.php b/core/lib/Drupal/Core/Annotation/ThemeEngine.php new file mode 100644 index 0000000..ca46a23 --- /dev/null +++ b/core/lib/Drupal/Core/Annotation/ThemeEngine.php @@ -0,0 +1,24 @@ +twigEnviroment = $twig_enviroment; + $this->settings = $settings; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('twig'), + $container->get('settings') + ); + } + + /** + * {@inheritdoc} + */ + public function initTheme($theme) { + $file = dirname($theme->filename) . '/' . $theme->name . '.theme'; + if (file_exists($file)) { + include_once DRUPAL_ROOT . '/' . $file; + } + } + + /** + * {@inheritdoc} + */ + public function theme($existing, $type, $theme, $path) { + $templates = drupal_find_theme_functions($existing, array($theme)); + $templates += drupal_find_theme_templates($existing, '.html.twig', $path); + return $templates; + } + + /** + * {@inheritdoc} + */ + public function getExtension() { + return '.html.twig'; + } + + /** + * {@inheritdoc} + * + * If the Twig debug setting is enabled, HTML comments including _theme() call + * and template file name suggestions will surround the template markup. + */ + public function renderTemplate($template_file, $variables) { + $variables['_references'] = array(); + $output = array( + 'debug_prefix' => '', + 'debug_info' => '', + 'rendered_markup' => $this->twigEnviroment->loadTemplate($template_file)->render($variables), + 'debug_suffix' => '', + ); + if ($this->settings->get('twig_debug', FALSE)) { + $output['debug_prefix'] .= "\n\n"; + $output['debug_prefix'] .= "\n"; + if (!empty($variables['theme_hook_suggestions'])) { + $extension = $this->getExtension(); + $current_template = basename($template_file); + $suggestions = $variables['theme_hook_suggestions']; + $suggestions[] = $variables['theme_hook_original']; + foreach ($suggestions as &$suggestion) { + $template = strtr($suggestion, '_', '-') . $extension; + $prefix = ($template == $current_template) ? 'x' : '*'; + $suggestion = $prefix . ' ' . $template; + } + $output['debug_info'] .= "\n"; + } + $output['debug_info'] .= "\n\n"; + $output['debug_suffix'] .= "\n\n\n"; + } + return implode('', $output); + } + +} diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php index 1d3cb22..2950b06 100644 --- a/core/lib/Drupal/Core/Theme/Registry.php +++ b/core/lib/Drupal/Core/Theme/Registry.php @@ -36,9 +36,9 @@ class Registry implements DestructableInterface { protected $baseThemes; /** - * The name of the theme engine of $theme. + * The theme engine instance. * - * @var string + * @var \Drupal\Core\Theme\ThemeEngineInterface */ protected $engine; @@ -126,10 +126,11 @@ class Registry implements DestructableInterface { * @param string $theme_name * (optional) The name of the theme for which to construct the registry. */ - public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, ModuleHandlerInterface $module_handler, $theme_name = NULL) { + public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, ModuleHandlerInterface $module_handler, ThemeEngineManager $theme_engine_manager, $theme_name = NULL) { $this->cache = $cache; $this->lock = $lock; $this->moduleHandler = $module_handler; + $this->themeEngineManager = $theme_engine_manager; $this->init($theme_name); } @@ -183,14 +184,12 @@ protected function init($theme_name = NULL) { // @see _drupal_theme_initialize() if (isset($this->theme->engine)) { - $this->engine = $this->theme->engine; + $this->engine = $this->themeEngineManager->createInstance($this->theme->engine); include_once DRUPAL_ROOT . '/' . $this->theme->owner; - if (function_exists($this->theme->engine . '_init')) { - foreach ($this->baseThemes as $base) { - call_user_func($this->theme->engine . '_init', $base); - } - call_user_func($this->theme->engine . '_init', $this->theme); + foreach ($this->baseThemes as $base) { + $this->engine->initTheme($base); } + $this->engine->initTheme($this->theme); } } } @@ -409,8 +408,13 @@ protected function processExtension(&$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)) { + if ($type == 'engine') { + $function = array($this->engine, 'theme'); + } + else { + $function = $name . '_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 diff --git a/core/lib/Drupal/Core/Theme/ThemeEngineInterface.php b/core/lib/Drupal/Core/Theme/ThemeEngineInterface.php new file mode 100644 index 0000000..5d68486 --- /dev/null +++ b/core/lib/Drupal/Core/Theme/ThemeEngineInterface.php @@ -0,0 +1,55 @@ +filename) . '/' . $template->name . '.theme'; - if (file_exists($file)) { - include_once DRUPAL_ROOT . '/' . $file; - } -} - -/** - * Renders a Twig template. - * - * If the Twig debug setting is enabled, HTML comments including _theme() call - * and template file name suggestions will surround the template markup. - * - * @param $template_file - * The file name of the template to render. - * @param $variables - * A keyed array of variables that will appear in the output. - * - * @return - * The output generated by the template, plus any debug information. - */ -function twig_render_template($template_file, $variables) { - $variables['_references'] = array(); - $output = array( - 'debug_prefix' => '', - 'debug_info' => '', - 'rendered_markup' => \Drupal::service('twig')->loadTemplate($template_file)->render($variables), - 'debug_suffix' => '', - ); - if (settings()->get('twig_debug', FALSE)) { - $output['debug_prefix'] .= "\n\n"; - $output['debug_prefix'] .= "\n"; - if (!empty($variables['theme_hook_suggestions'])) { - $extension = twig_extension(); - $current_template = basename($template_file); - $suggestions = $variables['theme_hook_suggestions']; - $suggestions[] = $variables['theme_hook_original']; - foreach ($suggestions as &$suggestion) { - $template = strtr($suggestion, '_', '-') . $extension; - $prefix = ($template == $current_template) ? 'x' : '*'; - $suggestion = $prefix . ' ' . $template; - } - $output['debug_info'] .= "\n"; - } - $output['debug_info'] .= "\n\n"; - $output['debug_suffix'] .= "\n\n\n"; - } - return implode('', $output); -} - -/** * Wrapper around render() for twig printed output. * * If an object is passed that has no __toString method an exception is thrown;