twig.engine
Same filename in other branches
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 Twig\Error\RuntimeError;
/**
* Implements hook_theme().
*/
function twig_theme($existing, $type, $theme, $path) {
return drupal_find_theme_templates($existing, '.html.twig', $path);
}
/**
* Implements hook_extension().
*/
function twig_extension() {
return '.html.twig';
}
/**
* Implements hook_render_template().
*/
function twig_render_template($template_file, array $variables) {
/** @var \Twig\Environment $twig_service */
$twig_service = \Drupal::service('twig');
try {
$rendered_markup = $twig_service->load($template_file)
->render($variables);
} catch (RuntimeError $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' => '',
'debug_info' => '',
'rendered_markup' => $rendered_markup,
'debug_suffix' => '',
];
$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 (str_contains($variables['theme_hook_original'], '__')) {
$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 (!str_contains($variables['theme_hook_original'], '__')) {
$suggestions[] = $variables['theme_hook_original'];
}
$invalid_suggestions = [];
$base_hook = $base_hook ?? $variables['theme_hook_original'];
foreach ($suggestions as $key => &$suggestion) {
// Valid suggestions are $base_hook, $base_hook__*, and contain no hyphens.
if ($suggestion !== $base_hook && !str_starts_with($suggestion, $base_hook . '__') || str_contains($suggestion, '-')) {
$invalid_suggestions[] = $suggestion;
unset($suggestions[$key]);
continue;
}
$template = strtr($suggestion, '_', '-') . $extension;
$prefix = $template == $current_template ? '✅' : '▪️';
$suggestion = $prefix . ' ' . $template;
}
$output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n " . Html::escape(implode("\n ", $suggestions)) . "\n-->";
if (!empty($invalid_suggestions)) {
$output['debug_info'] .= "\n<!-- INVALID FILE NAME SUGGESTIONS:";
$output['debug_info'] .= "\n See https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Render!theme.api.php/function/hook_theme_suggestions_alter";
$output['debug_info'] .= "\n " . Html::escape(implode("\n ", $invalid_suggestions));
$output['debug_info'] .= "\n-->";
}
}
// Check if the template_file belongs to a custom theme
$template_override_status_output = "BEGIN OUTPUT";
$template_override_suffix_output = "END OUTPUT";
if (str_starts_with($template_file, $variables['directory'])) {
$template_override_status_output = "💡 BEGIN CUSTOM TEMPLATE OUTPUT";
$template_override_suffix_output = "END CUSTOM TEMPLATE OUTPUT";
}
$output['debug_info'] .= "\n<!-- " . $template_override_status_output . " from '" . Html::escape($template_file) . "' -->\n";
$output['debug_suffix'] .= "\n<!-- " . $template_override_suffix_output . " from '" . Html::escape($template_file) . "' -->\n\n";
// This output has already been rendered and is therefore considered safe.
return Markup::create(implode('', $output));
}
return Markup::create($rendered_markup);
}
Functions
Title | Deprecated | Summary |
---|---|---|
twig_extension | Implements hook_extension(). | |
twig_render_template | Implements hook_render_template(). | |
twig_theme | Implements hook_theme(). |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.