HEX
Server: Apache
System: Linux 162-240-236-42.bluehost.com 3.10.0-1160.114.2.el7.x86_64 #1 SMP Wed Mar 20 15:54:52 UTC 2024 x86_64
User: bt667 (1004)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /home/bt667/www/wp-content/plugins/auto-sizes/includes/improve-calculate-sizes.php
<?php
/**
 * Functionality to improve the calculation of image `sizes` attributes.
 *
 * @package auto-sizes
 * @since 1.4.0
 */

/*
 * Map alignment values to a weighting value so they can be compared.
 * Note that 'left' and 'right' alignments are only constrained by max alignment.
 */
const AUTO_SIZES_CONSTRAINTS = array(
	'full'    => 0,
	'wide'    => 1,
	'left'    => 2,
	'right'   => 2,
	'default' => 3,
	'center'  => 3,
);

/**
 * Primes attachment into the cache with a single database query.
 *
 * @since 1.4.0
 *
 * @param string|mixed $content The HTML content.
 * @return string The HTML content.
 */
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;
}

/**
 * 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.
 */
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                       = isset( $block->attributes['id'] ) ? (int) $block->attributes['id'] : 0;
			$alignment                = $block->attributes['align'] ?? '';
			$width                    = isset( $block->attributes['width'] ) ? (int) $block->attributes['width'] : 0;
			$max_alignment            = $block->context['max_alignment'] ?? '';
			$container_relative_width = $block->context['container_relative_width'] ?? 1.0;

			/*
			 * For the post featured image block, use the post ID to get the featured image attachment ID.
			 * See https://github.com/WordPress/wordpress-develop/blob/3f9c6fce666ed2ea0d56c21f6235c37db3d91392/src/wp-includes/blocks/post-featured-image.php#L65
			 */
			if ( 'core/post-featured-image' === $block->name && isset( $block->context['postId'] ) ) {
				$id = auto_sizes_get_featured_image_attachment_id( $block->context['postId'] );
			}

			/*
			 * Update width for cover block.
			 * See https://github.com/WordPress/gutenberg/blob/938720602082dc50a1746bd2e33faa3d3a6096d4/packages/block-library/src/cover/style.scss#L82-L87.
			 */
			if ( 'core/cover' === $block->name && in_array( $alignment, array( 'left', 'right' ), true ) ) {
				$size = array( 420, 420 );
			}

			$better_sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width, $max_alignment, $container_relative_width );

			// If better sizes can't be calculated, use the default sizes.
			return false !== $better_sizes ? $better_sizes : $sizes;
		};

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

		// Get the image ID from the block attributes or context.
		$id = $parsed_block['attrs']['id'] ?? 0;

		/*
		 * For the post featured image block, use the post ID to get the featured image attachment ID.
		 * See https://github.com/WordPress/wordpress-develop/blob/3f9c6fce666ed2ea0d56c21f6235c37db3d91392/src/wp-includes/blocks/post-featured-image.php#L65
		 */
		if ( 'core/post-featured-image' === $block->name && isset( $block->context['postId'] ) ) {
			$id = auto_sizes_get_featured_image_attachment_id( $block->context['postId'] );
		}

		$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,
			$id
		);

		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;
}

/**
 * Modifies the sizes attribute of an image based on layout context.
 *
 * @since 1.4.0
 *
 * @param int                    $id                       The image attachment post ID.
 * @param string|array{int, int} $size                     Image size name or array of width and height.
 * @param string                 $align                    The image alignment.
 * @param int                    $resize_width             Resize image width.
 * @param string                 $max_alignment            The maximum usable layout alignment.
 * @param float                  $container_relative_width Container relative width.
 * @return string|false An improved sizes attribute or false if a better size cannot be calculated.
 */
