Skip to content

Commit

Permalink
Merge pull request #1699 from WordPress/update/separate-file-structure
Browse files Browse the repository at this point in the history
Auto sizes: Reorganize file structure by feature
  • Loading branch information
mukeshpanchal27 authored Nov 26, 2024
2 parents 7f3e44d + 11a63e2 commit 9ffe60c
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 271 deletions.
2 changes: 2 additions & 0 deletions plugins/auto-sizes/auto-sizes.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@

define( 'IMAGE_AUTO_SIZES_VERSION', '1.3.0' );

require_once __DIR__ . '/includes/auto-sizes.php';
require_once __DIR__ . '/includes/improve-calculate-sizes.php';
require_once __DIR__ . '/hooks.php';
276 changes: 7 additions & 269 deletions plugins/auto-sizes/hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,102 +10,6 @@
exit; // Exit if accessed directly.
}

/**
* Adds auto to the sizes attribute to the image, if applicable.
*
* @since 1.0.0
*
* @param array<string, string>|mixed $attr Attributes for the image markup.
* @return array<string, string> The filtered attributes for the image markup.
*/
function auto_sizes_update_image_attributes( $attr ): array {
if ( ! is_array( $attr ) ) {
$attr = array();
}

// Bail early if the image is not lazy-loaded.
if ( ! isset( $attr['loading'] ) || 'lazy' !== $attr['loading'] ) {
return $attr;
}

// Bail early if the image is not responsive.
if ( ! isset( $attr['sizes'] ) ) {
return $attr;
}

// Don't add 'auto' to the sizes attribute if it already exists.
if ( auto_sizes_attribute_includes_valid_auto( $attr['sizes'] ) ) {
return $attr;
}

$attr['sizes'] = 'auto, ' . $attr['sizes'];

return $attr;
}

/**
* Adds auto to the sizes attribute to the image, if applicable.
*
* @since 1.0.0
*
* @param string|mixed $html The HTML image tag markup being filtered.
* @return string The filtered HTML image tag markup.
*/
function auto_sizes_update_content_img_tag( $html ): string {
if ( ! is_string( $html ) ) {
$html = '';
}

$processor = new WP_HTML_Tag_Processor( $html );

// Bail if there is no IMG tag.
if ( ! $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) {
return $html;
}

// Bail early if the image is not lazy-loaded.
$value = $processor->get_attribute( 'loading' );
if ( ! is_string( $value ) || 'lazy' !== strtolower( trim( $value, " \t\f\r\n" ) ) ) {
return $html;
}

$sizes = $processor->get_attribute( 'sizes' );

// Bail early if the image is not responsive.
if ( ! is_string( $sizes ) ) {
return $html;
}

// Don't add 'auto' to the sizes attribute if it already exists.
if ( auto_sizes_attribute_includes_valid_auto( $sizes ) ) {
return $html;
}

$processor->set_attribute( 'sizes', "auto, $sizes" );
return $processor->get_updated_html();
}

// Skip loading plugin filters if WordPress Core already loaded the functionality.
if ( ! function_exists( 'wp_sizes_attribute_includes_valid_auto' ) ) {
add_filter( 'wp_get_attachment_image_attributes', 'auto_sizes_update_image_attributes' );
add_filter( 'wp_content_img_tag', 'auto_sizes_update_content_img_tag' );
}

/**
* Checks whether the given 'sizes' attribute includes the 'auto' keyword as the first item in the list.
*
* Per the HTML spec, if present it must be the first entry.
*
* @since 1.2.0
*
* @param string $sizes_attr The 'sizes' attribute value.
* @return bool True if the 'auto' keyword is present, false otherwise.
*/
function auto_sizes_attribute_includes_valid_auto( string $sizes_attr ): bool {
list( $first_size ) = explode( ',', $sizes_attr, 2 );
return 'auto' === strtolower( trim( $first_size, " \t\f\r\n" ) );
}

