<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[55850] trunk: Media: Fix lazy-loading bug by avoiding to modify content images when creating an excerpt.</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/55850">55850</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/55850","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>flixos90</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2023-05-23 18:23:59 +0000 (Tue, 23 May 2023)</dd>
</dl>
<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>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 <a href="https://core.trac.wordpress.org/ticket/56588">#56588</a>.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesformattingphp">trunk/src/wp-includes/formatting.php</a></li>
<li><a href="#trunktestsphpunittestsformattingwpTrimExcerptphp">trunk/tests/phpunit/tests/formatting/wpTrimExcerpt.php</a></li>
<li><a href="#trunktestsphpunittestsmediaphp">trunk/tests/phpunit/tests/media.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesformattingphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/formatting.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/formatting.php 2023-05-23 15:23:15 UTC (rev 55849)
+++ trunk/src/wp-includes/formatting.php 2023-05-23 18:23:59 UTC (rev 55850)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3937,10 +3937,26 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $text = strip_shortcodes( $text );
</span><span class="cx" style="display: block; padding: 0 10px"> $text = excerpt_remove_blocks( $text );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /*
+ * 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' );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /** This filter is documented in wp-includes/post-template.php */
</span><span class="cx" style="display: block; padding: 0 10px"> $text = apply_filters( 'the_content', $text );
</span><span class="cx" style="display: block; padding: 0 10px"> $text = str_replace( ']]>', ']]>', $text );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+ * 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' );
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /* translators: Maximum number of words used in a post excerpt. */
</span><span class="cx" style="display: block; padding: 0 10px"> $excerpt_length = (int) _x( '55', 'excerpt_length' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span></span></pre></div>
<a id="trunktestsphpunittestsformattingwpTrimExcerptphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/formatting/wpTrimExcerpt.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/formatting/wpTrimExcerpt.php 2023-05-23 15:23:15 UTC (rev 55849)
+++ trunk/tests/phpunit/tests/formatting/wpTrimExcerpt.php 2023-05-23 18:23:59 UTC (rev 55850)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -92,4 +92,60 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $this->assertSame( 'Post content', wp_trim_excerpt( null, $post ) );
</span><span class="cx" style="display: block; padding: 0 10px"> $this->assertSame( 'Post content', wp_trim_excerpt( false, $post ) );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ /**
+ * 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' ) );
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunktestsphpunittestsmediaphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/media.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/media.php 2023-05-23 15:23:15 UTC (rev 55849)
+++ trunk/tests/phpunit/tests/media.php 2023-05-23 18:23:59 UTC (rev 55850)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -75,6 +75,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> parent::tear_down_after_class();
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+ * 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();
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> public function test_img_caption_shortcode_added() {
</span><span class="cx" style="display: block; padding: 0 10px"> global $shortcode_tags;
</span><span class="cx" style="display: block; padding: 0 10px"> $this->assertSame( 'img_caption_shortcode', $shortcode_tags['caption'] );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3567,8 +3577,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $this->assertSame( 'lazy', wp_get_loading_attr_default( $context ) );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $query = $this->get_new_wp_query_for_published_post();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> while ( have_posts() ) {
</span><span class="cx" style="display: block; padding: 0 10px"> the_post();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3613,8 +3621,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> public function test_wp_omit_loading_attr_threshold_filter() {
</span><span class="cx" style="display: block; padding: 0 10px"> $query = $this->get_new_wp_query_for_published_post();
</span><span class="cx" style="display: block; padding: 0 10px"> $this->set_main_query( $query );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Use the filter to alter the threshold for not lazy-loading to the first five elements.
</span><span class="cx" style="display: block; padding: 0 10px"> $this->force_omit_loading_attr_threshold( 5 );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3655,8 +3661,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $query = $this->get_new_wp_query_for_published_post();
</span><span class="cx" style="display: block; padding: 0 10px"> $this->set_main_query( $query );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> while ( have_posts() ) {
</span><span class="cx" style="display: block; padding: 0 10px"> the_post();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3707,8 +3711,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> global $wp_query;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_query = $this->get_new_wp_query_for_published_post();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> do_action( 'get_header' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3732,8 +3734,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_query = $this->get_new_wp_query_for_published_post();
</span><span class="cx" style="display: block; padding: 0 10px"> $this->set_main_query( $wp_query );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Lazy if header not called.
</span><span class="cx" style="display: block; padding: 0 10px"> $this->assertSame( 'lazy', wp_get_loading_attr_default( $context ) );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3755,8 +3755,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_query = $this->get_new_wp_query_for_published_post();
</span><span class="cx" style="display: block; padding: 0 10px"> $this->set_main_query( $wp_query );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> do_action( 'get_header' );
</span><span class="cx" style="display: block; padding: 0 10px"> $this->assertFalse( wp_get_loading_attr_default( $context ) );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3778,8 +3776,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_query = $this->get_new_wp_query_for_published_post();
</span><span class="cx" style="display: block; padding: 0 10px"> $this->set_main_query( $wp_query );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> do_action( 'get_header' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3805,8 +3801,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_query = $this->get_new_wp_query_for_published_post();
</span><span class="cx" style="display: block; padding: 0 10px"> $this->set_main_query( $wp_query );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Ensure header and footer is called.
</span><span class="cx" style="display: block; padding: 0 10px"> do_action( 'get_header' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3865,8 +3859,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_query = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_the_query = $wp_query;
</span><span class="cx" style="display: block; padding: 0 10px"> $post = get_post( self::$post_ids['publish'] );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $_wp_current_template_content = '<!-- wp:post-content /-->';
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3922,8 +3914,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_query = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_the_query = $wp_query;
</span><span class="cx" style="display: block; padding: 0 10px"> $post = get_post( self::$post_ids['publish'] );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $_wp_current_template_content = '<!-- wp:post-featured-image /--> <!-- wp:post-content /-->';
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4013,8 +4003,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_query = new WP_Query( array( 'post__in' => array( self::$post_ids['publish'] ) ) );
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_the_query = $wp_query;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->reset_content_media_count();
- $this->reset_omit_loading_attr_filter();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> $content = '';
</span><span class="cx" style="display: block; padding: 0 10px"> while ( have_posts() ) {
</span><span class="cx" style="display: block; padding: 0 10px"> the_post();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4078,6 +4067,98 @@
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+ * 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 );
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> private function reset_content_media_count() {
</span><span class="cx" style="display: block; padding: 0 10px"> // Get current value without increasing.
</span><span class="cx" style="display: block; padding: 0 10px"> $content_media_count = wp_increase_content_media_count( 0 );
</span></span></pre>
</div>
</div>
</body>
</html>