resolve_block_template

The timeline below displays how wordpress function resolve_block_template has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 6.3

/**
 * Returns the correct 'wp_template' to render for the request template type.
 *
 * @access private
 * @since 5.8.0
 * @since 5.9.0 Added the `$fallback_template` parameter.
 *
 * @param string   $template_type      The current template type.
 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority.
 * @param string   $fallback_template  A PHP fallback template to use if no matching block template is found.
 * @return WP_Block_Template|null template A template object, or null if none could be found.
 */
function resolve_block_template($template_type, $template_hierarchy, $fallback_template)
{
    if (!$template_type) {
        return null;
    }
    if (empty($template_hierarchy)) {
        $template_hierarchy = array($template_type);
    }
    $slugs = array_map('_strip_template_file_suffix', $template_hierarchy);
    // Find all potential templates 'wp_template' post matching the hierarchy.
    $query = array('slug__in' => $slugs);
    $templates = get_block_templates($query);
    // Order these templates per slug priority.
    // Build map of template slugs to their priority in the current hierarchy.
    $slug_priorities = array_flip($slugs);
    usort($templates, static function ($template_a, $template_b) use ($slug_priorities) {
        return $slug_priorities[$template_a->slug] - $slug_priorities[$template_b->slug];
    });
    $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
    $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
    // Is the active theme a child theme, and is the PHP fallback template part of it?
    if (str_starts_with($fallback_template, $theme_base_path) && !str_contains($fallback_template, $parent_theme_base_path)) {
        $fallback_template_slug = substr(
            $fallback_template,
            // Starting position of slug.
            strpos($fallback_template, $theme_base_path) + strlen($theme_base_path),
            // Remove '.php' suffix.
            -4
        );
        // Is our candidate block template's slug identical to our PHP fallback template's?
        if (count($templates) && $fallback_template_slug === $templates[0]->slug && 'theme' === $templates[0]->source) {
            // Unfortunately, we cannot trust $templates[0]->theme, since it will always
            // be set to the active theme's slug by _build_block_template_result_from_file(),
            // even if the block template is really coming from the active theme's parent.
            // (The reason for this is that we want it to be associated with the active theme
            // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.)
            // Instead, we use _get_block_template_file() to locate the block template file.
            $template_file = _get_block_template_file('wp_template', $fallback_template_slug);
            if ($template_file && get_template() === $template_file['theme']) {
                // The block template is part of the parent theme, so we
                // have to give precedence to the child theme's PHP template.
                array_shift($templates);
            }
        }
    }
    return count($templates) ? $templates[0] : null;
}

WordPress Version: 1.1

/**
 * Returns the correct 'wp_template' to render for the request template type.
 *
 * @access private
 * @since 5.8.0
 * @since 5.9.0 Added the `$fallback_template` parameter.
 *
 * @param string   $template_type      The current template type.
 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority.
 * @param string   $fallback_template  A PHP fallback template to use if no matching block template is found.
 * @return WP_Block_Template|null template A template object, or null if none could be found.
 */
function resolve_block_template($template_type, $template_hierarchy, $fallback_template)
{
    if (!$template_type) {
        return null;
    }
    if (empty($template_hierarchy)) {
        $template_hierarchy = array($template_type);
    }
    $slugs = array_map('_strip_template_file_suffix', $template_hierarchy);
    // Find all potential templates 'wp_template' post matching the hierarchy.
    $query = array('theme' => get_stylesheet(), 'slug__in' => $slugs);
    $templates = get_block_templates($query);
    // Order these templates per slug priority.
    // Build map of template slugs to their priority in the current hierarchy.
    $slug_priorities = array_flip($slugs);
    usort($templates, static function ($template_a, $template_b) use ($slug_priorities) {
        return $slug_priorities[$template_a->slug] - $slug_priorities[$template_b->slug];
    });
    $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
    $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
    // Is the active theme a child theme, and is the PHP fallback template part of it?
    if (strpos($fallback_template, $theme_base_path) === 0 && strpos($fallback_template, $parent_theme_base_path) === false) {
        $fallback_template_slug = substr(
            $fallback_template,
            // Starting position of slug.
            strpos($fallback_template, $theme_base_path) + strlen($theme_base_path),
            // Remove '.php' suffix.
            -4
        );
        // Is our candidate block template's slug identical to our PHP fallback template's?
        if (count($templates) && $fallback_template_slug === $templates[0]->slug && 'theme' === $templates[0]->source) {
            // Unfortunately, we cannot trust $templates[0]->theme, since it will always
            // be set to the active theme's slug by _build_block_template_result_from_file(),
            // even if the block template is really coming from the active theme's parent.
            // (The reason for this is that we want it to be associated with the active theme
            // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.)
            // Instead, we use _get_block_template_file() to locate the block template file.
            $template_file = _get_block_template_file('wp_template', $fallback_template_slug);
            if ($template_file && get_template() === $template_file['theme']) {
                // The block template is part of the parent theme, so we
                // have to give precedence to the child theme's PHP template.
                array_shift($templates);
            }
        }
    }
    return count($templates) ? $templates[0] : null;
}

WordPress Version: 6.1

/**
 * Returns the correct 'wp_template' to render for the request template type.
 *
 * @access private
 * @since 5.8.0
 * @since 5.9.0 Added the `$fallback_template` parameter.
 *
 * @param string   $template_type      The current template type.
 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority.
 * @param string   $fallback_template  A PHP fallback template to use if no matching block template is found.
 * @return WP_Block_Template|null template A template object, or null if none could be found.
 */