/**
* Displays the HTML generator tag for the plugin.
*
Expand All @@ -120,183 +24,17 @@ function auto_sizes_render_generator(): void {
add_action( 'wp_head', 'auto_sizes_render_generator' );

/**
* Gets the smaller image size if the layout width is bigger.
*
* It will return the smaller image size and return "px" if the layout width
* is something else, e.g. min(640px, 90vw) or 90vw.
*
* @since 1.1.0
*
* @param string $layout_width The layout width.
* @param int $image_width The image width.
* @return string The proper width after some calculations.
*/
function auto_sizes_get_width( string $layout_width, int $image_width ): string {
if ( str_ends_with( $layout_width, 'px' ) ) {
return $image_width > (int) $layout_width ? $layout_width : $image_width . 'px';
}
return $image_width . 'px';
}

/**
* Primes attachment into the cache with a single database query.
*
* @since n.e.x.t
*
* @param string|mixed $content The HTML content.
* @return string The HTML content.
* Filters related to the auto-sizes functionality.
*/
function auto_sizes_prime_attachment_caches( $content ): string {
if ( ! is_string( $content ) ) {
return '';
}

$processor = new WP_HTML_Tag_Processor( $content );

$images = array();
while ( $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) {
$class = $processor->get_attribute( 'class' );

if ( ! is_string( $class ) ) {
continue;
}

if ( preg_match( '/(?:^|\s)wp-image-([1-9][0-9]*)(?:\s|$)/', $class, $class_id ) === 1 ) {
$attachment_id = (int) $class_id[1];
if ( $attachment_id > 0 ) {
$images[] = $attachment_id;
}
}
}

// Reduce the array to unique attachment IDs.
$attachment_ids = array_unique( $images );

if ( count( $attachment_ids ) > 1 ) {
/*
* Warm the object cache with post and meta information for all found
* images to avoid making individual database calls.
*/
_prime_post_caches( $attachment_ids, false, true );
}

return $content;
// Skip loading plugin filters if WordPress Core already loaded the functionality.
if ( ! function_exists( 'wp_img_tag_add_auto_sizes' ) ) {
add_filter( 'wp_get_attachment_image_attributes', 'auto_sizes_update_image_attributes' );
add_filter( 'wp_content_img_tag', 'auto_sizes_update_content_img_tag' );
}

// This must run before 'do_blocks', which runs at priority 9.
add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 );

/**
* Filter the sizes attribute for images to improve the default calculation.
*
* @since 1.1.0
*
* @param string|mixed $content The block content about to be rendered.
* @param array{ attrs?: array{ align?: string, width?: string } } $parsed_block The parsed block.
* @param WP_Block $block Block instance.
* @return string The updated block content.
* Filters related to the improved image sizes functionality.
*/
function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $block ): string {
if ( ! is_string( $content ) ) {
return '';
}
$processor = new WP_HTML_Tag_Processor( $content );
$has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) );

// Only update the markup if an image is found.
if ( $has_image ) {

/**
* Callback for calculating image sizes attribute value for an image block.
*
* This is a workaround to use block context data when calculating the img sizes attribute.
*
* @param string $sizes The image sizes attribute value.
* @param string $size The image size data.
*/
$filter = static function ( $sizes, $size ) use ( $block ) {
$id = $block->attributes['id'] ?? 0;
$alignment = $block->attributes['align'] ?? '';
$width = $block->attributes['width'] ?? '';

return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width );
};

// Hook this filter early, before default filters are run.
add_filter( 'wp_calculate_image_sizes', $filter, 9, 2 );

$sizes = wp_calculate_image_sizes(
// If we don't have a size slug, assume the full size was used.
$parsed_block['attrs']['sizeSlug'] ?? 'full',
null,
null,
$parsed_block['attrs']['id'] ?? 0
);

remove_filter( 'wp_calculate_image_sizes', $filter, 9 );

// Bail early if sizes are not calculated.
if ( false === $sizes ) {
return $content;
}

$processor->set_attribute( 'sizes', $sizes );

return $processor->get_updated_html();
}

return $content;
}
add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 ); // This must run before 'do_blocks', which runs at priority 9.
add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 );
add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 );

