function drupal_find_theme_templates

Same name in other branches
  1. 7.x includes/theme.inc \drupal_find_theme_templates()
  2. 9 core/includes/theme.inc \drupal_find_theme_templates()
  3. 8.9.x core/includes/theme.inc \drupal_find_theme_templates()
  4. 10 core/includes/theme.inc \drupal_find_theme_templates()

Allows themes and/or theme engines to easily discover overridden templates.

Parameters

$cache: The existing cache of theme hooks to test against.

$extension: The extension that these templates will have.

$path: The path to search.

4 calls to drupal_find_theme_templates()
nyan_cat_theme in core/modules/system/tests/themes/engines/nyan_cat/nyan_cat.engine
Implements hook_theme().
ThemeTest::testFindThemeTemplates in core/modules/system/tests/src/Kernel/Theme/ThemeTest.php
Tests drupal_find_theme_templates().
TwigDebugMarkupTest::testTwigDebugMarkup in core/modules/system/tests/src/Functional/Theme/TwigDebugMarkupTest.php
Tests debug markup added to Twig template output.
twig_theme in core/themes/engines/twig/twig.engine
Implements hook_theme().

File

core/includes/theme.inc, line 94

Code

function drupal_find_theme_templates($cache, $extension, $path) {
    $implementations = [];
    // Collect paths to all sub-themes grouped by base themes. These will be
    // used for filtering. This allows base themes to have sub-themes in its
    // folder hierarchy without affecting the base themes template discovery.
    $theme_paths = [];
    foreach (\Drupal::service('theme_handler')->listInfo() as $theme_info) {
        if (!empty($theme_info->base_theme)) {
            $theme_paths[$theme_info->base_theme][$theme_info->getName()] = $theme_info->getPath();
        }
    }
    foreach ($theme_paths as $basetheme => $subthemes) {
        foreach ($subthemes as $subtheme => $subtheme_path) {
            if (isset($theme_paths[$subtheme])) {
                $theme_paths[$basetheme] = array_merge($theme_paths[$basetheme], $theme_paths[$subtheme]);
            }
        }
    }
    $theme = \Drupal::theme()->getActiveTheme()
        ->getName();
    $subtheme_paths = $theme_paths[$theme] ?? [];
    // Escape the periods in the extension.
    $regex = '/' . str_replace('.', '\\.', $extension) . '$/';
    // Get a listing of all template files in the path to search.
    $files = [];
    if (is_dir($path)) {
        $files = \Drupal::service('file_system')->scanDirectory($path, $regex, [
            'key' => 'filename',
        ]);
    }
    // Find templates that implement registered theme hooks and include that in
    // what is returned so that the registry knows that the theme has this
    // implementation.
    foreach ($files as $template => $file) {
        // Ignore sub-theme templates for the current theme.
        if (!str_starts_with($file->uri, str_replace($subtheme_paths, '', $file->uri))) {
            continue;
        }
        // Remove the extension from the filename.
        $template = str_replace($extension, '', $template);
        // Transform - in filenames to _ to match function naming scheme
        // for the purposes of searching.
        $hook = strtr($template, '-', '_');
        if (isset($cache[$hook])) {
            $implementations[$hook] = [
                'template' => $template,
                'path' => dirname($file->uri),
            ];
        }
        // Match templates based on the 'template' filename.
        foreach ($cache as $hook => $info) {
            if (isset($info['template'])) {
                if ($template === $info['template']) {
                    $implementations[$hook] = [
                        'template' => $template,
                        'path' => dirname($file->uri),
                    ];
                }
            }
        }
    }
    // Find templates that implement possible "suggestion" variants of registered
    // theme hooks and add those as new registered theme hooks. See
    // hook_theme_suggestions_alter() for more information about suggestions and
    // the use of 'pattern' and 'base hook'.
    $patterns = array_keys($files);
    foreach ($cache as $hook => $info) {
        $pattern = $info['pattern'] ?? $hook . '__';
        if (!isset($info['base hook']) && !empty($pattern)) {
            // Transform _ in pattern to - to match file naming scheme
            // for the purposes of searching.
            $pattern = strtr($pattern, '_', '-');
            $matches = preg_grep('/^' . $pattern . '/', $patterns);
            if ($matches) {
                foreach ($matches as $match) {
                    $file = $match;
                    // Remove the extension from the filename.
                    $file = str_replace($extension, '', $file);
                    // Put the underscores back in for the hook name and register this
                    // pattern.
                    $arg_name = isset($info['variables']) ? 'variables' : 'render element';
                    $implementations[strtr($file, '-', '_')] = [
                        'template' => $file,
                        'path' => dirname($files[$match]->uri),
                        $arg_name => $info[$arg_name],
                        'base hook' => $hook,
                    ];
                }
            }
        }
    }
    return $implementations;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.