function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $resize_width, string $max_alignment, float $container_relative_width ) {
	// Bail early if not a block theme.
	if ( ! wp_is_block_theme() ) {
		return false;
	}

	// Without an image ID or a resize width, we cannot calculate a better size.
	if ( 0 === $id && 0 === $resize_width ) {
		return false;
	}

	$image_data = wp_get_attachment_image_src( $id, $size );

	$image_width = false !== $image_data ? $image_data[1] : 0;

	// If we don't have an image width or a resize width, we cannot calculate a better size.
	if ( 0 === $image_width && 0 === $resize_width ) {
		return false;
	}

	/*
	 * If we don't have an image width, use the resize width.
	 * If we have both an image width and a resize width, use the smaller of the two.
	 */
	if ( 0 === $image_width ) {
		$image_width = $resize_width;
	} elseif ( 0 !== $resize_width ) {
		$image_width = min( $image_width, $resize_width );
	}

	// Normalize default alignment values.
	$align = '' !== $align ? $align : 'default';

	// Use the defined constant for constraints.
	$constraints = AUTO_SIZES_CONSTRAINTS;

	$alignment = $constraints[ $align ] > $constraints[ $max_alignment ] ? $align : $max_alignment;

	// Handle different alignment use cases.
	switch ( $alignment ) {
		case 'full':
			$layout_width = auto_sizes_get_layout_width( 'full' );
			break;

		case 'wide':
			$layout_width = auto_sizes_get_layout_width( 'wide' );
			// TODO: Add support for em, rem, vh, and vw.
			if (
				str_ends_with( $layout_width, 'px' ) &&
				( $container_relative_width > 0.0 ||
				$container_relative_width < 1.0 )
			) {
				// First remove 'px' from width.
				$layout_width = str_replace( 'px', '', $layout_width );
				// Convert to float for better precision.
				$layout_width = (float) $layout_width * $container_relative_width;
				$layout_width = sprintf( '%dpx', (int) $layout_width );
			}
			break;

		case 'left':
		case 'right':
		case 'center':
		default:
			$layout_alignment = in_array( $alignment, array( 'left', 'right' ), true ) ? 'wide' : 'default';
			$layout_width     = auto_sizes_get_layout_width( $layout_alignment );

			/*
			 * If the layout width is in pixels, we can compare against the image width
			 * on the server. Otherwise, we need to rely on CSS functions.
			 *
			 * TODO: Add support for em, rem, vh, and vw.
			 */
			if (
				str_ends_with( $layout_width, 'px' ) &&
				( $container_relative_width > 0.0 ||
				$container_relative_width < 1.0 )
			) {
				// First remove 'px' from width.
				$layout_width = str_replace( 'px', '', $layout_width );
				// Convert to float for better precision.
				$layout_width = (float) $layout_width * $container_relative_width;
				$layout_width = sprintf( '%dpx', min( (int) $layout_width, $image_width ) );
			} else {
				$layout_width = sprintf( 'min(%1$s, %2$spx)', $layout_width, $image_width );
			}

			break;
	}

	// Format layout width when not 'full'.
	if ( 'full' !== $alignment ) {
		$layout_width = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout_width );
	}

	return $layout_width;
}

/**
 * Retrieves the layout width for an alignment defined in theme.json.
 *
 * @since 1.4.0
 *
 * @param string $alignment The alignment value.
 * @return string The alignment width based.
 */
function auto_sizes_get_layout_width( string $alignment ): string {
	$layout = wp_get_global_settings( array( 'layout' ) );

	$layout_widths = array(
		'full'    => '100vw', // Todo: incorporate useRootPaddingAwareAlignments.
		'wide'    => array_key_exists( 'wideSize', $layout ) ? $layout['wideSize'] : '',
		'default' => array_key_exists( 'contentSize', $layout ) ? $layout['contentSize'] : '',
	);

	return $layout_widths[ $alignment ] ?? '';
}

/**
 * Filters the context keys that a block type uses.
 *
 * @since 1.4.0
 *
 * @param string[]      $uses_context Array of registered uses context for a block type.
 * @param WP_Block_Type $block_type   The full block type object.
 * @return string[] The filtered context keys used by the block type.
 */
