diff --git a/src/wp-includes/block-template.php b/src/wp-includes/block-template.php index 945b96b027..b267c4ea8f 100644 --- a/src/wp-includes/block-template.php +++ b/src/wp-includes/block-template.php @@ -45,7 +45,7 @@ function locate_block_template( $template, $type, array $templates ) { $templates = array_slice( $templates, 0, $index + 1 ); } - $block_template = resolve_block_template( $type, $templates ); + $block_template = resolve_block_template( $type, $templates, $template ); if ( $block_template ) { if ( empty( $block_template->content ) && is_user_logged_in() ) { @@ -92,12 +92,14 @@ function locate_block_template( $template, $type, array $templates ) { * * @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 ) { +function resolve_block_template( $template_type, $template_hierarchy, $fallback_template ) { if ( ! $template_type ) { return null; } @@ -129,6 +131,43 @@ function resolve_block_template( $template_type, $template_hierarchy ) { } ); + $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; } diff --git a/tests/phpunit/tests/block-template.php b/tests/phpunit/tests/block-template.php index 94cea83896..95dab825b9 100644 --- a/tests/phpunit/tests/block-template.php +++ b/tests/phpunit/tests/block-template.php @@ -89,16 +89,10 @@ class Tests_Block_Template extends WP_UnitTestCase { * otherwise equal specificity. * * Covers https://github.com/WordPress/gutenberg/pull/31123. + * Covers https://core.trac.wordpress.org/ticket/54515. * */ function test_child_theme_php_template_takes_precedence_over_equally_specific_parent_theme_block_template() { - /** - * @todo This test is currently marked as skipped, since it wouldn't pass. Turns out that in Gutenberg, - * it only passed due to a erroneous test setup. - * For details, see https://github.com/WordPress/wordpress-develop/pull/1920#issuecomment-975929818. - */ - $this->markTestSkipped( 'The block template resolution algorithm needs fixing in order for this test to pass.' ); - switch_theme( 'block-theme-child' ); $page_slug_template = 'page-home.php';