/**
* Modifies the sizes attribute of an image based on layout context.
*
* @param int $id The image id.
* @param string $size The image size data.
* @param string $align The image alignment.
* @param string $resize_width Resize image width.
* @return string The sizes attribute value.
*/
function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width ): string {
$sizes = '';
$image = wp_get_attachment_image_src( $id, $size );

if ( false === $image ) {
return $sizes;
}

// Retrieve width from the image tag itself.
$image_width = '' !== $resize_width ? (int) $resize_width : $image[1];

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

// Handle different alignment use cases.
switch ( $align ) {
case 'full':
$sizes = '100vw';
break;

case 'wide':
if ( array_key_exists( 'wideSize', $layout ) ) {
$sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout['wideSize'] );
}
break;

case 'left':
case 'right':
case 'center':
$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width );
break;

default:
if ( array_key_exists( 'contentSize', $layout ) ) {
$width = auto_sizes_get_width( $layout['contentSize'], $image_width );
$sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width );
}
break;
}

return $sizes;
}
97 changes: 97 additions & 0 deletions plugins/auto-sizes/includes/auto-sizes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* Functionality to implement auto-sizes for lazy loaded images.
*
* @package auto-sizes
* @since n.e.x.t
*/

/**
* Adds auto to the sizes attribute to the image, if applicable.
*
* @since 1.0.0
*
* @param array<string, string>|mixed $attr Attributes for the image markup.
* @return array<string, string> The filtered attributes for the image markup.
*/
function auto_sizes_update_image_attributes( $attr ): array {
if ( ! is_array( $attr ) ) {
$attr = array();
}

// Bail early if the image is not lazy-loaded.
if ( ! isset( $attr['loading'] ) || 'lazy' !== $attr['loading'] ) {
return $attr;
}

// Bail early if the image is not responsive.
if ( ! isset( $attr['sizes'] ) ) {
return $attr;
}

// Don't add 'auto' to the sizes attribute if it already exists.
if ( auto_sizes_attribute_includes_valid_auto( $attr['sizes'] ) ) {
return $attr;
}

$attr['sizes'] = 'auto, ' . $attr['sizes'];

return $attr;
}

/**
* Adds auto to the sizes attribute to the image, if applicable.
*
* @since 1.0.0
*
* @param string|mixed $html The HTML image tag markup being filtered.
* @return string The filtered HTML image tag markup.
*/
function auto_sizes_update_content_img_tag( $html ): string {
if ( ! is_string( $html ) ) {
$html = '';
}

$processor = new WP_HTML_Tag_Processor( $html );

// Bail if there is no IMG tag.
if ( ! $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) {
return $html;
}

// Bail early if the image is not lazy-loaded.
$value = $processor->get_attribute( 'loading' );
if ( ! is_string( $value ) || 'lazy' !== strtolower( trim( $value, " \t\f\r\n" ) ) ) {
return $html;
}

$sizes = $processor->get_attribute( 'sizes' );

// Bail early if the image is not responsive.
if ( ! is_string( $sizes ) ) {
return $html;
}

// Don't add 'auto' to the sizes attribute if it already exists.
if ( auto_sizes_attribute_includes_valid_auto( $sizes ) ) {
return $html;
}

$processor->set_attribute( 'sizes', "auto, $sizes" );
return $processor->get_updated_html();
}

/**
* Checks whether the given 'sizes' attribute includes the 'auto' keyword as the first item in the list.
*
* Per the HTML spec, if present it must be the first entry.
*
* @since 1.2.0
*
* @param string $sizes_attr The 'sizes' attribute value.
* @return bool True if the 'auto' keyword is present, false otherwise.
*/
function auto_sizes_attribute_includes_valid_auto( string $sizes_attr ): bool {
list( $first_size ) = explode( ',', $sizes_attr, 2 );
return 'auto' === strtolower( trim( $first_size, " \t\f\r\n" ) );
}
Loading

0 comments on commit 9ffe60c

Please sign in to comment.