Skip to content

Commit 6e9f892

Browse files
authored
Merge pull request #1625 from WordPress/update/use-filter-approach
Accurate sizes: Calculate image sizes during block rendering
2 parents 9e276f1 + c06b4e7 commit 6e9f892

File tree

2 files changed

+108
-59
lines changed

2 files changed

+108
-59
lines changed

plugins/auto-sizes/hooks.php

Lines changed: 105 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -139,79 +139,139 @@ function auto_sizes_get_width( string $layout_width, int $image_width ): string
139139
}
140140

141141
/**
142-
* Filter the sizes attribute for images to improve the default calculation.
142+
* Primes attachment into the cache with a single database query.
143143
*
144-
* @since 1.1.0
144+
* @since n.e.x.t
145145
*
146-
* @param string $content The block content about to be rendered.
147-
* @param array{ attrs?: array{ align?: string, width?: string } } $parsed_block The parsed block.
148-
* @return string The updated block content.
146+
* @param string|mixed $content The HTML content.
147+
* @return string The HTML content.
149148
*/
150-
function auto_sizes_filter_image_tag( string $content, array $parsed_block ): string {
149+
function auto_sizes_prime_attachment_caches( $content ): string {
150+
if ( ! is_string( $content ) ) {
151+
return '';
152+
}
153+
151154
$processor = new WP_HTML_Tag_Processor( $content );
152-
$has_image = $processor->next_tag( array( 'tag_name' => 'img' ) );
153155

154-
// Only update the markup if an image is found.
155-
if ( $has_image ) {
156-
$processor->set_attribute( 'data-needs-sizes-update', true );
157-
if ( isset( $parsed_block['attrs']['align'] ) ) {
158-
$processor->set_attribute( 'data-align', $parsed_block['attrs']['align'] );
156+
$images = array();
157+
while ( $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) {
158+
$class = $processor->get_attribute( 'class' );
159+
160+
if ( ! is_string( $class ) ) {
161+
continue;
159162
}
160163

161-
// Resize image width.
162-
if ( isset( $parsed_block['attrs']['width'] ) ) {
163-
$processor->set_attribute( 'data-resize-width', $parsed_block['attrs']['width'] );
164+
if ( preg_match( '/(?:^|\s)wp-image-([1-9][0-9]*)(?:\s|$)/', $class, $class_id ) === 1 ) {
165+
$attachment_id = (int) $class_id[1];
166+
if ( $attachment_id > 0 ) {
167+
$images[] = $attachment_id;
168+
}
164169
}
170+
}
171+
172+
// Reduce the array to unique attachment IDs.
173+
$attachment_ids = array_unique( $images );
165174

166-
$content = $processor->get_updated_html();
175+
if ( count( $attachment_ids ) > 1 ) {
176+
/*
177+
* Warm the object cache with post and meta information for all found
178+
* images to avoid making individual database calls.
179+
*/
180+
_prime_post_caches( $attachment_ids, false, true );
167181
}
182+
168183
return $content;
169184
}
170-
add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 2 );
171-
add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 2 );
185+
186+
// This must run before 'do_blocks', which runs at priority 9.
187+
add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 );
172188

173189
/**
174190
* Filter the sizes attribute for images to improve the default calculation.
175191
*
176192
* @since 1.1.0
177193
*
178-
* @param string $content The block content about to be rendered.
194+
* @param string|mixed $content The block content about to be rendered.
195+
* @param array{ attrs?: array{ align?: string, width?: string } } $parsed_block The parsed block.
196+
* @param WP_Block $block Block instance.
179197
* @return string The updated block content.
180198
*/
181-
function auto_sizes_improve_image_sizes_attributes( string $content ): string {
182-
$processor = new WP_HTML_Tag_Processor( $content );
183-
if ( ! $processor->next_tag( array( 'tag_name' => 'img' ) ) ) {
184-
return $content;
199+
function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $block ): string {
200+
if ( ! is_string( $content ) ) {
201+
return '';
185202
}
203+
$processor = new WP_HTML_Tag_Processor( $content );
204+
$has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) );
186205

187-
$remove_data_attributes = static function () use ( $processor ): void {
188-
$processor->remove_attribute( 'data-needs-sizes-update' );
189-
$processor->remove_attribute( 'data-align' );
190-
$processor->remove_attribute( 'data-resize-width' );
191-
};
206+
// Only update the markup if an image is found.
207+
if ( $has_image ) {
208+
209+
/**
210+
* Callback for calculating image sizes attribute value for an image block.
211+
*
212+
* This is a workaround to use block context data when calculating the img sizes attribute.
213+
*
214+
* @param string $sizes The image sizes attribute value.
215+
* @param string $size The image size data.
216+
*/
217+
$filter = static function ( $sizes, $size ) use ( $block ) {
218+
$id = $block->attributes['id'] ?? 0;
219+
$alignment = $block->attributes['align'] ?? '';
220+
$width = $block->attributes['width'] ?? '';
221+
222+
return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width );
223+
};
224+
225+
// Hook this filter early, before default filters are run.
226+
add_filter( 'wp_calculate_image_sizes', $filter, 9, 2 );
227+
228+
$sizes = wp_calculate_image_sizes(
229+
// If we don't have a size slug, assume the full size was used.
230+
$parsed_block['attrs']['sizeSlug'] ?? 'full',
231+
null,
232+
null,
233+
$parsed_block['attrs']['id'] ?? 0
234+
);
235+
236+
remove_filter( 'wp_calculate_image_sizes', $filter, 9 );
237+
238+
// Bail early if sizes are not calculated.
239+
if ( false === $sizes ) {
240+
return $content;
241+
}
242+
243+
$processor->set_attribute( 'sizes', $sizes );
192244

193-
// Bail early if the responsive images are disabled.
194-
if ( null === $processor->get_attribute( 'sizes' ) ) {
195-
$remove_data_attributes();
196245
return $processor->get_updated_html();
197246
}
198247

199-
// Skips second time parsing if already processed.
200-
if ( null === $processor->get_attribute( 'data-needs-sizes-update' ) ) {
201-
return $content;
202-
}
248+
return $content;
249+
}
250+
add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 );
251+
add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 );
252+
253+
/**
254+
* Modifies the sizes attribute of an image based on layout context.
255+
*
256+
* @param int $id The image id.
257+
* @param string $size The image size data.
258+
* @param string $align The image alignment.
259+
* @param string $resize_width Resize image width.
260+
* @return string The sizes attribute value.
261+
*/
262+
function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width ): string {
263+
$sizes = '';
264+
$image = wp_get_attachment_image_src( $id, $size );
203265

204-
$align = $processor->get_attribute( 'data-align' );
266+
if ( false === $image ) {
267+
return $sizes;
268+
}
205269

206270
// Retrieve width from the image tag itself.
207-
$image_width = $processor->get_attribute( 'width' );
208-
if ( ! is_string( $image_width ) && ! in_array( $align, array( 'full', 'wide' ), true ) ) {
209-
return $content;
210-
}
271+
$image_width = '' !== $resize_width ? (int) $resize_width : $image[1];
211272

212273
$layout = wp_get_global_settings( array( 'layout' ) );
213274

214-
$sizes = null;
215275
// Handle different alignment use cases.
216276
switch ( $align ) {
217277
case 'full':
@@ -227,28 +287,16 @@ function auto_sizes_improve_image_sizes_attributes( string $content ): string {
227287
case 'left':
228288
case 'right':
229289
case 'center':
230-
// Resize image width.
231-
$image_width = $processor->get_attribute( 'data-resize-width' ) ?? $image_width;
232-
$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width );
290+
$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width );
233291
break;
234292

235293
default:
236294
if ( array_key_exists( 'contentSize', $layout ) ) {
237-
// Resize image width.
238-
$image_width = $processor->get_attribute( 'data-resize-width' ) ?? $image_width;
239-
$width = auto_sizes_get_width( $layout['contentSize'], (int) $image_width );
240-
$sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width );
295+
$width = auto_sizes_get_width( $layout['contentSize'], $image_width );
296+
$sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width );
241297
}
242298
break;
243299
}
244300

245-
if ( is_string( $sizes ) ) {
246-
$processor->set_attribute( 'sizes', $sizes );
247-
}
248-
249-
$remove_data_attributes();
250-
251-
return $processor->get_updated_html();
301+
return $sizes;
252302
}
253-
// Run filter prior to auto sizes "auto_sizes_update_content_img_tag" filter.
254-
add_filter( 'wp_content_img_tag', 'auto_sizes_improve_image_sizes_attributes', 9 );

plugins/auto-sizes/tests/test-improve-sizes.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ public function set_up(): void {
3131

3232
// Disable auto sizes.
3333
remove_filter( 'wp_content_img_tag', 'auto_sizes_update_content_img_tag' );
34+
35+
// Disable lazy loading attribute.
36+
add_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
3437
}
3538

3639
/**
3740
* Test that if disable responsive image then it will not add sizes attribute.
38-
*
39-
* @covers ::auto_sizes_improve_image_sizes_attributes
4041
*/
4142
public function test_that_if_disable_responsive_image_then_it_will_not_add_sizes_attribute(): void {
4243
// Disable responsive images.

0 commit comments

Comments
 (0)