From 15f5a952a1b804020d0e2677058f38450a874e53 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Tue, 12 Sep 2023 19:18:34 +0000 Subject: [PATCH] Posts, Post Types: Avoid unnecessarily parsing blocks twice in `wp_trim_excerpt()`. All blocks relevant for the excerpt are already being parsed in `excerpt_remove_blocks()`. Therefore running `do_blocks()` on the post content only to create the excerpt is unnecessary and wasteful from a performance perspective. Props thekt12, spacedmonkey, mukesh27, joemcgill. Fixes #58682. git-svn-id: https://develop.svn.wordpress.org/trunk@56560 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/blocks.php | 4 + src/wp-includes/formatting.php | 17 +++- .../tests/formatting/wpTrimExcerpt.php | 81 +++++++++++++++++++ 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 3c88f7b4c381a..7795a6969b113 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -956,6 +956,10 @@ function filter_block_kses_value( $value, $allowed_html, $allowed_protocols = ar * @return string The parsed and filtered content. */ function excerpt_remove_blocks( $content ) { + if ( ! has_blocks( $content ) ) { + return $content; + } + $allowed_inner_blocks = array( // Classic blocks have their blockName set to null. null, diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 05084329d333e..0e5797fa7e619 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -3980,18 +3980,29 @@ function wp_trim_excerpt( $text = '', $post = null ) { * within the excerpt are stripped out. Modifying the tags here * is wasteful and can lead to bugs in the image counting logic. */ - $filter_removed = remove_filter( 'the_content', 'wp_filter_content_tags' ); + $filter_image_removed = remove_filter( 'the_content', 'wp_filter_content_tags' ); + + /* + * Temporarily unhook do_blocks() since excerpt_remove_blocks( $text ) + * handels block rendering needed for excerpt. + */ + $filter_block_removed = remove_filter( 'the_content', 'do_blocks', 9 ); /** This filter is documented in wp-includes/post-template.php */ $text = apply_filters( 'the_content', $text ); $text = str_replace( ']]>', ']]>', $text ); - /** + // Restore the original filter if removed. + if ( $filter_block_removed ) { + add_filter( 'the_content', 'do_blocks', 9 ); + } + + /* * Only restore the filter callback if it was removed above. The logic * to unhook and restore only applies on the default priority of 10, * which is generally used for the filter callback in WordPress core. */ - if ( $filter_removed ) { + if ( $filter_image_removed ) { add_filter( 'the_content', 'wp_filter_content_tags' ); } diff --git a/tests/phpunit/tests/formatting/wpTrimExcerpt.php b/tests/phpunit/tests/formatting/wpTrimExcerpt.php index ba5e99f7d8074..d77f322058388 100644 --- a/tests/phpunit/tests/formatting/wpTrimExcerpt.php +++ b/tests/phpunit/tests/formatting/wpTrimExcerpt.php @@ -148,4 +148,85 @@ public function test_wp_trim_excerpt_does_not_restore_wp_filter_content_tags_if_ // Assert that the filter callback was not restored after running 'the_content'. $this->assertFalse( has_filter( 'the_content', 'wp_filter_content_tags' ) ); } + + /** + * Tests that `wp_trim_excerpt()` does process valid blocks. + * + * @ticket 58682 + * + * @covers ::wp_trim_excerpt + */ + public function test_wp_trim_excerpt_check_if_block_renders() { + $post = self::factory()->post->create( + array( + 'post_content' => '

A test paragraph

', + ) + ); + + $output_text = wp_trim_excerpt( '', $post ); + + $this->assertSame( 'A test paragraph', $output_text, 'wp_trim_excerpt() did not process paragraph block.' ); + } + + /** + * Tests that `wp_trim_excerpt()` unhooks `do_blocks()` from 'the_content' filter. + * + * @ticket 58682 + * + * @covers ::wp_trim_excerpt + */ + public function test_wp_trim_excerpt_unhooks_do_blocks() { + $post = self::factory()->post->create(); + + /* + * Record that during 'the_content' filter run by wp_trim_excerpt() the + * do_blocks() callback is not used. + */ + $has_filter = true; + add_filter( + 'the_content', + static function( $content ) use ( &$has_filter ) { + $has_filter = has_filter( 'the_content', 'do_blocks' ); + return $content; + } + ); + + wp_trim_excerpt( '', $post ); + + $this->assertFalse( $has_filter, 'do_blocks() was not unhooked in wp_trim_excerpt()' ); + } + + /** + * Tests that `wp_trim_excerpt()` doesn't permanently unhook `do_blocks()` from 'the_content' filter. + * + * @ticket 58682 + * + * @covers ::wp_trim_excerpt + */ + public function test_wp_trim_excerpt_should_not_permanently_unhook_do_blocks() { + $post = self::factory()->post->create(); + + wp_trim_excerpt( '', $post ); + + $this->assertSame( 9, has_filter( 'the_content', 'do_blocks' ), 'do_blocks() was not restored in wp_trim_excerpt()' ); + } + + /** + * Tests that `wp_trim_excerpt()` doesn't restore `do_blocks()` if it was previously unhooked. + * + * @ticket 58682 + * + * @covers ::wp_trim_excerpt + */ + public function test_wp_trim_excerpt_does_not_restore_do_blocks_if_previously_unhooked() { + $post = self::factory()->post->create(); + + // Remove do_blocks() from 'the_content' filter generally. + remove_filter( 'the_content', 'do_blocks', 9 ); + + wp_trim_excerpt( '', $post ); + + // Assert that the filter callback was not restored after running 'the_content'. + $this->assertFalse( has_filter( 'the_content', 'do_blocks' ) ); + } }