Skip to content

Commit 02f9f1e

Browse files
committed
Media: Simplify logic in wp_get_loading_optimization_attributes().
While the `wp_get_loading_optimization_attributes()` function was only recently introduced in 6.3, its code was mostly ported over from the now deprecated `wp_get_loading_attr_default()` function introduced in 5.5. That function started out in a simple way, but over time was expanded with more and more conditionals on when to avoid lazy-loading, which ended up making the logic extremely complex and hard to follow. This changeset refactors the logic to simplify it, in a way that allows to follow it more sequentially, and without making any functional changes, ensuring that the extensive existing unit test coverage still passes. This will facilitate future enhancements to the function to be less error-prone and make it more accessible to new contributors. Props flixos90, joemcgill. Fixes #58891. git-svn-id: https://develop.svn.wordpress.org/trunk@56347 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 3a3bd8e commit 02f9f1e

2 files changed

Lines changed: 134 additions & 102 deletions

File tree

src/wp-includes/media.php

Lines changed: 133 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -5604,33 +5604,6 @@ function wp_get_webp_info( $filename ) {
56045604
function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
56055605
global $wp_query;
56065606

5607-
/*
5608-
* Closure for postprocessing logic.
5609-
* It is here to avoid duplicate logic in many places below, without having
5610-
* to introduce a very specific private global function.
5611-
*/
5612-
$postprocess = static function( $loading_attributes, $with_fetchpriority = false ) use ( $tag_name, $attr, $context ) {
5613-
// Potentially add `fetchpriority="high"`.
5614-
if ( $with_fetchpriority ) {
5615-
$loading_attributes = wp_maybe_add_fetchpriority_high_attr( $loading_attributes, $tag_name, $attr );
5616-
}
5617-
// Potentially strip `loading="lazy"` if the feature is disabled.
5618-
if ( isset( $loading_attributes['loading'] ) && ! wp_lazy_loading_enabled( $tag_name, $context ) ) {
5619-
unset( $loading_attributes['loading'] );
5620-
}
5621-
return $loading_attributes;
5622-
};
5623-
5624-
// Closure to increase media count for images with certain minimum threshold, mostly used for header images.
5625-
$maybe_increase_content_media_count = static function() use ( $attr ) {
5626-
/** This filter is documented in wp-admin/includes/media.php */
5627-
$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );
5628-
// Images with a certain minimum size in the header of the page are also counted towards the threshold.
5629-
if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
5630-
wp_increase_content_media_count();
5631-
}
5632-
};
5633-
56345607
$loading_attrs = array();
56355608

56365609
/*
@@ -5651,104 +5624,163 @@ function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
56515624
return $loading_attrs;
56525625
}
56535626

5654-
if ( isset( $attr['loading'] ) ) {
5655-
/*
5656-
* While any `loading` value could be set in `$loading_attrs`, for
5657-
* consistency we only do it for `loading="lazy"` since that is the
5658-
* only possible value that WordPress core would apply on its own.
5659-
*/
5660-
if ( 'lazy' === $attr['loading'] ) {
5661-
$loading_attrs['loading'] = 'lazy';
5662-
if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
5663-
_doing_it_wrong(
5664-
__FUNCTION__,
5665-
__( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
5666-
'6.3.0'
5667-
);
5627+
/*
5628+
* Skip programmatically created images within post content as they need to be handled together with the other
5629+
* images within the post content.
5630+
* Without this clause, they would already be considered within their own context which skews the image count and
5631+
* can result in the first post content image being lazy-loaded or an image further down the page being marked as a
5632+
* high priority.
5633+
*/
5634+
switch ( $context ) {
5635+
case 'the_post_thumbnail':
5636+
case 'wp_get_attachment_image':
5637+
case 'widget_media_image':
5638+
if ( doing_filter( 'the_content' ) ) {
5639+
return $loading_attrs;
56685640
}
5669-
}
5670-
5671-
return $postprocess( $loading_attrs, true );
5672-
}
5673-
5674-
// An image with `fetchpriority="high"` cannot be assigned `loading="lazy"` at the same time.
5675-
if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
5676-
return $postprocess( $loading_attrs, true );
56775641
}
56785642

