twig.engine 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. <?php
  2. /**
  3. * @file
  4. * Handles integration of Twig templates with the Drupal theme system.
  5. */
  6. use Drupal\Component\Utility\Html;
  7. use Drupal\Core\Render\Markup;
  8. use Drupal\Core\Extension\Extension;
  9. /**
  10. * Implements hook_theme().
  11. */
  12. function twig_theme($existing, $type, $theme, $path) {
  13. $templates = drupal_find_theme_functions($existing, array($theme));
  14. $templates += drupal_find_theme_templates($existing, '.html.twig', $path);
  15. return $templates;
  16. }
  17. /**
  18. * Implements hook_extension().
  19. */
  20. function twig_extension() {
  21. return '.html.twig';
  22. }
  23. /**
  24. * Includes .theme file from themes.
  25. *
  26. * @param \Drupal\Core\Extension\Extension $theme
  27. * The theme extension object.
  28. */
  29. function twig_init(Extension $theme) {
  30. $theme->load();
  31. }
  32. /**
  33. * Implements hook_render_template().
  34. *
  35. * Renders a Twig template.
  36. *
  37. * If the Twig debug setting is enabled, HTML comments including the theme hook
  38. * and template file name suggestions will surround the template markup.
  39. *
  40. * @param string $template_file
  41. * The file name of the template to render.
  42. * @param array $variables
  43. * A keyed array of variables that will appear in the output.
  44. *
  45. * @return string|\Drupal\Component\Render\MarkupInterface
  46. * The output generated by the template, plus any debug information.
  47. */
  48. function twig_render_template($template_file, array $variables) {
  49. /** @var \Twig_Environment $twig_service */
  50. $twig_service = \Drupal::service('twig');
  51. $output = [
  52. 'debug_prefix' => '',
  53. 'debug_info' => '',
  54. 'rendered_markup' => '',
  55. 'debug_suffix' => '',
  56. ];
  57. try {
  58. $output['rendered_markup'] = $twig_service->loadTemplate($template_file)->render($variables);
  59. }
  60. catch (\Twig_Error_Loader $e) {
  61. drupal_set_message($e->getMessage(), 'error');
  62. }
  63. catch (\Twig_Error_Runtime $e) {
  64. // In case there is a previous exception, re-throw the previous exception,
  65. // so that the original exception is shown, rather than
  66. // \Twig_Template::displayWithErrorHandling()'s exception.
  67. $previous_exception = $e->getPrevious();
  68. if ($previous_exception) {
  69. throw $previous_exception;
  70. }
  71. throw $e;
  72. }
  73. if ($twig_service->isDebug()) {
  74. $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
  75. $output['debug_prefix'] .= "\n<!-- THEME HOOK: '" . Html::escape($variables['theme_hook_original']) . "' -->";
  76. // If there are theme suggestions, reverse the array so more specific
  77. // suggestions are shown first.
  78. if (!empty($variables['theme_hook_suggestions'])) {
  79. $variables['theme_hook_suggestions'] = array_reverse($variables['theme_hook_suggestions']);
  80. }
  81. // Add debug output for directly called suggestions like
  82. // '#theme' => 'comment__node__article'.
  83. if (strpos($variables['theme_hook_original'], '__') !== FALSE) {
  84. $derived_suggestions[] = $hook = $variables['theme_hook_original'];
  85. while ($pos = strrpos($hook, '__')) {
  86. $hook = substr($hook, 0, $pos);
  87. $derived_suggestions[] = $hook;
  88. }
  89. // Get the value of the base hook (last derived suggestion) and append it
  90. // to the end of all theme suggestions.
  91. $base_hook = array_pop($derived_suggestions);
  92. $variables['theme_hook_suggestions'] = array_merge($derived_suggestions, $variables['theme_hook_suggestions']);
  93. $variables['theme_hook_suggestions'][] = $base_hook;
  94. }
  95. if (!empty($variables['theme_hook_suggestions'])) {
  96. $extension = twig_extension();
  97. $current_template = basename($template_file);
  98. $suggestions = $variables['theme_hook_suggestions'];
  99. // Only add the original theme hook if it wasn't a directly called
  100. // suggestion.
  101. if (strpos($variables['theme_hook_original'], '__') === FALSE) {
  102. $suggestions[] = $variables['theme_hook_original'];
  103. }
  104. foreach ($suggestions as &$suggestion) {
  105. $template = strtr($suggestion, '_', '-') . $extension;
  106. $prefix = ($template == $current_template) ? 'x' : '*';
  107. $suggestion = $prefix . ' ' . $template;
  108. }
  109. $output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n " . Html::escape(implode("\n ", $suggestions)) . "\n-->";
  110. }
  111. $output['debug_info'] .= "\n<!-- BEGIN OUTPUT from '" . Html::escape($template_file) . "' -->\n";
  112. $output['debug_suffix'] .= "\n<!-- END OUTPUT from '" . Html::escape($template_file) . "' -->\n\n";
  113. }
  114. // This output has already been rendered and is therefore considered safe.
  115. return Markup::create(implode('', $output));
  116. }
  117. /**
  118. * Removes child elements from a copy of the original array.
  119. *
  120. * Creates a copy of the renderable array and removes child elements by key
  121. * specified through filter's arguments. The copy can be printed without these
  122. * elements. The original renderable array is still available and can be used
  123. * to print child elements in their entirety in the twig template.
  124. *
  125. * @param array|object $element
  126. * The parent renderable array to exclude the child items.
  127. * @param string[] $args, ...
  128. * The string keys of $element to prevent printing.
  129. *
  130. * @return array
  131. * The filtered renderable array.
  132. */
  133. function twig_without($element) {
  134. if ($element instanceof ArrayAccess) {
  135. $filtered_element = clone $element;
  136. }
  137. else {
  138. $filtered_element = $element;
  139. }
  140. $args = func_get_args();
  141. unset($args[0]);
  142. foreach ($args as $arg) {
  143. if (isset($filtered_element[$arg])) {
  144. unset($filtered_element[$arg]);
  145. }
  146. }
  147. return $filtered_element;
  148. }