diff --git a/src/wp-includes/block-template.php b/src/wp-includes/block-template.php index ffed0e0436..7030e2e2a1 100644 --- a/src/wp-includes/block-template.php +++ b/src/wp-includes/block-template.php @@ -208,6 +208,7 @@ function _block_template_render_title_tag() { * @access private * @since 5.8.0 * + * @global string $_wp_current_template_id * @global string $_wp_current_template_content * @global WP_Embed $wp_embed * @global WP_Query $wp_query @@ -215,7 +216,7 @@ function _block_template_render_title_tag() { * @return string Block template markup. */ function get_the_block_template_html() { - global $_wp_current_template_content, $wp_embed, $wp_query; + global $_wp_current_template_id, $_wp_current_template_content, $wp_embed, $wp_query; if ( ! $_wp_current_template_content ) { if ( is_user_logged_in() ) { @@ -242,8 +243,18 @@ function get_the_block_template_html() { * Even if the block template contained a `core/query` and `core/post-template` block referencing the main query * loop, it would not cause errors since it would use a cloned instance and go through the same loop of a single * post, within the actual main query loop. + * + * This special logic should be skipped if the current template does not come from the current theme, in which case + * it has been injected by a plugin by hijacking the block template loader mechanism. In that case, entirely custom + * logic may be applied which is unpredictable and therefore safer to omit this special handling on. */ - if ( is_singular() && 1 === $wp_query->post_count && have_posts() ) { + if ( + $_wp_current_template_id && + str_starts_with( $_wp_current_template_id, get_stylesheet() . '//' ) && + is_singular() && + 1 === $wp_query->post_count && + have_posts() + ) { while ( have_posts() ) { the_post(); $content = do_blocks( $content ); diff --git a/tests/phpunit/tests/block-template.php b/tests/phpunit/tests/block-template.php index d7ffad7f90..8e86254f29 100644 --- a/tests/phpunit/tests/block-template.php +++ b/tests/phpunit/tests/block-template.php @@ -19,8 +19,8 @@ class Tests_Block_Template extends WP_UnitTestCase { } public function tear_down() { - global $_wp_current_template_content; - unset( $_wp_current_template_content ); + global $_wp_current_template_id, $_wp_current_template_content; + unset( $_wp_current_template_id, $_wp_current_template_content ); parent::tear_down(); } @@ -193,10 +193,11 @@ class Tests_Block_Template extends WP_UnitTestCase { * since there is only a single post in the main query loop in such cases anyway. * * @ticket 58154 + * @ticket 59736 * @covers ::get_the_block_template_html */ public function test_get_the_block_template_html_enforces_singular_query_loop() { - global $_wp_current_template_content, $wp_query, $wp_the_query; + global $_wp_current_template_id, $_wp_current_template_content, $wp_query, $wp_the_query; // Register test block to log `in_the_loop()` results. $in_the_loop_logs = array(); @@ -207,6 +208,8 @@ class Tests_Block_Template extends WP_UnitTestCase { $wp_query = new WP_Query( array( 'p' => $post_id ) ); $wp_the_query = $wp_query; + // Force a template ID that is for the current stylesheet. + $_wp_current_template_id = get_stylesheet() . '//single'; // Use block template that just renders post title and the above test block. $_wp_current_template_content = ''; @@ -227,7 +230,7 @@ class Tests_Block_Template extends WP_UnitTestCase { * @covers ::get_the_block_template_html */ public function test_get_the_block_template_html_does_not_generally_enforce_loop() { - global $_wp_current_template_content, $wp_query, $wp_the_query; + global $_wp_current_template_id, $_wp_current_template_content, $wp_query, $wp_the_query; // Register test block to log `in_the_loop()` results. $in_the_loop_logs = array(); @@ -248,6 +251,9 @@ class Tests_Block_Template extends WP_UnitTestCase { ); $wp_the_query = $wp_query; + // Force a template ID that is for the current stylesheet. + $_wp_current_template_id = get_stylesheet() . '//home'; + /* * Use block template that renders the above test block, followed by a main query loop. * `get_the_block_template_html()` should not start the loop, but the `core/query` and `core/post-template` @@ -276,6 +282,35 @@ class Tests_Block_Template extends WP_UnitTestCase { $this->assertSame( array( false, true ), $in_the_loop_logs, 'Main query loop was triggered incorrectly' ); } + /** + * Tests that `get_the_block_template_html()` does not start the main query loop when on a template that is not from the current theme. + * + * @ticket 58154 + * @ticket 59736 + * @covers ::get_the_block_template_html + */ + public function test_get_the_block_template_html_skips_singular_query_loop_when_non_theme_template() { + global $_wp_current_template_id, $_wp_current_template_content, $wp_query, $wp_the_query; + + // Register test block to log `in_the_loop()` results. + $in_the_loop_logs = array(); + $this->register_in_the_loop_logger_block( $in_the_loop_logs ); + + // Set main query to single post. + $post_id = self::factory()->post->create( array( 'post_title' => 'A single post' ) ); + $wp_query = new WP_Query( array( 'p' => $post_id ) ); + $wp_the_query = $wp_query; + + // Force a template ID that is not for the current stylesheet. + $_wp_current_template_id = 'some-plugin-slug//single'; + // Use block template that just renders post title and the above test block. + $_wp_current_template_content = ''; + + $output = get_the_block_template_html(); + $this->unregister_in_the_loop_logger_block(); + $this->assertSame( array( false ), $in_the_loop_logs, 'Main query loop was triggered despite a custom block template outside the current theme being used' ); + } + /** * @ticket 58319 * diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 3b8615dd82..8d91517b1f 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -79,6 +79,9 @@ CAP; * Ensures that the static content media count, fetchpriority element flag and related filter are reset between tests. */ public function tear_down() { + global $_wp_current_template_id, $_wp_current_template_content; + unset( $_wp_current_template_id, $_wp_current_template_content ); + parent::tear_down(); $this->reset_content_media_count(); @@ -3972,7 +3975,7 @@ EOF; * @covers ::wp_get_loading_optimization_attributes */ public function test_wp_filter_content_tags_does_not_lazy_load_first_image_in_block_theme() { - global $_wp_current_template_content, $wp_query, $wp_the_query, $post; + global $_wp_current_template_id, $_wp_current_template_content, $wp_query, $wp_the_query, $post; // Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test. add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); @@ -4001,6 +4004,8 @@ EOF; $wp_the_query = $wp_query; $post = get_post( self::$post_ids['publish'] ); + // Force a template ID that is for the current stylesheet. + $_wp_current_template_id = get_stylesheet() . '//single'; $_wp_current_template_content = ''; $html = get_the_block_template_html(); @@ -4020,7 +4025,7 @@ EOF; * @covers ::wp_get_loading_optimization_attributes */ public function test_wp_filter_content_tags_does_not_lazy_load_first_featured_image_in_block_theme() { - global $_wp_current_template_content, $wp_query, $wp_the_query, $post; + global $_wp_current_template_id, $_wp_current_template_content, $wp_query, $wp_the_query, $post; // Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test. add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); @@ -4069,6 +4074,8 @@ EOF; $wp_the_query = $wp_query; $post = get_post( self::$post_ids['publish'] ); + // Force a template ID that is for the current stylesheet. + $_wp_current_template_id = get_stylesheet() . '//single'; $_wp_current_template_content = ' '; $html = get_the_block_template_html(); @@ -4087,7 +4094,7 @@ EOF; * @covers ::wp_get_loading_optimization_attributes */ public function test_wp_filter_content_tags_does_not_lazy_load_images_in_header() { - global $_wp_current_template_content; + global $_wp_current_template_id, $_wp_current_template_content; // Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test. add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); @@ -4122,6 +4129,8 @@ EOF; wp_set_post_terms( $footer_post_id, WP_TEMPLATE_PART_AREA_FOOTER, 'wp_template_part_area' ); wp_set_post_terms( $footer_post_id, get_stylesheet(), 'wp_theme' ); + // Force a template ID that is for the current stylesheet. + $_wp_current_template_id = get_stylesheet() . '//single'; $_wp_current_template_content = ''; // Header image should not be lazy-loaded, footer image should be lazy-loaded.