56795643
/*
5680-
* Do not lazy-load images in the header block template part, as they are likely above the fold.
5681-
* For classic themes, this is handled in the condition below using the 'get_header' action.
5644+
* The key function logic starts here.
56825645
*/
5683-
$header_area = WP_TEMPLATE_PART_AREA_HEADER;
5684-
if ( "template_part_{$header_area}" === $context ) {
5685-
// Increase media count if there are images in header above a certian minimum size threshold.
5686-
$maybe_increase_content_media_count();
5687-
return $postprocess( $loading_attrs, true );
5688-
}
5689-
5690-
// The custom header image is always expected to be in the header.
5691-
if ( 'get_header_image_tag' === $context ) {
5692-
// Increase media count if there are images in header above a certian minimum size threshold.
5693-
$maybe_increase_content_media_count();
5694-
return $postprocess( $loading_attrs, true );
5695-
}
5646+
$maybe_in_viewport = null;
5647+
$increase_count = false;
5648+
$maybe_increase_count = false;
56965649

5697-
// Special handling for programmatically created image tags.
5698-
if ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context || 'widget_media_image' === $context ) {
5650+
// Logic to handle a `loading` attribute that is already provided.
5651+
if ( isset( $attr['loading'] ) ) {
56995652
/*
5700-
* Skip programmatically created images within post content as they need to be handled together with the other
5701-
* images within the post content.
5702-
* Without this clause, they would already be considered below which skews the image count and can result in
5703-
* the first post content image being lazy-loaded or an image further down the page being marked as a high
5704-
* priority.
5653+
* Interpret "lazy" as not in viewport. Any other value can be
5654+
* interpreted as in viewport (realistically only "eager" or `false`
5655+
* to force-omit the attribute are other potential values).
57055656
*/
5706-
if ( doing_filter( 'the_content' ) ) {
5707-
return $loading_attrs;
5657+
if ( 'lazy' === $attr['loading'] ) {
5658+
$maybe_in_viewport = false;
5659+
} else {
5660+
$maybe_in_viewport = true;
57085661
}
5662+
}
57095663

5710-
// Conditionally skip lazy-loading on images before the loop.
5711-
if (
5712-
// Only apply for main query but before the loop.
5713-
$wp_query->before_loop && $wp_query->is_main_query()
5664+
// Logic to handle a `fetchpriority` attribute that is already provided.
5665+
if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
5666+
/*
5667+
* If the image was already determined to not be in the viewport (e.g.
5668+
* from an already provided `loading` attribute), trigger a warning.
5669+
* Otherwise, the value can be interpreted as in viewport, since only
5670+
* the most important in-viewport image should have `fetchpriority` set
5671+
* to "high".
5672+
*/
5673+
if ( false === $maybe_in_viewport ) {
5674+
_doing_it_wrong(
5675+
__FUNCTION__,
5676+
__( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
5677+
'6.3.0'
5678+
);
57145679
/*
5715-
* Any image before the loop, but after the header has started should not be lazy-loaded,
5716-
* except when the footer has already started which can happen when the current template
5717-
* does not include any loop.
5680+
* Set `fetchpriority` here for backward-compatibility as we should
5681+
* not override what a developer decided, even though it seems
5682+
* incorrect.
57185683
*/
5719-
&& did_action( 'get_header' ) && ! did_action( 'get_footer' )
5720-
) {
5721-
// Increase media count if there are images in header above a certian minimum size threshold.
5722-
$maybe_increase_content_media_count();
5723-
return $postprocess( $loading_attrs, true );
5684+
$loading_attrs['fetchpriority'] = 'high';
5685+
} else {
5686+
$maybe_in_viewport = true;
5687+
}
5688+
}
5689+
5690+
if ( null === $maybe_in_viewport ) {
5691+
switch ( $context ) {
5692+
// Consider elements with these header-specific contexts to be in viewport.
5693+
case 'template_part_' . WP_TEMPLATE_PART_AREA_HEADER:
5694+
case 'get_header_image_tag':
5695+
$maybe_in_viewport = true;
5696+
$maybe_increase_count = true;
5697+
break;
5698+
// Count main content elements and detect whether in viewport.
5699+
case 'the_content':
5700+
case 'the_post_thumbnail':
5701+
case 'do_shortcode':
5702+
// Only elements within the main query loop have special handling.
5703+
if ( ! is_admin() && in_the_loop() && is_main_query() ) {
5704+
/*
5705+
* Get the content media count, since this is a main query
5706+
* content element. This is accomplished by "increasing"
5707+
* the count by zero, as the only way to get the count is
5708+
* to call this function.
5709+
* The actual count increase happens further below, based
5710+
* on the `$increase_count` flag set here.
5711+
*/
5712+
$content_media_count = wp_increase_content_media_count( 0 );
5713+
$increase_count = true;
5714+
5715+
// If the count so far is below the threshold, `loading` attribute is omitted.
5716+
if ( $content_media_count < wp_omit_loading_attr_threshold() ) {
5717+
$maybe_in_viewport = true;
5718+
} else {
5719+
$maybe_in_viewport = false;
5720+
}
5721+
}
5722+
/*
5723+
* For the 'the_post_thumbnail' context, the following case
5724+
* clause needs to be considered as well, therefore skip the
5725+
* break statement here if the viewport has not been
5726+
* determined.
5727+
*/
5728+
if ( 'the_post_thumbnail' !== $context || null !== $maybe_in_viewport ) {
5729+
break;
5730+
}
5731+
// phpcs:ignore Generic.WhiteSpace.ScopeIndent.Incorrect
5732+
// Consider elements before the loop as being in viewport.
5733+
case 'wp_get_attachment_image':
5734+
case 'widget_media_image':
5735+
if (
5736+
// Only apply for main query but before the loop.
5737+
$wp_query->before_loop && $wp_query->is_main_query()
5738+
/*
5739+
* Any image before the loop, but after the header has started should not be lazy-loaded,
5740+
* except when the footer has already started which can happen when the current template
5741+
* does not include any loop.
5742+
*/
5743+
&& did_action( 'get_header' ) && ! did_action( 'get_footer' )
5744+
) {
5745+
$maybe_in_viewport = true;
5746+
$maybe_increase_count = true;
5747+
}
5748+
break;
57245749
}
57255750
}
57265751

57275752
/*
5728-
* The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded,
5729-
* as they are likely above the fold. Shortcodes are processed after content images, so if
5730-
* thresholds haven't already been met, apply the same logic to those as well.
5753+
* If the element is in the viewport (`true`), potentially add
5754+
* `fetchpriority` with a value of "high". Otherwise, i.e. if the element
5755+
* is not not in the viewport (`false`) or it is unknown (`null`), add
5756+
* `loading` with a value of "lazy".
57315757
*/
5732-
if ( 'the_content' === $context || 'the_post_thumbnail' === $context || 'do_shortcode' === $context ) {
5733-
// Only elements within the main query loop have special handling.
5734-
if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
5758+
if ( $maybe_in_viewport ) {
5759+
$loading_attrs = wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr );
5760+
} else {
5761+
// Only add `loading="lazy"` if the feature is enabled.
5762+
if ( wp_lazy_loading_enabled( $tag_name, $context ) ) {
57355763
$loading_attrs['loading'] = 'lazy';
5736-
return $postprocess( $loading_attrs, false );
57375764
}
5765+
}
57385766

5739-
// Increase the counter since this is a main query content element.
5740-
$content_media_count = wp_increase_content_media_count();
5767+
/*
5768+
* If flag was set based on contextual logic above, increase the content
5769+
* media count, either unconditionally, or based on whether the image size
5770+
* is larger than the threshold.
5771+
*/
5772+
if ( $increase_count ) {
5773+
wp_increase_content_media_count();
5774+
} elseif ( $maybe_increase_count ) {
5775+
/** This filter is documented in wp-admin/includes/media.php */
5776+
$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );
57415777

5742-
// If the count so far is below the threshold, `loading` attribute is omitted.
5743-
if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
5744-
// The first largest image will still get `fetchpriority='high'`.
5745-
return $postprocess( $loading_attrs, true );
5778+
if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
5779+
wp_increase_content_media_count();
57465780
}
57475781
}
57485782

5749-
// Lazy-load by default for any unknown context.
5750-
$loading_attrs['loading'] = 'lazy';
5751-
return $postprocess( $loading_attrs, false );
5783+
return $loading_attrs;
57525784
}
57535785

57545786
/**

tests/phpunit/tests/media.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4956,7 +4956,7 @@ public function test_wp_get_loading_optimization_attributes_incorrect_loading_at
49564956
$attr['loading'] = 'lazy';
49574957
$attr['fetchpriority'] = 'high';
49584958

4959-
$this->assertSame(
4959+
$this->assertEqualSetsWithIndex(
49604960
array(
49614961
'loading' => 'lazy',
49624962
'fetchpriority' => 'high',

0 commit comments

Comments
 (0)