function resolve_block_template($template_type, $template_hierarchy, $fallback_template)
{
    if (!$template_type) {
        return null;
    }
    if (empty($template_hierarchy)) {
        $template_hierarchy = array($template_type);
    }
    $slugs = array_map('_strip_template_file_suffix', $template_hierarchy);
    // Find all potential templates 'wp_template' post matching the hierarchy.
    $query = array('theme' => wp_get_theme()->get_stylesheet(), 'slug__in' => $slugs);
    $templates = get_block_templates($query);
    // Order these templates per slug priority.
    // Build map of template slugs to their priority in the current hierarchy.
    $slug_priorities = array_flip($slugs);
    usort($templates, static function ($template_a, $template_b) use ($slug_priorities) {
        return $slug_priorities[$template_a->slug] - $slug_priorities[$template_b->slug];
    });
    $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
    $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
    // Is the active theme a child theme, and is the PHP fallback template part of it?
    if (strpos($fallback_template, $theme_base_path) === 0 && strpos($fallback_template, $parent_theme_base_path) === false) {
        $fallback_template_slug = substr(
            $fallback_template,
            // Starting position of slug.
            strpos($fallback_template, $theme_base_path) + strlen($theme_base_path),
            // Remove '.php' suffix.
            -4
        );
        // Is our candidate block template's slug identical to our PHP fallback template's?
        if (count($templates) && $fallback_template_slug === $templates[0]->slug && 'theme' === $templates[0]->source) {
            // Unfortunately, we cannot trust $templates[0]->theme, since it will always
            // be set to the active theme's slug by _build_block_template_result_from_file(),
            // even if the block template is really coming from the active theme's parent.
            // (The reason for this is that we want it to be associated with the active theme
            // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.)
            // Instead, we use _get_block_template_file() to locate the block template file.
            $template_file = _get_block_template_file('wp_template', $fallback_template_slug);
            if ($template_file && get_template() === $template_file['theme']) {
                // The block template is part of the parent theme, so we
                // have to give precedence to the child theme's PHP template.
                array_shift($templates);
            }
        }
    }
    return count($templates) ? $templates[0] : null;
}

WordPress Version: 5.9

/**
 * Return the correct 'wp_template' to render for the request template type.
 *
 * @access private
 * @since 5.8.0
 * @since 5.9.0 Added the `$fallback_template` parameter.
 *
 * @param string   $template_type      The current template type.
 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority.
 * @param string   $fallback_template  A PHP fallback template to use if no matching block template is found.
 * @return WP_Block_Template|null template A template object, or null if none could be found.
 */
function resolve_block_template($template_type, $template_hierarchy, $fallback_template)
{
    if (!$template_type) {
        return null;
    }
    if (empty($template_hierarchy)) {
        $template_hierarchy = array($template_type);
    }
    $slugs = array_map('_strip_template_file_suffix', $template_hierarchy);
    // Find all potential templates 'wp_template' post matching the hierarchy.
    $query = array('theme' => wp_get_theme()->get_stylesheet(), 'slug__in' => $slugs);
    $templates = get_block_templates($query);
    // Order these templates per slug priority.
    // Build map of template slugs to their priority in the current hierarchy.
    $slug_priorities = array_flip($slugs);
    usort($templates, static function ($template_a, $template_b) use ($slug_priorities) {
        return $slug_priorities[$template_a->slug] - $slug_priorities[$template_b->slug];
    });
    $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
    $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
    // Is the current theme a child theme, and is the PHP fallback template part of it?
    if (strpos($fallback_template, $theme_base_path) === 0 && strpos($fallback_template, $parent_theme_base_path) === false) {
        $fallback_template_slug = substr(
            $fallback_template,
            // Starting position of slug.
            strpos($fallback_template, $theme_base_path) + strlen($theme_base_path),
            // Remove '.php' suffix.
            -4
        );
        // Is our candidate block template's slug identical to our PHP fallback template's?
        if (count($templates) && $fallback_template_slug === $templates[0]->slug && 'theme' === $templates[0]->source) {
            // Unfortunately, we cannot trust $templates[0]->theme, since it will always
            // be set to the current theme's slug by _build_block_template_result_from_file(),
            // even if the block template is really coming from the current theme's parent.
            // (The reason for this is that we want it to be associated with the current theme
            // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.)
            // Instead, we use _get_block_template_file() to locate the block template file.
            $template_file = _get_block_template_file('wp_template', $fallback_template_slug);
            if ($template_file && get_template() === $template_file['theme']) {
                // The block template is part of the parent theme, so we
                // have to give precedence to the child theme's PHP template.
                array_shift($templates);
            }
        }
    }
    return count($templates) ? $templates[0] : null;
}

WordPress Version: 5.8

/**
 * Return the correct 'wp_template' to render for the request template type.
 *
 * @access private
 * @since 5.8.0
 *
 * @param string   $template_type      The current template type.
 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority.
 * @return WP_Block_Template|null template A template object, or null if none could be found.
 */
function resolve_block_template($template_type, $template_hierarchy)
{
    if (!$template_type) {
        return null;
    }
    if (empty($template_hierarchy)) {
        $template_hierarchy = array($template_type);
    }
    $slugs = array_map('_strip_template_file_suffix', $template_hierarchy);
    // Find all potential templates 'wp_template' post matching the hierarchy.
    $query = array('theme' => wp_get_theme()->get_stylesheet(), 'slug__in' => $slugs);
    $templates = get_block_templates($query);
    // Order these templates per slug priority.
    // Build map of template slugs to their priority in the current hierarchy.
    $slug_priorities = array_flip($slugs);
    usort($templates, function ($template_a, $template_b) use ($slug_priorities) {
        return $slug_priorities[$template_a->slug] - $slug_priorities[$template_b->slug];
    });
    return count($templates) ? $templates[0] : null;
}