Make WordPress Core


Ignore:
Timestamp:
05/23/2023 06:23:59 PM (11 months ago)
Author:
flixos90
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/media.php

    r55847 r55850  
    7474        wp_delete_attachment( self::$large_id, true );
    7575        parent::tear_down_after_class();
     76    }
     77
     78    /**
     79     * Ensures that the static content media count and related filter are reset between tests.
     80     */
     81    public function set_up() {
     82        parent::set_up();
     83
     84        $this->reset_content_media_count();
     85        $this->reset_omit_loading_attr_filter();
    7686    }
    7787
     
    35683578
    35693579        $query = $this->get_new_wp_query_for_published_post();
    3570         $this->reset_content_media_count();
    3571         $this->reset_omit_loading_attr_filter();
    35723580
    35733581        while ( have_posts() ) {
     
    36143622        $query = $this->get_new_wp_query_for_published_post();
    36153623        $this->set_main_query( $query );
    3616         $this->reset_content_media_count();
    3617         $this->reset_omit_loading_attr_filter();
    36183624
    36193625        // Use the filter to alter the threshold for not lazy-loading to the first five elements.
     
    36563662        $query = $this->get_new_wp_query_for_published_post();
    36573663        $this->set_main_query( $query );
    3658         $this->reset_content_media_count();
    3659         $this->reset_omit_loading_attr_filter();
    36603664
    36613665        while ( have_posts() ) {
     
    37083712
    37093713        $wp_query = $this->get_new_wp_query_for_published_post();
    3710         $this->reset_content_media_count();
    3711         $this->reset_omit_loading_attr_filter();
    37123714
    37133715        do_action( 'get_header' );
     
    37333735        $wp_query = $this->get_new_wp_query_for_published_post();
    37343736        $this->set_main_query( $wp_query );
    3735         $this->reset_content_media_count();
    3736         $this->reset_omit_loading_attr_filter();
    37373737
    37383738        // Lazy if header not called.
     
    37563756        $wp_query = $this->get_new_wp_query_for_published_post();
    37573757        $this->set_main_query( $wp_query );
    3758         $this->reset_content_media_count();
    3759         $this->reset_omit_loading_attr_filter();
    37603758
    37613759        do_action( 'get_header' );
     
    37793777        $wp_query = $this->get_new_wp_query_for_published_post();
    37803778        $this->set_main_query( $wp_query );
    3781         $this->reset_content_media_count();
    3782         $this->reset_omit_loading_attr_filter();
    37833779
    37843780        do_action( 'get_header' );
     
    38063802        $wp_query = $this->get_new_wp_query_for_published_post();
    38073803        $this->set_main_query( $wp_query );
    3808         $this->reset_content_media_count();
    3809         $this->reset_omit_loading_attr_filter();
    38103804
    38113805        // Ensure header and footer is called.
     
    38663860        $wp_the_query = $wp_query;
    38673861        $post         = get_post( self::$post_ids['publish'] );
    3868         $this->reset_content_media_count();
    3869         $this->reset_omit_loading_attr_filter();
    38703862
    38713863        $_wp_current_template_content = '<!-- wp:post-content /-->';
     
    39233915        $wp_the_query = $wp_query;
    39243916        $post         = get_post( self::$post_ids['publish'] );
    3925         $this->reset_content_media_count();
    3926         $this->reset_omit_loading_attr_filter();
    39273917
    39283918        $_wp_current_template_content = '<!-- wp:post-featured-image /--> <!-- wp:post-content /-->';
     
    40144004        $wp_query     = new WP_Query( array( 'post__in' => array( self::$post_ids['publish'] ) ) );
    40154005        $wp_the_query = $wp_query;
    4016         $this->reset_content_media_count();
    4017         $this->reset_omit_loading_attr_filter();
     4006
    40184007        $content = '';
    40194008        while ( have_posts() ) {
     
    40774066            'wp_get_attachment_image' => array( 'context' => 'wp_get_attachment_image' ),
    40784067        );
     4068    }
     4069
     4070    /**
     4071     * Tests that the content media count is not affected by `the_excerpt()` calls for posts that contain images.
     4072     *
     4073     * @ticket 56588
     4074     *
     4075     * @covers ::wp_trim_excerpt
     4076     */
     4077    public function test_the_excerpt_does_not_affect_content_media_count() {
     4078        global $wp_query, $wp_the_query;
     4079
     4080        /*
     4081         * Use the filter to alter the threshold for not lazy-loading to the first 2 elements,
     4082         * then use a post that contains exactly 2 images.
     4083         */
     4084        $this->force_omit_loading_attr_threshold( 2 );
     4085        $post_content  = '<img src="example.jpg" width="800" height="600">';
     4086        $post_content .= '<p>Some text.</p>';
     4087        $post_content .= '<img src="example2.jpg" width="800" height="600">';
     4088
     4089        $post_id = self::factory()->post->create(
     4090            array(
     4091                'post_content' => $post_content,
     4092                'post_excerpt' => '',
     4093            )
     4094        );
     4095
     4096        $wp_query     = new WP_Query( array( 'post__in' => array( $post_id ) ) );
     4097        $wp_the_query = $wp_query;
     4098
     4099        while ( have_posts() ) {
     4100            the_post();
     4101
     4102            // Call `the_excerpt()` without generating output.
     4103            get_echo( 'the_excerpt' );
     4104        }
     4105
     4106        // The only way to access the value is by calling this function without increasing the value.
     4107        $content_media_count = wp_increase_content_media_count( 0 );
     4108
     4109        // Assert that the media count was not increased even though there are 3 images in the post's content.
     4110        $this->assertSame( 0, $content_media_count );
     4111    }
     4112
     4113    /**
     4114     * Tests that the lazy-loading result is not affected by `the_excerpt()` calls for posts that
     4115     * contain images.
     4116     *
     4117     * Printing the excerpt for a post that contains images in its content prior to its featured image should result in
     4118     * that featured image not being lazy-loaded, since the images in the post content aren't displayed in the excerpt.
     4119     *
     4120     * @ticket 56588
     4121     *
     4122     * @covers ::wp_trim_excerpt
     4123     */
     4124    public function test_the_excerpt_does_not_affect_omit_lazy_loading_logic() {
     4125        global $wp_query, $wp_the_query;
     4126
     4127        /*
     4128         * Use the filter to alter the threshold for not lazy-loading to the first 2 elements,
     4129         * then use a post that contains exactly 2 images.
     4130         */
     4131        $this->force_omit_loading_attr_threshold( 2 );
     4132        $post_content  = '<img src="example.jpg" width="800" height="600">';
     4133        $post_content .= '<p>Some text.</p>';
     4134        $post_content .= '<img src="example2.jpg" width="800" height="600">';
     4135
     4136        $post_id           = self::factory()->post->create(
     4137            array(
     4138                'post_content' => $post_content,
     4139                'post_excerpt' => '',
     4140            )
     4141        );
     4142        $featured_image_id = self::$large_id;
     4143        update_post_meta( $post_id, '_thumbnail_id', $featured_image_id );
     4144
     4145        $expected_image_tag = get_the_post_thumbnail( $post_id, 'post-thumbnail', array( 'loading' => false ) );
     4146
     4147        $wp_query     = new WP_Query( array( 'post__in' => array( $post_id ) ) );
     4148        $wp_the_query = $wp_query;
     4149
     4150        $output = '';
     4151        while ( have_posts() ) {
     4152            the_post();
     4153
     4154            // Print excerpt first, then the featured image.
     4155            $output .= get_echo( 'the_excerpt' );
     4156            $output .= get_echo( 'the_post_thumbnail' );
     4157        }
     4158
     4159        $this->assertStringContainsString( $expected_image_tag, $output );
    40794160    }
    40804161
Note: See TracChangeset for help on using the changeset viewer.