diff -r be944660c56a -r 3d72ae0968f4 wp/wp-includes/media.php
--- a/wp/wp-includes/media.php Wed Sep 21 18:19:35 2022 +0200
+++ b/wp/wp-includes/media.php Tue Sep 27 16:37:53 2022 +0200
@@ -393,7 +393,7 @@
*/
$class = apply_filters( 'get_image_tag_class', $class, $id, $align, $size );
- $html = '
';
+ $html = '
';
/**
* Filters the HTML content for the image tag.
@@ -882,7 +882,8 @@
* @uses wp_get_additional_image_sizes()
* @uses get_intermediate_image_sizes()
*
- * @return array Associative array of the registered image sub-sizes.
+ * @return array[] Associative array of arrays of image sub-size information,
+ * keyed by image size name.
*/
function wp_get_registered_image_subsizes() {
$additional_sizes = wp_get_additional_image_sizes();
@@ -1046,7 +1047,7 @@
// Add `loading` attribute.
if ( wp_lazy_loading_enabled( 'img', 'wp_get_attachment_image' ) ) {
- $default_attr['loading'] = 'lazy';
+ $default_attr['loading'] = wp_get_loading_attr_default( 'wp_get_attachment_image' );
}
$attr = wp_parse_args( $attr, $default_attr );
@@ -1129,7 +1130,7 @@
*/
function wp_get_attachment_image_url( $attachment_id, $size = 'thumbnail', $icon = false ) {
$image = wp_get_attachment_image_src( $attachment_id, $size, $icon );
- return isset( $image['0'] ) ? $image['0'] : false;
+ return isset( $image[0] ) ? $image[0] : false;
}
/**
@@ -1820,39 +1821,68 @@
_prime_post_caches( $attachment_ids, false, true );
}
- foreach ( $images as $image => $attachment_id ) {
- $filtered_image = $image;
-
- // Add 'width' and 'height' attributes if applicable.
- if ( $attachment_id > 0 && false === strpos( $filtered_image, ' width=' ) && false === strpos( $filtered_image, ' height=' ) ) {
- $filtered_image = wp_img_tag_add_width_and_height_attr( $filtered_image, $context, $attachment_id );
- }
-
- // Add 'srcset' and 'sizes' attributes if applicable.
- if ( $attachment_id > 0 && false === strpos( $filtered_image, ' srcset=' ) ) {
- $filtered_image = wp_img_tag_add_srcset_and_sizes_attr( $filtered_image, $context, $attachment_id );
- }
-
- // Add 'loading' attribute if applicable.
- if ( $add_img_loading_attr && false === strpos( $filtered_image, ' loading=' ) ) {
- $filtered_image = wp_img_tag_add_loading_attr( $filtered_image, $context );
+ // Iterate through the matches in order of occurrence as it is relevant for whether or not to lazy-load.
+ foreach ( $matches as $match ) {
+ // Filter an image match.
+ if ( isset( $images[ $match[0] ] ) ) {
+ $filtered_image = $match[0];
+ $attachment_id = $images[ $match[0] ];
+
+ // Add 'width' and 'height' attributes if applicable.
+ if ( $attachment_id > 0 && false === strpos( $filtered_image, ' width=' ) && false === strpos( $filtered_image, ' height=' ) ) {
+ $filtered_image = wp_img_tag_add_width_and_height_attr( $filtered_image, $context, $attachment_id );
+ }
+
+ // Add 'srcset' and 'sizes' attributes if applicable.
+ if ( $attachment_id > 0 && false === strpos( $filtered_image, ' srcset=' ) ) {
+ $filtered_image = wp_img_tag_add_srcset_and_sizes_attr( $filtered_image, $context, $attachment_id );
+ }
+
+ // Add 'loading' attribute if applicable.
+ if ( $add_img_loading_attr && false === strpos( $filtered_image, ' loading=' ) ) {
+ $filtered_image = wp_img_tag_add_loading_attr( $filtered_image, $context );
+ }
+
+ /**
+ * Filters an img tag within the content for a given context.
+ *
+ * @since 6.0.0
+ *
+ * @param string $filtered_image Full img tag with attributes that will replace the source img tag.
+ * @param string $context Additional context, like the current filter name or the function name from where this was called.
+ * @param int $attachment_id The image attachment ID. May be 0 in case the image is not an attachment.
+ */
+ $filtered_image = apply_filters( 'wp_content_img_tag', $filtered_image, $context, $attachment_id );
+
+ if ( $filtered_image !== $match[0] ) {
+ $content = str_replace( $match[0], $filtered_image, $content );
+ }
+
+ /*
+ * Unset image lookup to not run the same logic again unnecessarily if the same image tag is used more than
+ * once in the same blob of content.
+ */
+ unset( $images[ $match[0] ] );
}
- if ( $filtered_image !== $image ) {
- $content = str_replace( $image, $filtered_image, $content );
- }
- }
-
- foreach ( $iframes as $iframe => $attachment_id ) {
- $filtered_iframe = $iframe;
-
- // Add 'loading' attribute if applicable.
- if ( $add_iframe_loading_attr && false === strpos( $filtered_iframe, ' loading=' ) ) {
- $filtered_iframe = wp_iframe_tag_add_loading_attr( $filtered_iframe, $context );
- }
-
- if ( $filtered_iframe !== $iframe ) {
- $content = str_replace( $iframe, $filtered_iframe, $content );
+ // Filter an iframe match.
+ if ( isset( $iframes[ $match[0] ] ) ) {
+ $filtered_iframe = $match[0];
+
+ // Add 'loading' attribute if applicable.
+ if ( $add_iframe_loading_attr && false === strpos( $filtered_iframe, ' loading=' ) ) {
+ $filtered_iframe = wp_iframe_tag_add_loading_attr( $filtered_iframe, $context );
+ }
+
+ if ( $filtered_iframe !== $match[0] ) {
+ $content = str_replace( $match[0], $filtered_iframe, $content );
+ }
+
+ /*
+ * Unset iframe lookup to not run the same logic again unnecessarily if the same iframe tag is used more
+ * than once in the same blob of content.
+ */
+ unset( $iframes[ $match[0] ] );
}
}
@@ -1869,6 +1899,10 @@
* @return string Converted `img` tag with `loading` attribute added.
*/
function wp_img_tag_add_loading_attr( $image, $context ) {
+ // Get loading attribute value to use. This must occur before the conditional check below so that even images that
+ // are ineligible for being lazy-loaded are considered.
+ $value = wp_get_loading_attr_default( $context );
+
// Images should have source and dimension attributes for the `loading` attribute to be added.
if ( false === strpos( $image, ' src="' ) || false === strpos( $image, ' width="' ) || false === strpos( $image, ' height="' ) ) {
return $image;
@@ -1883,11 +1917,11 @@
* @since 5.5.0
*
* @param string|bool $value The `loading` attribute value. Returning a falsey value will result in
- * the attribute being omitted for the image. Default 'lazy'.
+ * the attribute being omitted for the image.
* @param string $image The HTML `img` tag to be filtered.
* @param string $context Additional context about how the function was called or where the img tag is.
*/
- $value = apply_filters( 'wp_img_tag_add_loading_attr', 'lazy', $image, $context );
+ $value = apply_filters( 'wp_img_tag_add_loading_attr', $value, $image, $context );
if ( $value ) {
if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
@@ -1995,6 +2029,10 @@
return $iframe;
}
+ // Get loading attribute value to use. This must occur before the conditional check below so that even iframes that
+ // are ineligible for being lazy-loaded are considered.
+ $value = wp_get_loading_attr_default( $context );
+
// Iframes should have source and dimension attributes for the `loading` attribute to be added.
if ( false === strpos( $iframe, ' src="' ) || false === strpos( $iframe, ' width="' ) || false === strpos( $iframe, ' height="' ) ) {
return $iframe;
@@ -2009,11 +2047,11 @@
* @since 5.7.0
*
* @param string|bool $value The `loading` attribute value. Returning a falsey value will result in
- * the attribute being omitted for the iframe. Default 'lazy'.
+ * the attribute being omitted for the iframe.
* @param string $iframe The HTML `iframe` tag to be filtered.
* @param string $context Additional context about how the function was called or where the iframe tag is.
*/
- $value = apply_filters( 'wp_iframe_tag_add_loading_attr', 'lazy', $iframe, $context );
+ $value = apply_filters( 'wp_iframe_tag_add_loading_attr', $value, $iframe, $context );
if ( $value ) {
if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
@@ -2085,6 +2123,7 @@
* @since 2.6.0
* @since 3.9.0 The `class` attribute was added.
* @since 5.1.0 The `caption_id` attribute was added.
+ * @since 5.9.0 The `$content` parameter default value changed from `null` to `''`.
*
* @param array $attr {
* Attributes of the caption shortcode.
@@ -2097,10 +2136,10 @@
* @type string $caption The caption text.
* @type string $class Additional class name(s) added to the caption container.
* }
- * @param string $content Shortcode content.
+ * @param string $content Optional. Shortcode content. Default empty string.
* @return string HTML content to display the caption.
*/
-function img_caption_shortcode( $attr, $content = null ) {
+function img_caption_shortcode( $attr, $content = '' ) {
// New-style shortcode with the caption inside the shortcode with the link and image tags.
if ( ! isset( $attr['caption'] ) ) {
if ( preg_match( '#((?:]+>\s*)?
]+>(?:\s*)?)(.*)#is', $content, $matches ) ) {
@@ -4030,7 +4069,7 @@
if ( isset( $meta['filesize'] ) ) {
$bytes = $meta['filesize'];
} elseif ( file_exists( $attached_file ) ) {
- $bytes = filesize( $attached_file );
+ $bytes = wp_filesize( $attached_file );
} else {
$bytes = '';
}
@@ -4209,7 +4248,7 @@
* @param array $args {
* Arguments for enqueuing media scripts.
*
- * @type int|WP_Post A post object or ID.
+ * @type int|WP_Post $post A post object or ID.
* }
*/
function wp_enqueue_media( $args = array() ) {
@@ -4455,7 +4494,7 @@
'allDates' => __( 'All dates' ),
'noItemsFound' => __( 'No items found.' ),
'insertIntoPost' => $post_type_object->labels->insert_into_item,
- 'unattached' => __( 'Unattached' ),
+ 'unattached' => _x( 'Unattached', 'media items' ),
'mine' => _x( 'Mine', 'media items' ),
'trash' => _x( 'Trash', 'noun' ),
'uploadedToThisPost' => $post_type_object->labels->uploaded_to_this_item,
@@ -4466,6 +4505,7 @@
'trashSelected' => __( 'Move to Trash' ),
'restoreSelected' => __( 'Restore from Trash' ),
'deletePermanently' => __( 'Delete permanently' ),
+ 'errorDeleting' => __( 'Error in deleting the attachment.' ),
'apply' => __( 'Apply' ),
'filterByDate' => __( 'Filter by date' ),
'filterByType' => __( 'Filter by type' ),
@@ -4710,7 +4750,7 @@
return array();
}
- if ( ! has_shortcode( $post->post_content, 'gallery' ) ) {
+ if ( ! has_shortcode( $post->post_content, 'gallery' ) && ! has_block( 'gallery', $post->post_content ) ) {
return array();
}
@@ -4752,6 +4792,98 @@
}
}
+ if ( has_block( 'gallery', $post->post_content ) ) {
+ $post_blocks = parse_blocks( $post->post_content );
+
+ while ( $block = array_shift( $post_blocks ) ) {
+ $has_inner_blocks = ! empty( $block['innerBlocks'] );
+
+ // Skip blocks with no blockName and no innerHTML.
+ if ( ! $block['blockName'] ) {
+ continue;
+ }
+
+ // Skip non-Gallery blocks.
+ if ( 'core/gallery' !== $block['blockName'] ) {
+ // Move inner blocks into the root array before skipping.
+ if ( $has_inner_blocks ) {
+ array_push( $post_blocks, ...$block['innerBlocks'] );
+ }
+ continue;
+ }
+
+ // New Gallery block format as HTML.
+ if ( $has_inner_blocks && $html ) {
+ $block_html = wp_list_pluck( $block['innerBlocks'], 'innerHTML' );
+ $galleries[] = '' . implode( ' ', $block_html ) . '';
+ continue;
+ }
+
+ $srcs = array();
+
+ // New Gallery block format as an array.
+ if ( $has_inner_blocks ) {
+ $attrs = wp_list_pluck( $block['innerBlocks'], 'attrs' );
+ $ids = wp_list_pluck( $attrs, 'id' );
+
+ foreach ( $ids as $id ) {
+ $url = wp_get_attachment_url( $id );
+
+ if ( is_string( $url ) && ! in_array( $url, $srcs, true ) ) {
+ $srcs[] = $url;
+ }
+ }
+
+ $galleries[] = array(
+ 'ids' => implode( ',', $ids ),
+ 'src' => $srcs,
+ );
+
+ continue;
+ }
+
+ // Old Gallery block format as HTML.
+ if ( $html ) {
+ $galleries[] = $block['innerHTML'];
+ continue;
+ }
+
+ // Old Gallery block format as an array.
+ $ids = ! empty( $block['attrs']['ids'] ) ? $block['attrs']['ids'] : array();
+
+ // If present, use the image IDs from the JSON blob as canonical.
+ if ( ! empty( $ids ) ) {
+ foreach ( $ids as $id ) {
+ $url = wp_get_attachment_url( $id );
+
+ if ( is_string( $url ) && ! in_array( $url, $srcs, true ) ) {
+ $srcs[] = $url;
+ }
+ }
+
+ $galleries[] = array(
+ 'ids' => implode( ',', $ids ),
+ 'src' => $srcs,
+ );
+
+ continue;
+ }
+
+ // Otherwise, extract srcs from the innerHTML.
+ preg_match_all( '#src=([\'"])(.+?)\1#is', $block['innerHTML'], $found_srcs, PREG_SET_ORDER );
+
+ if ( ! empty( $found_srcs[0] ) ) {
+ foreach ( $found_srcs as $src ) {
+ if ( isset( $src[2] ) && ! in_array( $src[2], $srcs, true ) ) {
+ $srcs[] = $src[2];
+ }
+ }
+ }
+
+ $galleries[] = array( 'src' => $srcs );
+ }
+ }
+
/**
* Filters the list of all found galleries in the given post.
*
@@ -5091,7 +5223,7 @@
return array(
$width,
$height,
- IMAGETYPE_WEBP, // phpcs:ignore PHPCompatibility.Constants.NewConstants.imagetype_webpFound
+ IMAGETYPE_WEBP,
sprintf(
'width="%d" height="%d"',
$width,
@@ -5107,20 +5239,19 @@
}
/**
- * Extracts meta information about a webp file: width, height and type.
+ * Extracts meta information about a WebP file: width, height, and type.
*
* @since 5.8.0
*
* @param string $filename Path to a WebP file.
- * @return array $webp_info {
+ * @return array {
* An array of WebP image information.
*
- * @type array $size {
- * @type int|false $width Image width on success, false on failure.
- * @type int|false $height Image height on success, false on failure.
- * @type string|false $type The WebP type: one of 'lossy', 'lossless' or 'animated-alpha'.
- * False on failure.
- * }
+ * @type int|false $width Image width on success, false on failure.
+ * @type int|false $height Image height on success, false on failure.
+ * @type string|false $type The WebP type: one of 'lossy', 'lossless' or 'animated-alpha'.
+ * False on failure.
+ * }
*/
function wp_get_webp_info( $filename ) {
$width = false;
@@ -5131,48 +5262,139 @@
return compact( 'width', 'height', 'type' );
}
- try {
- $handle = fopen( $filename, 'rb' );
- if ( $handle ) {
- $magic = fread( $handle, 40 );
- fclose( $handle );
-
- // Make sure we got enough bytes.
- if ( strlen( $magic ) < 40 ) {
- return compact( 'width', 'height', 'type' );
- }
-
- // The headers are a little different for each of the three formats.
- // Header values based on WebP docs, see https://developers.google.com/speed/webp/docs/riff_container.
- switch ( substr( $magic, 12, 4 ) ) {
- // Lossy WebP.
- case 'VP8 ':
- $parts = unpack( 'v2', substr( $magic, 26, 4 ) );
- $width = (int) ( $parts[1] & 0x3FFF );
- $height = (int) ( $parts[2] & 0x3FFF );
- $type = 'lossy';
- break;
- // Lossless WebP.
- case 'VP8L':
- $parts = unpack( 'C4', substr( $magic, 21, 4 ) );
- $width = (int) ( $parts[1] | ( ( $parts[2] & 0x3F ) << 8 ) ) + 1;
- $height = (int) ( ( ( $parts[2] & 0xC0 ) >> 6 ) | ( $parts[3] << 2 ) | ( ( $parts[4] & 0x03 ) << 10 ) ) + 1;
- $type = 'lossless';
- break;
- // Animated/alpha WebP.
- case 'VP8X':
- // Pad 24-bit int.
- $width = unpack( 'V', substr( $magic, 24, 3 ) . "\x00" );
- $width = (int) ( $width[1] & 0xFFFFFF ) + 1;
- // Pad 24-bit int.
- $height = unpack( 'V', substr( $magic, 27, 3 ) . "\x00" );
- $height = (int) ( $height[1] & 0xFFFFFF ) + 1;
- $type = 'animated-alpha';
- break;
- }
- }
- } catch ( Exception $e ) {
+ $magic = file_get_contents( $filename, false, null, 0, 40 );
+
+ if ( false === $magic ) {
+ return compact( 'width', 'height', 'type' );
+ }
+
+ // Make sure we got enough bytes.
+ if ( strlen( $magic ) < 40 ) {
+ return compact( 'width', 'height', 'type' );
+ }
+
+ // The headers are a little different for each of the three formats.
+ // Header values based on WebP docs, see https://developers.google.com/speed/webp/docs/riff_container.
+ switch ( substr( $magic, 12, 4 ) ) {
+ // Lossy WebP.
+ case 'VP8 ':
+ $parts = unpack( 'v2', substr( $magic, 26, 4 ) );
+ $width = (int) ( $parts[1] & 0x3FFF );
+ $height = (int) ( $parts[2] & 0x3FFF );
+ $type = 'lossy';
+ break;
+ // Lossless WebP.
+ case 'VP8L':
+ $parts = unpack( 'C4', substr( $magic, 21, 4 ) );
+ $width = (int) ( $parts[1] | ( ( $parts[2] & 0x3F ) << 8 ) ) + 1;
+ $height = (int) ( ( ( $parts[2] & 0xC0 ) >> 6 ) | ( $parts[3] << 2 ) | ( ( $parts[4] & 0x03 ) << 10 ) ) + 1;
+ $type = 'lossless';
+ break;
+ // Animated/alpha WebP.
+ case 'VP8X':
+ // Pad 24-bit int.
+ $width = unpack( 'V', substr( $magic, 24, 3 ) . "\x00" );
+ $width = (int) ( $width[1] & 0xFFFFFF ) + 1;
+ // Pad 24-bit int.
+ $height = unpack( 'V', substr( $magic, 27, 3 ) . "\x00" );
+ $height = (int) ( $height[1] & 0xFFFFFF ) + 1;
+ $type = 'animated-alpha';
+ break;
}
return compact( 'width', 'height', 'type' );
}
+
+/**
+ * Gets the default value to use for a `loading` attribute on an element.
+ *
+ * This function should only be called for a tag and context if lazy-loading is generally enabled.
+ *
+ * The function usually returns 'lazy', but uses certain heuristics to guess whether the current element is likely to
+ * appear above the fold, in which case it returns a boolean `false`, which will lead to the `loading` attribute being
+ * omitted on the element. The purpose of this refinement is to avoid lazy-loading elements that are within the initial
+ * viewport, which can have a negative performance impact.
+ *
+ * Under the hood, the function uses {@see wp_increase_content_media_count()} every time it is called for an element
+ * within the main content. If the element is the very first content element, the `loading` attribute will be omitted.
+ * This default threshold of 1 content element to omit the `loading` attribute for can be customized using the
+ * {@see 'wp_omit_loading_attr_threshold'} filter.
+ *
+ * @since 5.9.0
+ *
+ * @param string $context Context for the element for which the `loading` attribute value is requested.
+ * @return string|bool The default `loading` attribute value. Either 'lazy', 'eager', or a boolean `false`, to indicate
+ * that the `loading` attribute should be skipped.
+ */
+function wp_get_loading_attr_default( $context ) {
+ // Only elements with 'the_content' or 'the_post_thumbnail' context have special handling.
+ if ( 'the_content' !== $context && 'the_post_thumbnail' !== $context ) {
+ return 'lazy';
+ }
+
+ // Only elements within the main query loop have special handling.
+ if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
+ return 'lazy';
+ }
+
+ // Increase the counter since this is a main query content element.
+ $content_media_count = wp_increase_content_media_count();
+
+ // If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
+ if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
+ return false;
+ }
+
+ // For elements after the threshold, lazy-load them as usual.
+ return 'lazy';
+}
+
+/**
+ * Gets the threshold for how many of the first content media elements to not lazy-load.
+ *
+ * This function runs the {@see 'wp_omit_loading_attr_threshold'} filter, which uses a default threshold value of 1.
+ * The filter is only run once per page load, unless the `$force` parameter is used.
+ *
+ * @since 5.9.0
+ *
+ * @param bool $force Optional. If set to true, the filter will be (re-)applied even if it already has been before.
+ * Default false.
+ * @return int The number of content media elements to not lazy-load.
+ */
+function wp_omit_loading_attr_threshold( $force = false ) {
+ static $omit_threshold;
+
+ // This function may be called multiple times. Run the filter only once per page load.
+ if ( ! isset( $omit_threshold ) || $force ) {
+ /**
+ * Filters the threshold for how many of the first content media elements to not lazy-load.
+ *
+ * For these first content media elements, the `loading` attribute will be omitted. By default, this is the case
+ * for only the very first content media element.
+ *
+ * @since 5.9.0
+ *
+ * @param int $omit_threshold The number of media elements where the `loading` attribute will not be added. Default 1.
+ */
+ $omit_threshold = apply_filters( 'wp_omit_loading_attr_threshold', 1 );
+ }
+
+ return $omit_threshold;
+}
+
+/**
+ * Increases an internal content media count variable.
+ *
+ * @since 5.9.0
+ * @access private
+ *
+ * @param int $amount Optional. Amount to increase by. Default 1.
+ * @return int The latest content media count, after the increase.
+ */
+function wp_increase_content_media_count( $amount = 1 ) {
+ static $content_media_count = 0;
+
+ $content_media_count += $amount;
+
+ return $content_media_count;
+}