mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2026-06-28 14:20:15 +00:00
Media: Fix lazy-loading bug by avoiding to modify content images when creating an excerpt.
The `wp_filter_content_tags()` function, which modifies image tags for example to optimize performance, is hooked into the `the_content` filter by default. When rendering an excerpt for a post that doesn't have a manually provided excerpt, the post content is used to generate the excerpt, handled by the `wp_trim_excerpt()` function. Prior to this changeset, this led to `wp_filter_content_tags()` being called on the content when generating the excerpt, which is wasteful as all tags are stripped from the excerpt, and it furthermore could result in a lazy-loading bug when the post content contained images, as those images were being counted even though they would never be rendered as part of the excerpt. This changeset fixes the bug and slightly improves performance for generating an excerpt by temporarily unhooking the `wp_filter_content_tags()` function from the `the_content` filter when using it to generate the excerpt. Props costdev, flixos90, joemcgill, mukesh27, salvoaranzulla, spacedmonkey, thekt12, westonruter. Fixes #56588. git-svn-id: https://develop.svn.wordpress.org/trunk@55850 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
@@ -3937,10 +3937,26 @@ function wp_trim_excerpt( $text = '', $post = null ) {
|
||||
$text = strip_shortcodes( $text );
|
||||
$text = excerpt_remove_blocks( $text );
|
||||
|
||||
/*
|
||||
* Temporarily unhook wp_filter_content_tags() since any tags
|
||||
* 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' );
|
||||
|
||||
/** This filter is documented in wp-includes/post-template.php */
|
||||
$text = apply_filters( 'the_content', $text );
|
||||
$text = str_replace( ']]>', ']]>', $text );
|
||||
|
||||
/**
|
||||
* 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 ) {
|
||||
add_filter( 'the_content', 'wp_filter_content_tags' );
|
||||
}
|
||||
|
||||
/* translators: Maximum number of words used in a post excerpt. */
|
||||
$excerpt_length = (int) _x( '55', 'excerpt_length' );
|
||||
|
||||
|
||||
@@ -92,4 +92,60 @@ class Tests_Formatting_wpTrimExcerpt extends WP_UnitTestCase {
|
||||
$this->assertSame( 'Post content', wp_trim_excerpt( null, $post ) );
|
||||
$this->assertSame( 'Post content', wp_trim_excerpt( false, $post ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that `wp_trim_excerpt()` unhooks `wp_filter_content_tags()` from 'the_content' filter.
|
||||
*
|
||||
* @ticket 56588
|
||||
*/
|
||||
public function test_wp_trim_excerpt_unhooks_wp_filter_content_tags() {
|
||||
$post = self::factory()->post->create();
|
||||
|
||||
/*
|
||||
* Record that during 'the_content' filter run by wp_trim_excerpt() the
|
||||
* wp_filter_content_tags() callback is not used.
|
||||
*/
|
||||
$has_filter = true;
|
||||
add_filter(
|
||||
'the_content',
|
||||
static function( $content ) use ( &$has_filter ) {
|
||||
$has_filter = has_filter( 'the_content', 'wp_filter_content_tags' );
|
||||
return $content;
|
||||
}
|
||||
);
|
||||
|
||||
wp_trim_excerpt( '', $post );
|
||||
|
||||
$this->assertFalse( $has_filter, 'wp_filter_content_tags() was not unhooked in wp_trim_excerpt()' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that `wp_trim_excerpt()` doesn't permanently unhook `wp_filter_content_tags()` from 'the_content' filter.
|
||||
*
|
||||
* @ticket 56588
|
||||
*/
|
||||
public function test_wp_trim_excerpt_should_not_permanently_unhook_wp_filter_content_tags() {
|
||||
$post = self::factory()->post->create();
|
||||
|
||||
wp_trim_excerpt( '', $post );
|
||||
|
||||
$this->assertSame( 10, has_filter( 'the_content', 'wp_filter_content_tags' ), 'wp_filter_content_tags() was not restored in wp_trim_excerpt()' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that `wp_trim_excerpt()` doesn't restore `wp_filter_content_tags()` if it was previously unhooked.
|
||||
*
|
||||
* @ticket 56588
|
||||
*/
|
||||
public function test_wp_trim_excerpt_does_not_restore_wp_filter_content_tags_if_previously_unhooked() {
|
||||
$post = self::factory()->post->create();
|
||||
|
||||
// Remove wp_filter_content_tags() from 'the_content' filter generally.
|
||||
remove_filter( 'the_content', 'wp_filter_content_tags' );
|
||||
|
||||
wp_trim_excerpt( '', $post );
|
||||
|
||||
// Assert that the filter callback was not restored after running 'the_content'.
|
||||
$this->assertFalse( has_filter( 'the_content', 'wp_filter_content_tags' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,16 @@ CAP;
|
||||
parent::tear_down_after_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the static content media count and related filter are reset between tests.
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
}
|
||||
|
||||
public function test_img_caption_shortcode_added() {
|
||||
global $shortcode_tags;
|
||||
$this->assertSame( 'img_caption_shortcode', $shortcode_tags['caption'] );
|
||||
@@ -3567,8 +3577,6 @@ EOF;
|
||||
$this->assertSame( 'lazy', wp_get_loading_attr_default( $context ) );
|
||||
|
||||
$query = $this->get_new_wp_query_for_published_post();
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
while ( have_posts() ) {
|
||||
the_post();
|
||||
@@ -3613,8 +3621,6 @@ EOF;
|
||||
public function test_wp_omit_loading_attr_threshold_filter() {
|
||||
$query = $this->get_new_wp_query_for_published_post();
|
||||
$this->set_main_query( $query );
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
// Use the filter to alter the threshold for not lazy-loading to the first five elements.
|
||||
$this->force_omit_loading_attr_threshold( 5 );
|
||||
@@ -3655,8 +3661,6 @@ EOF;
|
||||
|
||||
$query = $this->get_new_wp_query_for_published_post();
|
||||
$this->set_main_query( $query );
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
while ( have_posts() ) {
|
||||
the_post();
|
||||
@@ -3707,8 +3711,6 @@ EOF;
|
||||
global $wp_query;
|
||||
|
||||
$wp_query = $this->get_new_wp_query_for_published_post();
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
do_action( 'get_header' );
|
||||
|
||||
@@ -3732,8 +3734,6 @@ EOF;
|
||||
|
||||
$wp_query = $this->get_new_wp_query_for_published_post();
|
||||
$this->set_main_query( $wp_query );
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
// Lazy if header not called.
|
||||
$this->assertSame( 'lazy', wp_get_loading_attr_default( $context ) );
|
||||
@@ -3755,8 +3755,6 @@ EOF;
|
||||
|
||||
$wp_query = $this->get_new_wp_query_for_published_post();
|
||||
$this->set_main_query( $wp_query );
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
do_action( 'get_header' );
|
||||
$this->assertFalse( wp_get_loading_attr_default( $context ) );
|
||||
@@ -3778,8 +3776,6 @@ EOF;
|
||||
|
||||
$wp_query = $this->get_new_wp_query_for_published_post();
|
||||
$this->set_main_query( $wp_query );
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
do_action( 'get_header' );
|
||||
|
||||
@@ -3805,8 +3801,6 @@ EOF;
|
||||
|
||||
$wp_query = $this->get_new_wp_query_for_published_post();
|
||||
$this->set_main_query( $wp_query );
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
// Ensure header and footer is called.
|
||||
do_action( 'get_header' );
|
||||
@@ -3865,8 +3859,6 @@ EOF;
|
||||
$wp_query = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
|
||||
$wp_the_query = $wp_query;
|
||||
$post = get_post( self::$post_ids['publish'] );
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
$_wp_current_template_content = '<!-- wp:post-content /-->';
|
||||
|
||||
@@ -3922,8 +3914,6 @@ EOF;
|
||||
$wp_query = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
|
||||
$wp_the_query = $wp_query;
|
||||
$post = get_post( self::$post_ids['publish'] );
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
$_wp_current_template_content = '<!-- wp:post-featured-image /--> <!-- wp:post-content /-->';
|
||||
|
||||
@@ -4013,8 +4003,7 @@ EOF;
|
||||
*/
|
||||
$wp_query = new WP_Query( array( 'post__in' => array( self::$post_ids['publish'] ) ) );
|
||||
$wp_the_query = $wp_query;
|
||||
$this->reset_content_media_count();
|
||||
$this->reset_omit_loading_attr_filter();
|
||||
|
||||
$content = '';
|
||||
while ( have_posts() ) {
|
||||
the_post();
|
||||
@@ -4078,6 +4067,98 @@ EOF;
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the content media count is not affected by `the_excerpt()` calls for posts that contain images.
|
||||
*
|
||||
* @ticket 56588
|
||||
*
|
||||
* @covers ::wp_trim_excerpt
|
||||
*/
|
||||
public function test_the_excerpt_does_not_affect_content_media_count() {
|
||||
global $wp_query, $wp_the_query;
|
||||
|
||||
/*
|
||||
* Use the filter to alter the threshold for not lazy-loading to the first 2 elements,
|
||||
* then use a post that contains exactly 2 images.
|
||||
*/
|
||||
$this->force_omit_loading_attr_threshold( 2 );
|
||||
$post_content = '<img src="example.jpg" width="800" height="600">';
|
||||
$post_content .= '<p>Some text.</p>';
|
||||
$post_content .= '<img src="example2.jpg" width="800" height="600">';
|
||||
|
||||
$post_id = self::factory()->post->create(
|
||||
array(
|
||||
'post_content' => $post_content,
|
||||
'post_excerpt' => '',
|
||||
)
|
||||
);
|
||||
|
||||
$wp_query = new WP_Query( array( 'post__in' => array( $post_id ) ) );
|
||||
$wp_the_query = $wp_query;
|
||||
|
||||
while ( have_posts() ) {
|
||||
the_post();
|
||||
|
||||
// Call `the_excerpt()` without generating output.
|
||||
get_echo( 'the_excerpt' );
|
||||
}
|
||||
|
||||
// The only way to access the value is by calling this function without increasing the value.
|
||||
$content_media_count = wp_increase_content_media_count( 0 );
|
||||
|
||||
// Assert that the media count was not increased even though there are 3 images in the post's content.
|
||||
$this->assertSame( 0, $content_media_count );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the lazy-loading result is not affected by `the_excerpt()` calls for posts that
|
||||
* contain images.
|
||||
*
|
||||
* Printing the excerpt for a post that contains images in its content prior to its featured image should result in
|
||||
* that featured image not being lazy-loaded, since the images in the post content aren't displayed in the excerpt.
|
||||
*
|
||||
* @ticket 56588
|
||||
*
|
||||
* @covers ::wp_trim_excerpt
|
||||
*/
|
||||
public function test_the_excerpt_does_not_affect_omit_lazy_loading_logic() {
|
||||
global $wp_query, $wp_the_query;
|
||||
|
||||
/*
|
||||
* Use the filter to alter the threshold for not lazy-loading to the first 2 elements,
|
||||
* then use a post that contains exactly 2 images.
|
||||
*/
|
||||
$this->force_omit_loading_attr_threshold( 2 );
|
||||
$post_content = '<img src="example.jpg" width="800" height="600">';
|
||||
$post_content .= '<p>Some text.</p>';
|
||||
$post_content .= '<img src="example2.jpg" width="800" height="600">';
|
||||
|
||||
$post_id = self::factory()->post->create(
|
||||
array(
|
||||
'post_content' => $post_content,
|
||||
'post_excerpt' => '',
|
||||
)
|
||||
);
|
||||
$featured_image_id = self::$large_id;
|
||||
update_post_meta( $post_id, '_thumbnail_id', $featured_image_id );
|
||||
|
||||
$expected_image_tag = get_the_post_thumbnail( $post_id, 'post-thumbnail', array( 'loading' => false ) );
|
||||
|
||||
$wp_query = new WP_Query( array( 'post__in' => array( $post_id ) ) );
|
||||
$wp_the_query = $wp_query;
|
||||
|
||||
$output = '';
|
||||
while ( have_posts() ) {
|
||||
the_post();
|
||||
|
||||
// Print excerpt first, then the featured image.
|
||||
$output .= get_echo( 'the_excerpt' );
|
||||
$output .= get_echo( 'the_post_thumbnail' );
|
||||
}
|
||||
|
||||
$this->assertStringContainsString( $expected_image_tag, $output );
|
||||
}
|
||||
|
||||
private function reset_content_media_count() {
|
||||
// Get current value without increasing.
|
||||
$content_media_count = wp_increase_content_media_count( 0 );
|
||||
|
||||
Reference in New Issue
Block a user