You are here

twig.engine in Zircon Profile 8

Same filename and directory in other branches
  1. 8.0 core/themes/engines/twig/twig.engine

Handles integration of Twig templates with the Drupal theme system.

File

core/themes/engines/twig/twig.engine
View source
<?php

/**
 * @file
 * Handles integration of Twig templates with the Drupal theme system.
 */
use Drupal\Component\Utility\Html;
use Drupal\Core\Render\Markup;
use Drupal\Core\Extension\Extension;

/**
 * Implements hook_theme().
 */
function twig_theme($existing, $type, $theme, $path) {
  $templates = drupal_find_theme_functions($existing, array(
    $theme,
  ));
  $templates += drupal_find_theme_templates($existing, '.html.twig', $path);
  return $templates;
}

/**
 * Implements hook_extension().
 */
function twig_extension() {
  return '.html.twig';
}

/**
 * Implements hook_init().
 */
function twig_init(Extension $theme) {
  $theme
    ->load();
}

/**
 * Implements hook_render_template().
 *
 * 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 string $template_file
 *   The file name of the template to render.
 * @param array $variables
 *   A keyed array of variables that will appear in the output.
 *
 * @return string|\Drupal\Component\Render\MarkupInterface
 *   The output generated by the template, plus any debug information.
 */
function twig_render_template($template_file, array $variables) {

  /** @var \Twig_Environment $twig_service */
  $twig_service = \Drupal::service('twig');
  $output = [
    'debug_prefix' => '',
    'debug_info' => '',
    'rendered_markup' => '',
    'debug_suffix' => '',
  ];
  try {
    $output['rendered_markup'] = $twig_service
      ->loadTemplate($template_file)
      ->render($variables);
  } catch (\Twig_Error_Loader $e) {
    drupal_set_message($e
      ->getMessage(), 'error');
  } catch (\Twig_Error_Runtime $e) {

    // In case there is a previous exception, re-throw the previous exception,
    // so that the original exception is shown, rather than
    // \Twig_Template::displayWithErrorHandling()'s exception.
    $previous_exception = $e
      ->getPrevious();
    if ($previous_exception) {
      throw $previous_exception;
    }
    throw $e;
  }
  if ($twig_service
    ->isDebug()) {
    $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
    $output['debug_prefix'] .= "\n<!-- THEME HOOK: '" . Html::escape($variables['theme_hook_original']) . "' -->";

    // If there are theme suggestions, reverse the array so more specific
    // suggestions are shown first.
    if (!empty($variables['theme_hook_suggestions'])) {
      $variables['theme_hook_suggestions'] = array_reverse($variables['theme_hook_suggestions']);
    }

    // Add debug output for directly called suggestions like
    // '#theme' => 'comment__node__article'.
    if (strpos($variables['theme_hook_original'], '__') !== FALSE) {
      $derived_suggestions[] = $hook = $variables['theme_hook_original'];
      while ($pos = strrpos($hook, '__')) {
        $hook = substr($hook, 0, $pos);
        $derived_suggestions[] = $hook;
      }

      // Get the value of the base hook (last derived suggestion) and append it
      // to the end of all theme suggestions.
      $base_hook = array_pop($derived_suggestions);
      $variables['theme_hook_suggestions'] = array_merge($derived_suggestions, $variables['theme_hook_suggestions']);
      $variables['theme_hook_suggestions'][] = $base_hook;
    }
    if (!empty($variables['theme_hook_suggestions'])) {
      $extension = twig_extension();
      $current_template = basename($template_file);
      $suggestions = $variables['theme_hook_suggestions'];

      // Only add the original theme hook if it wasn't a directly called
      // suggestion.
      if (strpos($variables['theme_hook_original'], '__') === FALSE) {
        $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<!-- FILE NAME SUGGESTIONS:\n   " . Html::escape(implode("\n   ", $suggestions)) . "\n-->";
    }
    $output['debug_info'] .= "\n<!-- BEGIN OUTPUT from '" . Html::escape($template_file) . "' -->\n";
    $output['debug_suffix'] .= "\n<!-- END OUTPUT from '" . Html::escape($template_file) . "' -->\n\n";
  }

  // This output has already been rendered and is therefore considered safe.
  return Markup::create(implode('', $output));
}

/**
 * Removes child elements from a copy of the original array.
 *
 * Creates a copy of the renderable array and removes child elements by key
 * specified through filter's arguments. The copy can be printed without these
 * elements. The original renderable array is still available and can be used
 * to print child elements in their entirety in the twig template.
 *
 * @param array|object $element
 *   The parent renderable array to exclude the child items.
 * @param string[] $args, ...
 *   The string keys of $element to prevent printing.
 *
 * @return array
 *   The filtered renderable array.
 */
function twig_without($element) {
  if ($element instanceof ArrayAccess) {
    $filtered_element = clone $element;
  }
  else {
    $filtered_element = $element;
  }
  $args = func_get_args();
  unset($args[0]);
  foreach ($args as $arg) {
    if (isset($filtered_element[$arg])) {
      unset($filtered_element[$arg]);
    }
  }
  return $filtered_element;
}

Functions

Namesort descending Description
twig_extension Implements hook_extension().
twig_init Implements hook_init().
twig_render_template Implements hook_render_template().
twig_theme Implements hook_theme().
twig_without Removes child elements from a copy of the original array.