function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $block_type ): array {
	// Define block-specific context usage.
	$block_specific_context = array(
		'core/cover'               => array( 'max_alignment', 'container_relative_width' ),
		'core/image'               => array( 'max_alignment', 'container_relative_width' ),
		'core/post-featured-image' => array( 'max_alignment', 'container_relative_width' ),
		'core/group'               => array( 'max_alignment' ),
		'core/columns'             => array( 'max_alignment', 'column_count', 'container_relative_width' ),
		'core/column'              => array( 'max_alignment' ),
	);

	if ( isset( $block_specific_context[ $block_type->name ] ) ) {
		// Use array_values to reset array keys after merging.
		return array_values( array_unique( array_merge( $uses_context, $block_specific_context[ $block_type->name ] ) ) );
	}
	return $uses_context;
}

/**
 * Modifies the block context during rendering to blocks.
 *
 * @since 1.4.0
 *
 * @param array<string, mixed> $context      Current block context.
 * @param array<string, mixed> $block        The block being rendered.
 * @param WP_Block|null        $parent_block If this is a nested block, a reference to the parent block.
 * @return array<string, mixed> Modified block context.
 */
function auto_sizes_filter_render_block_context( array $context, array $block, ?WP_Block $parent_block ): array {
	// When no max alignment is set, the maximum is assumed to be 'full'.
	$context['max_alignment'] = $context['max_alignment'] ?? 'full';

	// The list of blocks that can modify outer layout context.
	$provider_blocks = array(
		'core/columns',
		'core/group',
		'core/post-featured-image',
	);

	if ( in_array( $block['blockName'], $provider_blocks, true ) ) {
		// Normalize default alignment values.
		$alignment = isset( $block['attrs']['align'] ) && '' !== $block['attrs']['align'] ? $block['attrs']['align'] : 'default';
		// Use the defined constant for constraints.
		$constraints = AUTO_SIZES_CONSTRAINTS;

		$context['max_alignment'] = $constraints[ $context['max_alignment'] ] > $constraints[ $alignment ] ? $context['max_alignment'] : $alignment;
	}

	if ( 'core/columns' === $block['blockName'] ) {
		// This is a special context key just to pass to the child 'core/column' block.
		$context['column_count'] = count( $block['innerBlocks'] );
	}

	if ( 'core/column' === $block['blockName'] ) {
		$found_image_block = wp_get_first_block( $block['innerBlocks'], 'core/image' );
		$found_cover_block = wp_get_first_block( $block['innerBlocks'], 'core/cover' );
		if ( count( $found_image_block ) > 0 || count( $found_cover_block ) > 0 ) {
			// Get column width, if explicitly set.
			if ( isset( $block['attrs']['width'] ) && '' !== $block['attrs']['width'] ) {
				$current_width = floatval( rtrim( $block['attrs']['width'], '%' ) ) / 100;
			} elseif ( isset( $parent_block->context['column_count'] ) && $parent_block->context['column_count'] ) {
				// Default to equally divided width if not explicitly set.
				$current_width = 1.0 / $parent_block->context['column_count'];
			} else {
				// Full width fallback.
				$current_width = 1.0;
			}

			// Multiply with parent's width if available.
			if (
				isset( $parent_block->context['container_relative_width'] ) &&
				( $current_width > 0.0 || $current_width < 1.0 )
			) {
				$context['container_relative_width'] = $parent_block->context['container_relative_width'] * $current_width;
			} else {
				$context['container_relative_width'] = $current_width;
			}
		}
	}
	return $context;
}

/**
 * Retrieves the featured image attachment ID for a given post ID.
 *
 * @since 1.6.0
 *
 * @param int $post_id The post ID.
 * @return int The featured image attachment ID or 0 if not found.
 */
function auto_sizes_get_featured_image_attachment_id( int $post_id ): int {
	if ( 0 === $post_id ) {
		return 0;
	}

	return (int) get_post_thumbnail_id( $post_id );
}