wp/wp-includes/media.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     5  * @package WordPress
     5  * @package WordPress
     6  * @subpackage Media
     6  * @subpackage Media
     7  */
     7  */
     8 
     8 
     9 /**
     9 /**
    10  * Retrieve additional image sizes.
    10  * Retrieves additional image sizes.
    11  *
    11  *
    12  * @since 4.7.0
    12  * @since 4.7.0
    13  *
    13  *
    14  * @global array $_wp_additional_image_sizes
    14  * @global array $_wp_additional_image_sizes
    15  *
    15  *
    24 
    24 
    25 	return $_wp_additional_image_sizes;
    25 	return $_wp_additional_image_sizes;
    26 }
    26 }
    27 
    27 
    28 /**
    28 /**
    29  * Scale down the default size of an image.
    29  * Scales down the default size of an image.
    30  *
    30  *
    31  * This is so that the image is a better fit for the editor and theme.
    31  * This is so that the image is a better fit for the editor and theme.
    32  *
    32  *
    33  * The `$size` parameter accepts either an array or a string. The supported string
    33  * The `$size` parameter accepts either an array or a string. The supported string
    34  * values are 'thumb' or 'thumbnail' for the given thumbnail size or defaults at
    34  * values are 'thumb' or 'thumbnail' for the given thumbnail size or defaults at
   134 
   134 
   135 	return wp_constrain_dimensions( $width, $height, $max_width, $max_height );
   135 	return wp_constrain_dimensions( $width, $height, $max_width, $max_height );
   136 }
   136 }
   137 
   137 
   138 /**
   138 /**
   139  * Retrieve width and height attributes using given width and height values.
   139  * Retrieves width and height attributes using given width and height values.
   140  *
   140  *
   141  * Both attributes are required in the sense that both parameters must have a
   141  * Both attributes are required in the sense that both parameters must have a
   142  * value, but are optional in that if you set them to false or null, then they
   142  * value, but are optional in that if you set them to false or null, then they
   143  * will not be added to the returned string.
   143  * will not be added to the returned string.
   144  *
   144  *
   162 	}
   162 	}
   163 	return $out;
   163 	return $out;
   164 }
   164 }
   165 
   165 
   166 /**
   166 /**
   167  * Scale an image to fit a particular size (such as 'thumb' or 'medium').
   167  * Scales an image to fit a particular size (such as 'thumb' or 'medium').
   168  *
   168  *
   169  * The URL might be the original image, or it might be a resized version. This
   169  * The URL might be the original image, or it might be a resized version. This
   170  * function won't create a new resized copy, it will just return an already
   170  * function won't create a new resized copy, it will just return an already
   171  * resized one if it exists.
   171  * resized one if it exists.
   172  *
   172  *
   215 	$width            = 0;
   215 	$width            = 0;
   216 	$height           = 0;
   216 	$height           = 0;
   217 	$is_intermediate  = false;
   217 	$is_intermediate  = false;
   218 	$img_url_basename = wp_basename( $img_url );
   218 	$img_url_basename = wp_basename( $img_url );
   219 
   219 
   220 	// If the file isn't an image, attempt to replace its URL with a rendered image from its meta.
   220 	/*
   221 	// Otherwise, a non-image type could be returned.
   221 	 * If the file isn't an image, attempt to replace its URL with a rendered image from its meta.
       
   222 	 * Otherwise, a non-image type could be returned.
       
   223 	 */
   222 	if ( ! $is_image ) {
   224 	if ( ! $is_image ) {
   223 		if ( ! empty( $meta['sizes']['full'] ) ) {
   225 		if ( ! empty( $meta['sizes']['full'] ) ) {
   224 			$img_url          = str_replace( $img_url_basename, $meta['sizes']['full']['file'], $img_url );
   226 			$img_url          = str_replace( $img_url_basename, $meta['sizes']['full']['file'], $img_url );
   225 			$img_url_basename = $meta['sizes']['full']['file'];
   227 			$img_url_basename = $meta['sizes']['full']['file'];
   226 			$width            = $meta['sizes']['full']['width'];
   228 			$width            = $meta['sizes']['full']['width'];
   236 	if ( $intermediate ) {
   238 	if ( $intermediate ) {
   237 		$img_url         = str_replace( $img_url_basename, $intermediate['file'], $img_url );
   239 		$img_url         = str_replace( $img_url_basename, $intermediate['file'], $img_url );
   238 		$width           = $intermediate['width'];
   240 		$width           = $intermediate['width'];
   239 		$height          = $intermediate['height'];
   241 		$height          = $intermediate['height'];
   240 		$is_intermediate = true;
   242 		$is_intermediate = true;
   241 	} elseif ( 'thumbnail' === $size ) {
   243 	} elseif ( 'thumbnail' === $size && ! empty( $meta['thumb'] ) && is_string( $meta['thumb'] ) ) {
   242 		// Fall back to the old thumbnail.
   244 		// Fall back to the old thumbnail.
   243 		$thumb_file = wp_get_attachment_thumb_file( $id );
   245 		$imagefile = get_attached_file( $id );
   244 		$info       = null;
   246 		$thumbfile = str_replace( wp_basename( $imagefile ), wp_basename( $meta['thumb'] ), $imagefile );
   245 
   247 
   246 		if ( $thumb_file ) {
   248 		if ( file_exists( $thumbfile ) ) {
   247 			$info = wp_getimagesize( $thumb_file );
   249 			$info = wp_getimagesize( $thumbfile );
   248 		}
   250 
   249 
   251 			if ( $info ) {
   250 		if ( $thumb_file && $info ) {
   252 				$img_url         = str_replace( $img_url_basename, wp_basename( $thumbfile ), $img_url );
   251 			$img_url         = str_replace( $img_url_basename, wp_basename( $thumb_file ), $img_url );
   253 				$width           = $info[0];
   252 			$width           = $info[0];
   254 				$height          = $info[1];
   253 			$height          = $info[1];
   255 				$is_intermediate = true;
   254 			$is_intermediate = true;
   256 			}
   255 		}
   257 		}
   256 	}
   258 	}
   257 
   259 
   258 	if ( ! $width && ! $height && isset( $meta['width'], $meta['height'] ) ) {
   260 	if ( ! $width && ! $height && isset( $meta['width'], $meta['height'] ) ) {
   259 		// Any other type: use the real image.
   261 		// Any other type: use the real image.
   270 
   272 
   271 	return false;
   273 	return false;
   272 }
   274 }
   273 
   275 
   274 /**
   276 /**
   275  * Register a new image size.
   277  * Registers a new image size.
   276  *
   278  *
   277  * @since 2.9.0
   279  * @since 2.9.0
   278  *
   280  *
   279  * @global array $_wp_additional_image_sizes Associative array of additional image sizes.
   281  * @global array $_wp_additional_image_sizes Associative array of additional image sizes.
   280  *
   282  *
   281  * @param string     $name   Image size identifier.
   283  * @param string     $name   Image size identifier.
   282  * @param int        $width  Optional. Image width in pixels. Default 0.
   284  * @param int        $width  Optional. Image width in pixels. Default 0.
   283  * @param int        $height Optional. Image height in pixels. Default 0.
   285  * @param int        $height Optional. Image height in pixels. Default 0.
   284  * @param bool|array $crop   Optional. Image cropping behavior. If false, the image will be scaled (default),
   286  * @param bool|array $crop   {
   285  *                           If true, image will be cropped to the specified dimensions using center positions.
   287  *     Optional. Image cropping behavior. If false, the image will be scaled (default).
   286  *                           If an array, the image will be cropped using the array to specify the crop location.
   288  *     If true, image will be cropped to the specified dimensions using center positions.
   287  *                           Array values must be in the format: array( x_crop_position, y_crop_position ) where:
   289  *     If an array, the image will be cropped using the array to specify the crop location:
   288  *                               - x_crop_position accepts: 'left', 'center', or 'right'.
   290  *
   289  *                               - y_crop_position accepts: 'top', 'center', or 'bottom'.
   291  *     @type string $0 The x crop position. Accepts 'left' 'center', or 'right'.
       
   292  *     @type string $1 The y crop position. Accepts 'top', 'center', or 'bottom'.
       
   293  * }
   290  */
   294  */
   291 function add_image_size( $name, $width = 0, $height = 0, $crop = false ) {
   295 function add_image_size( $name, $width = 0, $height = 0, $crop = false ) {
   292 	global $_wp_additional_image_sizes;
   296 	global $_wp_additional_image_sizes;
   293 
   297 
   294 	$_wp_additional_image_sizes[ $name ] = array(
   298 	$_wp_additional_image_sizes[ $name ] = array(
   297 		'crop'   => $crop,
   301 		'crop'   => $crop,
   298 	);
   302 	);
   299 }
   303 }
   300 
   304 
   301 /**
   305 /**
   302  * Check if an image size exists.
   306  * Checks if an image size exists.
   303  *
   307  *
   304  * @since 3.9.0
   308  * @since 3.9.0
   305  *
   309  *
   306  * @param string $name The image size to check.
   310  * @param string $name The image size to check.
   307  * @return bool True if the image size exists, false if not.
   311  * @return bool True if the image size exists, false if not.
   310 	$sizes = wp_get_additional_image_sizes();
   314 	$sizes = wp_get_additional_image_sizes();
   311 	return isset( $sizes[ $name ] );
   315 	return isset( $sizes[ $name ] );
   312 }
   316 }
   313 
   317 
   314 /**
   318 /**
   315  * Remove a new image size.
   319  * Removes a new image size.
   316  *
   320  *
   317  * @since 3.9.0
   321  * @since 3.9.0
   318  *
   322  *
   319  * @global array $_wp_additional_image_sizes
   323  * @global array $_wp_additional_image_sizes
   320  *
   324  *
   339  *
   343  *
   340  * @see add_image_size() for details on cropping behavior.
   344  * @see add_image_size() for details on cropping behavior.
   341  *
   345  *
   342  * @param int        $width  Image width in pixels.
   346  * @param int        $width  Image width in pixels.
   343  * @param int        $height Image height in pixels.
   347  * @param int        $height Image height in pixels.
   344  * @param bool|array $crop   Optional. Whether to crop images to specified width and height or resize.
   348  * @param bool|array $crop   {
   345  *                           An array can specify positioning of the crop area. Default false.
   349  *     Optional. Image cropping behavior. If false, the image will be scaled (default).
       
   350  *     If true, image will be cropped to the specified dimensions using center positions.
       
   351  *     If an array, the image will be cropped using the array to specify the crop location:
       
   352  *
       
   353  *     @type string $0 The x crop position. Accepts 'left' 'center', or 'right'.
       
   354  *     @type string $1 The y crop position. Accepts 'top', 'center', or 'bottom'.
       
   355  * }
   346  */
   356  */
   347 function set_post_thumbnail_size( $width = 0, $height = 0, $crop = false ) {
   357 function set_post_thumbnail_size( $width = 0, $height = 0, $crop = false ) {
   348 	add_image_size( 'post-thumbnail', $width, $height, $crop );
   358 	add_image_size( 'post-thumbnail', $width, $height, $crop );
   349 }
   359 }
   350 
   360 
   366  * @param string       $alt   Image description for the alt attribute.
   376  * @param string       $alt   Image description for the alt attribute.
   367  * @param string       $title Image description for the title attribute.
   377  * @param string       $title Image description for the title attribute.
   368  * @param string       $align Part of the class name for aligning the image.
   378  * @param string       $align Part of the class name for aligning the image.
   369  * @param string|int[] $size  Optional. Image size. Accepts any registered image size name, or an array of
   379  * @param string|int[] $size  Optional. Image size. Accepts any registered image size name, or an array of
   370  *                            width and height values in pixels (in that order). Default 'medium'.
   380  *                            width and height values in pixels (in that order). Default 'medium'.
   371  * @return string HTML IMG element for given image attachment
   381  * @return string HTML IMG element for given image attachment.
   372  */
   382  */
   373 function get_image_tag( $id, $alt, $title, $align, $size = 'medium' ) {
   383 function get_image_tag( $id, $alt, $title, $align, $size = 'medium' ) {
   374 
   384 
   375 	list( $img_src, $width, $height ) = image_downsize( $id, $size );
   385 	list( $img_src, $width, $height ) = image_downsize( $id, $size );
   376 	$hwstring                         = image_hwstring( $width, $height );
   386 	$hwstring                         = image_hwstring( $width, $height );
   507  * Retrieves calculated resize dimensions for use in WP_Image_Editor.
   517  * Retrieves calculated resize dimensions for use in WP_Image_Editor.
   508  *
   518  *
   509  * Calculates dimensions and coordinates for a resized image that fits
   519  * Calculates dimensions and coordinates for a resized image that fits
   510  * within a specified width and height.
   520  * within a specified width and height.
   511  *
   521  *
   512  * Cropping behavior is dependent on the value of $crop:
       
   513  * 1. If false (default), images will not be cropped.
       
   514  * 2. If an array in the form of array( x_crop_position, y_crop_position ):
       
   515  *    - x_crop_position accepts 'left' 'center', or 'right'.
       
   516  *    - y_crop_position accepts 'top', 'center', or 'bottom'.
       
   517  *    Images will be cropped to the specified dimensions within the defined crop area.
       
   518  * 3. If true, images will be cropped to the specified dimensions using center positions.
       
   519  *
       
   520  * @since 2.5.0
   522  * @since 2.5.0
   521  *
   523  *
   522  * @param int        $orig_w Original width in pixels.
   524  * @param int        $orig_w Original width in pixels.
   523  * @param int        $orig_h Original height in pixels.
   525  * @param int        $orig_h Original height in pixels.
   524  * @param int        $dest_w New width in pixels.
   526  * @param int        $dest_w New width in pixels.
   525  * @param int        $dest_h New height in pixels.
   527  * @param int        $dest_h New height in pixels.
   526  * @param bool|array $crop   Optional. Whether to crop image to specified width and height or resize.
   528  * @param bool|array $crop   {
   527  *                           An array can specify positioning of the crop area. Default false.
   529  *     Optional. Image cropping behavior. If false, the image will be scaled (default).
       
   530  *     If true, image will be cropped to the specified dimensions using center positions.
       
   531  *     If an array, the image will be cropped using the array to specify the crop location:
       
   532  *
       
   533  *     @type string $0 The x crop position. Accepts 'left' 'center', or 'right'.
       
   534  *     @type string $1 The y crop position. Accepts 'top', 'center', or 'bottom'.
       
   535  * }
   528  * @return array|false Returned array matches parameters for `imagecopyresampled()`. False on failure.
   536  * @return array|false Returned array matches parameters for `imagecopyresampled()`. False on failure.
   529  */
   537  */
   530 function image_resize_dimensions( $orig_w, $orig_h, $dest_w, $dest_h, $crop = false ) {
   538 function image_resize_dimensions( $orig_w, $orig_h, $dest_w, $dest_h, $crop = false ) {
   531 
   539 
   532 	if ( $orig_w <= 0 || $orig_h <= 0 ) {
   540 	if ( $orig_w <= 0 || $orig_h <= 0 ) {
   650 		if ( ! $proceed ) {
   658 		if ( ! $proceed ) {
   651 			return false;
   659 			return false;
   652 		}
   660 		}
   653 	}
   661 	}
   654 
   662 
   655 	// The return array matches the parameters to imagecopyresampled().
   663 	/*
   656 	// int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h
   664 	 * The return array matches the parameters to imagecopyresampled().
       
   665 	 * int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h
       
   666 	 */
   657 	return array( 0, 0, (int) $s_x, (int) $s_y, (int) $new_w, (int) $new_h, (int) $crop_w, (int) $crop_h );
   667 	return array( 0, 0, (int) $s_x, (int) $s_y, (int) $new_w, (int) $new_h, (int) $crop_w, (int) $crop_h );
   658 }
   668 }
   659 
   669 
   660 /**
   670 /**
   661  * Resizes an image to make a thumbnail or intermediate size.
   671  * Resizes an image to make a thumbnail or intermediate size.
   664  * {@see 'image_make_intermediate_size'} filter can be used to hook in and change the
   674  * {@see 'image_make_intermediate_size'} filter can be used to hook in and change the
   665  * values of the returned array. The only parameter is the resized file path.
   675  * values of the returned array. The only parameter is the resized file path.
   666  *
   676  *
   667  * @since 2.5.0
   677  * @since 2.5.0
   668  *
   678  *
   669  * @param string $file   File path.
   679  * @param string     $file   File path.
   670  * @param int    $width  Image width.
   680  * @param int        $width  Image width.
   671  * @param int    $height Image height.
   681  * @param int        $height Image height.
   672  * @param bool   $crop   Optional. Whether to crop image to specified width and height or resize.
   682  * @param bool|array $crop   {
   673  *                       Default false.
   683  *     Optional. Image cropping behavior. If false, the image will be scaled (default).
       
   684  *     If true, image will be cropped to the specified dimensions using center positions.
       
   685  *     If an array, the image will be cropped using the array to specify the crop location:
       
   686  *
       
   687  *     @type string $0 The x crop position. Accepts 'left' 'center', or 'right'.
       
   688  *     @type string $1 The y crop position. Accepts 'top', 'center', or 'bottom'.
       
   689  * }
   674  * @return array|false Metadata array on success. False if no image was created.
   690  * @return array|false Metadata array on success. False if no image was created.
   675  */
   691  */
   676 function image_make_intermediate_size( $file, $width, $height, $crop = false ) {
   692 function image_make_intermediate_size( $file, $width, $height, $crop = false ) {
   677 	if ( $width || $height ) {
   693 	if ( $width || $height ) {
   678 		$editor = wp_get_image_editor( $file );
   694 		$editor = wp_get_image_editor( $file );
   747  *                              of width and height values in pixels (in that order). Default 'thumbnail'.
   763  *                              of width and height values in pixels (in that order). Default 'thumbnail'.
   748  * @return array|false {
   764  * @return array|false {
   749  *     Array of file relative path, width, and height on success. Additionally includes absolute
   765  *     Array of file relative path, width, and height on success. Additionally includes absolute
   750  *     path and URL if registered size is passed to `$size` parameter. False on failure.
   766  *     path and URL if registered size is passed to `$size` parameter. False on failure.
   751  *
   767  *
   752  *     @type string $file   Path of image relative to uploads directory.
   768  *     @type string $file   Filename of image.
   753  *     @type int    $width  Width of image in pixels.
   769  *     @type int    $width  Width of image in pixels.
   754  *     @type int    $height Height of image in pixels.
   770  *     @type int    $height Height of image in pixels.
   755  *     @type string $path   Absolute filesystem path of image.
   771  *     @type string $path   Path of image relative to uploads directory.
   756  *     @type string $url    URL of image.
   772  *     @type string $url    URL of image.
   757  * }
   773  * }
   758  */
   774  */
   759 function image_get_intermediate_size( $post_id, $size = 'thumbnail' ) {
   775 function image_get_intermediate_size( $post_id, $size = 'thumbnail' ) {
   760 	$imagedata = wp_get_attachment_metadata( $post_id );
   776 	$imagedata = wp_get_attachment_metadata( $post_id );
   954 	$image = image_downsize( $attachment_id, $size );
   970 	$image = image_downsize( $attachment_id, $size );
   955 	if ( ! $image ) {
   971 	if ( ! $image ) {
   956 		$src = false;
   972 		$src = false;
   957 
   973 
   958 		if ( $icon ) {
   974 		if ( $icon ) {
   959 			$src = wp_mime_type_icon( $attachment_id );
   975 			$src = wp_mime_type_icon( $attachment_id, '.svg' );
   960 
   976 
   961 			if ( $src ) {
   977 			if ( $src ) {
   962 				/** This filter is documented in wp-includes/post.php */
   978 				/** This filter is documented in wp-includes/post.php */
   963 				$icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' );
   979 				$icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' );
   964 
   980 
   965 				$src_file               = $icon_dir . '/' . wp_basename( $src );
   981 				$src_file = $icon_dir . '/' . wp_basename( $src );
       
   982 
   966 				list( $width, $height ) = wp_getimagesize( $src_file );
   983 				list( $width, $height ) = wp_getimagesize( $src_file );
       
   984 
       
   985 				$ext = strtolower( substr( $src_file, -4 ) );
       
   986 
       
   987 				if ( '.svg' === $ext ) {
       
   988 					// SVG does not have true dimensions, so this assigns width and height directly.
       
   989 					$width  = 48;
       
   990 					$height = 64;
       
   991 				} else {
       
   992 					list( $width, $height ) = wp_getimagesize( $src_file );
       
   993 				}
   967 			}
   994 			}
   968 		}
   995 		}
   969 
   996 
   970 		if ( $src && $width && $height ) {
   997 		if ( $src && $width && $height ) {
   971 			$image = array( $src, $width, $height, false );
   998 			$image = array( $src, $width, $height, false );
   991 	 */
  1018 	 */
   992 	return apply_filters( 'wp_get_attachment_image_src', $image, $attachment_id, $size, $icon );
  1019 	return apply_filters( 'wp_get_attachment_image_src', $image, $attachment_id, $size, $icon );
   993 }
  1020 }
   994 
  1021 
   995 /**
  1022 /**
   996  * Get an HTML img element representing an image attachment.
  1023  * Gets an HTML img element representing an image attachment.
   997  *
  1024  *
   998  * While `$size` will accept an array, it is better to register a size with
  1025  * While `$size` will accept an array, it is better to register a size with
   999  * add_image_size() so that a cropped version is generated. It's much more
  1026  * add_image_size() so that a cropped version is generated. It's much more
  1000  * efficient than having to find the closest-sized image and then having the
  1027  * efficient than having to find the closest-sized image and then having the
  1001  * browser scale down the image.
  1028  * browser scale down the image.
  1002  *
  1029  *
  1003  * @since 2.5.0
  1030  * @since 2.5.0
  1004  * @since 4.4.0 The `$srcset` and `$sizes` attributes were added.
  1031  * @since 4.4.0 The `$srcset` and `$sizes` attributes were added.
  1005  * @since 5.5.0 The `$loading` attribute was added.
  1032  * @since 5.5.0 The `$loading` attribute was added.
       
  1033  * @since 6.1.0 The `$decoding` attribute was added.
  1006  *
  1034  *
  1007  * @param int          $attachment_id Image attachment ID.
  1035  * @param int          $attachment_id Image attachment ID.
  1008  * @param string|int[] $size          Optional. Image size. Accepts any registered image size name, or an array
  1036  * @param string|int[] $size          Optional. Image size. Accepts any registered image size name, or an array
  1009  *                                    of width and height values in pixels (in that order). Default 'thumbnail'.
  1037  *                                    of width and height values in pixels (in that order). Default 'thumbnail'.
  1010  * @param bool         $icon          Optional. Whether the image should be treated as an icon. Default false.
  1038  * @param bool         $icon          Optional. Whether the image should be treated as an icon. Default false.
  1011  * @param string|array $attr {
  1039  * @param string|array $attr {
  1012  *     Optional. Attributes for the image markup.
  1040  *     Optional. Attributes for the image markup.
  1013  *
  1041  *
  1014  *     @type string       $src     Image attachment URL.
  1042  *     @type string       $src           Image attachment URL.
  1015  *     @type string       $class   CSS class name or space-separated list of classes.
  1043  *     @type string       $class         CSS class name or space-separated list of classes.
  1016  *                                 Default `attachment-$size_class size-$size_class`,
  1044  *                                       Default `attachment-$size_class size-$size_class`,
  1017  *                                 where `$size_class` is the image size being requested.
  1045  *                                       where `$size_class` is the image size being requested.
  1018  *     @type string       $alt     Image description for the alt attribute.
  1046  *     @type string       $alt           Image description for the alt attribute.
  1019  *     @type string       $srcset  The 'srcset' attribute value.
  1047  *     @type string       $srcset        The 'srcset' attribute value.
  1020  *     @type string       $sizes   The 'sizes' attribute value.
  1048  *     @type string       $sizes         The 'sizes' attribute value.
  1021  *     @type string|false $loading The 'loading' attribute value. Passing a value of false
  1049  *     @type string|false $loading       The 'loading' attribute value. Passing a value of false
  1022  *                                 will result in the attribute being omitted for the image.
  1050  *                                       will result in the attribute being omitted for the image.
  1023  *                                 Defaults to 'lazy', depending on wp_lazy_loading_enabled().
  1051  *                                       Default determined by {@see wp_get_loading_optimization_attributes()}.
       
  1052  *     @type string       $decoding      The 'decoding' attribute value. Possible values are
       
  1053  *                                       'async' (default), 'sync', or 'auto'. Passing false or an empty
       
  1054  *                                       string will result in the attribute being omitted.
       
  1055  *     @type string       $fetchpriority The 'fetchpriority' attribute value, whether `high`, `low`, or `auto`.
       
  1056  *                                       Default determined by {@see wp_get_loading_optimization_attributes()}.
  1024  * }
  1057  * }
  1025  * @return string HTML img element or empty string on failure.
  1058  * @return string HTML img element or empty string on failure.
  1026  */
  1059  */
  1027 function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = false, $attr = '' ) {
  1060 function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = false, $attr = '' ) {
  1028 	$html  = '';
  1061 	$html  = '';
  1043 			'src'   => $src,
  1076 			'src'   => $src,
  1044 			'class' => "attachment-$size_class size-$size_class",
  1077 			'class' => "attachment-$size_class size-$size_class",
  1045 			'alt'   => trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ),
  1078 			'alt'   => trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ),
  1046 		);
  1079 		);
  1047 
  1080 
  1048 		// Add `loading` attribute.
  1081 		/**
  1049 		if ( wp_lazy_loading_enabled( 'img', 'wp_get_attachment_image' ) ) {
  1082 		 * Filters the context in which wp_get_attachment_image() is used.
  1050 			$default_attr['loading'] = wp_get_loading_attr_default( 'wp_get_attachment_image' );
  1083 		 *
  1051 		}
  1084 		 * @since 6.3.0
  1052 
  1085 		 *
  1053 		$attr = wp_parse_args( $attr, $default_attr );
  1086 		 * @param string $context The context. Default 'wp_get_attachment_image'.
  1054 
  1087 		 */
  1055 		// If the default value of `lazy` for the `loading` attribute is overridden
  1088 		$context = apply_filters( 'wp_get_attachment_image_context', 'wp_get_attachment_image' );
  1056 		// to omit the attribute for this image, ensure it is not included.
  1089 		$attr    = wp_parse_args( $attr, $default_attr );
  1057 		if ( array_key_exists( 'loading', $attr ) && ! $attr['loading'] ) {
  1090 
       
  1091 		$loading_attr              = $attr;
       
  1092 		$loading_attr['width']     = $width;
       
  1093 		$loading_attr['height']    = $height;
       
  1094 		$loading_optimization_attr = wp_get_loading_optimization_attributes(
       
  1095 			'img',
       
  1096 			$loading_attr,
       
  1097 			$context
       
  1098 		);
       
  1099 
       
  1100 		// Add loading optimization attributes if not available.
       
  1101 		$attr = array_merge( $attr, $loading_optimization_attr );
       
  1102 
       
  1103 		// Omit the `decoding` attribute if the value is invalid according to the spec.
       
  1104 		if ( empty( $attr['decoding'] ) || ! in_array( $attr['decoding'], array( 'async', 'sync', 'auto' ), true ) ) {
       
  1105 			unset( $attr['decoding'] );
       
  1106 		}
       
  1107 
       
  1108 		/*
       
  1109 		 * If the default value of `lazy` for the `loading` attribute is overridden
       
  1110 		 * to omit the attribute for this image, ensure it is not included.
       
  1111 		 */
       
  1112 		if ( isset( $attr['loading'] ) && ! $attr['loading'] ) {
  1058 			unset( $attr['loading'] );
  1113 			unset( $attr['loading'] );
       
  1114 		}
       
  1115 
       
  1116 		// If the `fetchpriority` attribute is overridden and set to false or an empty string.
       
  1117 		if ( isset( $attr['fetchpriority'] ) && ! $attr['fetchpriority'] ) {
       
  1118 			unset( $attr['fetchpriority'] );
  1059 		}
  1119 		}
  1060 
  1120 
  1061 		// Generate 'srcset' and 'sizes' if not already present.
  1121 		// Generate 'srcset' and 'sizes' if not already present.
  1062 		if ( empty( $attr['srcset'] ) ) {
  1122 		if ( empty( $attr['srcset'] ) ) {
  1063 			$image_meta = wp_get_attachment_metadata( $attachment_id );
  1123 			$image_meta = wp_get_attachment_metadata( $attachment_id );
  1099 
  1159 
  1100 		$html .= ' />';
  1160 		$html .= ' />';
  1101 	}
  1161 	}
  1102 
  1162 
  1103 	/**
  1163 	/**
  1104 	 * HTML img element representing an image attachment.
  1164 	 * Filters the HTML img element representing an image attachment.
  1105 	 *
  1165 	 *
  1106 	 * @since 5.6.0
  1166 	 * @since 5.6.0
  1107 	 *
  1167 	 *
  1108 	 * @param string       $html          HTML img element or empty string on failure.
  1168 	 * @param string       $html          HTML img element or empty string on failure.
  1109 	 * @param int          $attachment_id Image attachment ID.
  1169 	 * @param int          $attachment_id Image attachment ID.
  1115 	 */
  1175 	 */
  1116 	return apply_filters( 'wp_get_attachment_image', $html, $attachment_id, $size, $icon, $attr );
  1176 	return apply_filters( 'wp_get_attachment_image', $html, $attachment_id, $size, $icon, $attr );
  1117 }
  1177 }
  1118 
  1178 
  1119 /**
  1179 /**
  1120  * Get the URL of an image attachment.
  1180  * Gets the URL of an image attachment.
  1121  *
  1181  *
  1122  * @since 4.4.0
  1182  * @since 4.4.0
  1123  *
  1183  *
  1124  * @param int          $attachment_id Image attachment ID.
  1184  * @param int          $attachment_id Image attachment ID.
  1125  * @param string|int[] $size          Optional. Image size. Accepts any registered image size name, or an array of
  1185  * @param string|int[] $size          Optional. Image size. Accepts any registered image size name, or an array of
  1132 	$image = wp_get_attachment_image_src( $attachment_id, $size, $icon );
  1192 	$image = wp_get_attachment_image_src( $attachment_id, $size, $icon );
  1133 	return isset( $image[0] ) ? $image[0] : false;
  1193 	return isset( $image[0] ) ? $image[0] : false;
  1134 }
  1194 }
  1135 
  1195 
  1136 /**
  1196 /**
  1137  * Get the attachment path relative to the upload directory.
  1197  * Gets the attachment path relative to the upload directory.
  1138  *
  1198  *
  1139  * @since 4.4.1
  1199  * @since 4.4.1
  1140  * @access private
  1200  * @access private
  1141  *
  1201  *
  1142  * @param string $file Attachment file name.
  1202  * @param string $file Attachment file name.
  1147 
  1207 
  1148 	if ( '.' === $dirname ) {
  1208 	if ( '.' === $dirname ) {
  1149 		return '';
  1209 		return '';
  1150 	}
  1210 	}
  1151 
  1211 
  1152 	if ( false !== strpos( $dirname, 'wp-content/uploads' ) ) {
  1212 	if ( str_contains( $dirname, 'wp-content/uploads' ) ) {
  1153 		// Get the directory name relative to the upload directory (back compat for pre-2.7 uploads).
  1213 		// Get the directory name relative to the upload directory (back compat for pre-2.7 uploads).
  1154 		$dirname = substr( $dirname, strpos( $dirname, 'wp-content/uploads' ) + 18 );
  1214 		$dirname = substr( $dirname, strpos( $dirname, 'wp-content/uploads' ) + 18 );
  1155 		$dirname = ltrim( $dirname, '/' );
  1215 		$dirname = ltrim( $dirname, '/' );
  1156 	}
  1216 	}
  1157 
  1217 
  1158 	return $dirname;
  1218 	return $dirname;
  1159 }
  1219 }
  1160 
  1220 
  1161 /**
  1221 /**
  1162  * Get the image size as array from its meta data.
  1222  * Gets the image size as array from its meta data.
  1163  *
  1223  *
  1164  * Used for responsive images.
  1224  * Used for responsive images.
  1165  *
  1225  *
  1166  * @since 4.4.0
  1226  * @since 4.4.0
  1167  * @access private
  1227  * @access private
  1199  * @see wp_calculate_image_srcset()
  1259  * @see wp_calculate_image_srcset()
  1200  *
  1260  *
  1201  * @param int          $attachment_id Image attachment ID.
  1261  * @param int          $attachment_id Image attachment ID.
  1202  * @param string|int[] $size          Optional. Image size. Accepts any registered image size name, or an array of
  1262  * @param string|int[] $size          Optional. Image size. Accepts any registered image size name, or an array of
  1203  *                                    width and height values in pixels (in that order). Default 'medium'.
  1263  *                                    width and height values in pixels (in that order). Default 'medium'.
  1204  * @param array        $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
  1264  * @param array|null   $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
  1205  *                                    Default null.
  1265  *                                    Default null.
  1206  * @return string|false A 'srcset' value string or false.
  1266  * @return string|false A 'srcset' value string or false.
  1207  */
  1267  */
  1208 function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $image_meta = null ) {
  1268 function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $image_meta = null ) {
  1209 	$image = wp_get_attachment_image_src( $attachment_id, $size );
  1269 	$image = wp_get_attachment_image_src( $attachment_id, $size );
  1241  * @param int    $attachment_id Optional. The image attachment ID. Default 0.
  1301  * @param int    $attachment_id Optional. The image attachment ID. Default 0.
  1242  * @return string|false The 'srcset' attribute value. False on error or when only one source exists.
  1302  * @return string|false The 'srcset' attribute value. False on error or when only one source exists.
  1243  */
  1303  */
  1244 function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id = 0 ) {
  1304 function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id = 0 ) {
  1245 	/**
  1305 	/**
  1246 	 * Let plugins pre-filter the image meta to be able to fix inconsistencies in the stored data.
  1306 	 * Pre-filters the image meta to be able to fix inconsistencies in the stored data.
  1247 	 *
  1307 	 *
  1248 	 * @since 4.5.0
  1308 	 * @since 4.5.0
  1249 	 *
  1309 	 *
  1250 	 * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
  1310 	 * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
  1251 	 * @param int[]  $size_array    {
  1311 	 * @param int[]  $size_array    {
  1285 		$image_sizes[] = array(
  1345 		$image_sizes[] = array(
  1286 			'width'  => $image_meta['width'],
  1346 			'width'  => $image_meta['width'],
  1287 			'height' => $image_meta['height'],
  1347 			'height' => $image_meta['height'],
  1288 			'file'   => $image_basename,
  1348 			'file'   => $image_basename,
  1289 		);
  1349 		);
  1290 	} elseif ( strpos( $image_src, $image_meta['file'] ) ) {
  1350 	} elseif ( str_contains( $image_src, $image_meta['file'] ) ) {
  1291 		return false;
  1351 		return false;
  1292 	}
  1352 	}
  1293 
  1353 
  1294 	// Retrieve the uploads sub-directory from the full size image.
  1354 	// Retrieve the uploads sub-directory from the full size image.
  1295 	$dirname = _wp_get_attachment_relative_path( $image_meta['file'] );
  1355 	$dirname = _wp_get_attachment_relative_path( $image_meta['file'] );
  1303 
  1363 
  1304 	/*
  1364 	/*
  1305 	 * If currently on HTTPS, prefer HTTPS URLs when we know they're supported by the domain
  1365 	 * If currently on HTTPS, prefer HTTPS URLs when we know they're supported by the domain
  1306 	 * (which is to say, when they share the domain name of the current request).
  1366 	 * (which is to say, when they share the domain name of the current request).
  1307 	 */
  1367 	 */
  1308 	if ( is_ssl() && 'https' !== substr( $image_baseurl, 0, 5 ) && parse_url( $image_baseurl, PHP_URL_HOST ) === $_SERVER['HTTP_HOST'] ) {
  1368 	if ( is_ssl() && ! str_starts_with( $image_baseurl, 'https' ) ) {
  1309 		$image_baseurl = set_url_scheme( $image_baseurl, 'https' );
  1369 		/*
       
  1370 		 * Since the `Host:` header might contain a port, it should
       
  1371 		 * be compared against the image URL using the same port.
       
  1372 		 */
       
  1373 		$parsed = parse_url( $image_baseurl );
       
  1374 		$domain = isset( $parsed['host'] ) ? $parsed['host'] : '';
       
  1375 
       
  1376 		if ( isset( $parsed['port'] ) ) {
       
  1377 			$domain .= ':' . $parsed['port'];
       
  1378 		}
       
  1379 
       
  1380 		if ( $_SERVER['HTTP_HOST'] === $domain ) {
       
  1381 			$image_baseurl = set_url_scheme( $image_baseurl, 'https' );
       
  1382 		}
  1310 	}
  1383 	}
  1311 
  1384 
  1312 	/*
  1385 	/*
  1313 	 * Images that have been edited in WordPress after being uploaded will
  1386 	 * Images that have been edited in WordPress after being uploaded will
  1314 	 * contain a unique hash. Look for that hash and use it later to filter
  1387 	 * contain a unique hash. Look for that hash and use it later to filter
  1352 		if ( ! is_array( $image ) ) {
  1425 		if ( ! is_array( $image ) ) {
  1353 			continue;
  1426 			continue;
  1354 		}
  1427 		}
  1355 
  1428 
  1356 		// If the file name is part of the `src`, we've confirmed a match.
  1429 		// If the file name is part of the `src`, we've confirmed a match.
  1357 		if ( ! $src_matched && false !== strpos( $image_src, $dirname . $image['file'] ) ) {
  1430 		if ( ! $src_matched && str_contains( $image_src, $dirname . $image['file'] ) ) {
  1358 			$src_matched = true;
  1431 			$src_matched = true;
  1359 			$is_src      = true;
  1432 			$is_src      = true;
  1360 		}
  1433 		}
  1361 
  1434 
  1362 		// Filter out images that are from previous edits.
  1435 		// Filter out images that are from previous edits.
  1440  * @see wp_calculate_image_sizes()
  1513  * @see wp_calculate_image_sizes()
  1441  *
  1514  *
  1442  * @param int          $attachment_id Image attachment ID.
  1515  * @param int          $attachment_id Image attachment ID.
  1443  * @param string|int[] $size          Optional. Image size. Accepts any registered image size name, or an array of
  1516  * @param string|int[] $size          Optional. Image size. Accepts any registered image size name, or an array of
  1444  *                                    width and height values in pixels (in that order). Default 'medium'.
  1517  *                                    width and height values in pixels (in that order). Default 'medium'.
  1445  * @param array        $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
  1518  * @param array|null   $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
  1446  *                                    Default null.
  1519  *                                    Default null.
  1447  * @return string|false A valid source size value for use in a 'sizes' attribute or false.
  1520  * @return string|false A valid source size value for use in a 'sizes' attribute or false.
  1448  */
  1521  */
  1449 function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $image_meta = null ) {
  1522 function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $image_meta = null ) {
  1450 	$image = wp_get_attachment_image_src( $attachment_id, $size );
  1523 	$image = wp_get_attachment_image_src( $attachment_id, $size );
  1471  *
  1544  *
  1472  * @since 4.4.0
  1545  * @since 4.4.0
  1473  *
  1546  *
  1474  * @param string|int[] $size          Image size. Accepts any registered image size name, or an array of
  1547  * @param string|int[] $size          Image size. Accepts any registered image size name, or an array of
  1475  *                                    width and height values in pixels (in that order).
  1548  *                                    width and height values in pixels (in that order).
  1476  * @param string       $image_src     Optional. The URL to the image file. Default null.
  1549  * @param string|null  $image_src     Optional. The URL to the image file. Default null.
  1477  * @param array        $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
  1550  * @param array|null   $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
  1478  *                                    Default null.
  1551  *                                    Default null.
  1479  * @param int          $attachment_id Optional. Image attachment ID. Either `$image_meta` or `$attachment_id`
  1552  * @param int          $attachment_id Optional. Image attachment ID. Either `$image_meta` or `$attachment_id`
  1480  *                                    is needed when using the image size name as argument for `$size`. Default 0.
  1553  *                                    is needed when using the image size name as argument for `$size`. Default 0.
  1481  * @return string|false A valid source size value for use in a 'sizes' attribute or false.
  1554  * @return string|false A valid source size value for use in a 'sizes' attribute or false.
  1482  */
  1555  */
  1538 function wp_image_file_matches_image_meta( $image_location, $image_meta, $attachment_id = 0 ) {
  1611 function wp_image_file_matches_image_meta( $image_location, $image_meta, $attachment_id = 0 ) {
  1539 	$match = false;
  1612 	$match = false;
  1540 
  1613 
  1541 	// Ensure the $image_meta is valid.
  1614 	// Ensure the $image_meta is valid.
  1542 	if ( isset( $image_meta['file'] ) && strlen( $image_meta['file'] ) > 4 ) {
  1615 	if ( isset( $image_meta['file'] ) && strlen( $image_meta['file'] ) > 4 ) {
  1543 		// Remove quiery args if image URI.
  1616 		// Remove query args in image URI.
  1544 		list( $image_location ) = explode( '?', $image_location );
  1617 		list( $image_location ) = explode( '?', $image_location );
  1545 
  1618 
  1546 		// Check if the relative image path from the image meta is at the end of $image_location.
  1619 		// Check if the relative image path from the image meta is at the end of $image_location.
  1547 		if ( strrpos( $image_location, $image_meta['file'] ) === strlen( $image_location ) - strlen( $image_meta['file'] ) ) {
  1620 		if ( strrpos( $image_location, $image_meta['file'] ) === strlen( $image_location ) - strlen( $image_meta['file'] ) ) {
  1548 			$match = true;
  1621 			$match = true;
  1604 	$dimensions = false;
  1677 	$dimensions = false;
  1605 
  1678 
  1606 	// Is it a full size image?
  1679 	// Is it a full size image?
  1607 	if (
  1680 	if (
  1608 		isset( $image_meta['file'] ) &&
  1681 		isset( $image_meta['file'] ) &&
  1609 		strpos( $image_src, wp_basename( $image_meta['file'] ) ) !== false
  1682 		str_contains( $image_src, wp_basename( $image_meta['file'] ) )
  1610 	) {
  1683 	) {
  1611 		$dimensions = array(
  1684 		$dimensions = array(
  1612 			(int) $image_meta['width'],
  1685 			(int) $image_meta['width'],
  1613 			(int) $image_meta['height'],
  1686 			(int) $image_meta['height'],
  1614 		);
  1687 		);
  1671 	if ( ! $image_src ) {
  1744 	if ( ! $image_src ) {
  1672 		return $image;
  1745 		return $image;
  1673 	}
  1746 	}
  1674 
  1747 
  1675 	// Bail early if an image has been inserted and later edited.
  1748 	// Bail early if an image has been inserted and later edited.
  1676 	if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) &&
  1749 	if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash )
  1677 		strpos( wp_basename( $image_src ), $img_edit_hash[0] ) === false ) {
  1750 		&& ! str_contains( wp_basename( $image_src ), $img_edit_hash[0] )
  1678 
  1751 	) {
  1679 		return $image;
  1752 		return $image;
  1680 	}
  1753 	}
  1681 
  1754 
  1682 	$width  = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : 0;
  1755 	$width  = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : 0;
  1683 	$height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;
  1756 	$height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;
  1727  * @param string $context  Additional context, like the current filter name
  1800  * @param string $context  Additional context, like the current filter name
  1728  *                         or the function name from where this was called.
  1801  *                         or the function name from where this was called.
  1729  * @return bool Whether to add the attribute.
  1802  * @return bool Whether to add the attribute.
  1730  */
  1803  */
  1731 function wp_lazy_loading_enabled( $tag_name, $context ) {
  1804 function wp_lazy_loading_enabled( $tag_name, $context ) {
  1732 	// By default add to all 'img' and 'iframe' tags.
  1805 	/*
  1733 	// See https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-loading
  1806 	 * By default add to all 'img' and 'iframe' tags.
  1734 	// See https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-loading
  1807 	 * See https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-loading
       
  1808 	 * See https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-loading
       
  1809 	 */
  1735 	$default = ( 'img' === $tag_name || 'iframe' === $tag_name );
  1810 	$default = ( 'img' === $tag_name || 'iframe' === $tag_name );
  1736 
  1811 
  1737 	/**
  1812 	/**
  1738 	 * Filters whether to add the `loading` attribute to the specified tag in the specified context.
  1813 	 * Filters whether to add the `loading` attribute to the specified tag in the specified context.
  1739 	 *
  1814 	 *
  1759  * @since 5.5.0
  1834  * @since 5.5.0
  1760  * @since 5.7.0 Now supports adding `loading` attributes to `iframe` tags.
  1835  * @since 5.7.0 Now supports adding `loading` attributes to `iframe` tags.
  1761  *
  1836  *
  1762  * @see wp_img_tag_add_width_and_height_attr()
  1837  * @see wp_img_tag_add_width_and_height_attr()
  1763  * @see wp_img_tag_add_srcset_and_sizes_attr()
  1838  * @see wp_img_tag_add_srcset_and_sizes_attr()
  1764  * @see wp_img_tag_add_loading_attr()
  1839  * @see wp_img_tag_add_loading_optimization_attrs()
  1765  * @see wp_iframe_tag_add_loading_attr()
  1840  * @see wp_iframe_tag_add_loading_attr()
  1766  *
  1841  *
  1767  * @param string $content The HTML content to be filtered.
  1842  * @param string $content The HTML content to be filtered.
  1768  * @param string $context Optional. Additional context to pass to the filters.
  1843  * @param string $context Optional. Additional context to pass to the filters.
  1769  *                        Defaults to `current_filter()` when not set.
  1844  *                        Defaults to `current_filter()` when not set.
  1772 function wp_filter_content_tags( $content, $context = null ) {
  1847 function wp_filter_content_tags( $content, $context = null ) {
  1773 	if ( null === $context ) {
  1848 	if ( null === $context ) {
  1774 		$context = current_filter();
  1849 		$context = current_filter();
  1775 	}
  1850 	}
  1776 
  1851 
  1777 	$add_img_loading_attr    = wp_lazy_loading_enabled( 'img', $context );
       
  1778 	$add_iframe_loading_attr = wp_lazy_loading_enabled( 'iframe', $context );
  1852 	$add_iframe_loading_attr = wp_lazy_loading_enabled( 'iframe', $context );
  1779 
  1853 
  1780 	if ( ! preg_match_all( '/<(img|iframe)\s[^>]+>/', $content, $matches, PREG_SET_ORDER ) ) {
  1854 	if ( ! preg_match_all( '/<(img|iframe)\s[^>]+>/', $content, $matches, PREG_SET_ORDER ) ) {
  1781 		return $content;
  1855 		return $content;
  1782 	}
  1856 	}
  1794 			case 'img':
  1868 			case 'img':
  1795 				if ( preg_match( '/wp-image-([0-9]+)/i', $tag, $class_id ) ) {
  1869 				if ( preg_match( '/wp-image-([0-9]+)/i', $tag, $class_id ) ) {
  1796 					$attachment_id = absint( $class_id[1] );
  1870 					$attachment_id = absint( $class_id[1] );
  1797 
  1871 
  1798 					if ( $attachment_id ) {
  1872 					if ( $attachment_id ) {
  1799 						// If exactly the same image tag is used more than once, overwrite it.
  1873 						/*
  1800 						// All identical tags will be replaced later with 'str_replace()'.
  1874 						 * If exactly the same image tag is used more than once, overwrite it.
       
  1875 						 * All identical tags will be replaced later with 'str_replace()'.
       
  1876 						 */
  1801 						$images[ $tag ] = $attachment_id;
  1877 						$images[ $tag ] = $attachment_id;
  1802 						break;
  1878 						break;
  1803 					}
  1879 					}
  1804 				}
  1880 				}
  1805 				$images[ $tag ] = 0;
  1881 				$images[ $tag ] = 0;
  1827 		if ( isset( $images[ $match[0] ] ) ) {
  1903 		if ( isset( $images[ $match[0] ] ) ) {
  1828 			$filtered_image = $match[0];
  1904 			$filtered_image = $match[0];
  1829 			$attachment_id  = $images[ $match[0] ];
  1905 			$attachment_id  = $images[ $match[0] ];
  1830 
  1906 
  1831 			// Add 'width' and 'height' attributes if applicable.
  1907 			// Add 'width' and 'height' attributes if applicable.
  1832 			if ( $attachment_id > 0 && false === strpos( $filtered_image, ' width=' ) && false === strpos( $filtered_image, ' height=' ) ) {
  1908 			if ( $attachment_id > 0 && ! str_contains( $filtered_image, ' width=' ) && ! str_contains( $filtered_image, ' height=' ) ) {
  1833 				$filtered_image = wp_img_tag_add_width_and_height_attr( $filtered_image, $context, $attachment_id );
  1909 				$filtered_image = wp_img_tag_add_width_and_height_attr( $filtered_image, $context, $attachment_id );
  1834 			}
  1910 			}
  1835 
  1911 
  1836 			// Add 'srcset' and 'sizes' attributes if applicable.
  1912 			// Add 'srcset' and 'sizes' attributes if applicable.
  1837 			if ( $attachment_id > 0 && false === strpos( $filtered_image, ' srcset=' ) ) {
  1913 			if ( $attachment_id > 0 && ! str_contains( $filtered_image, ' srcset=' ) ) {
  1838 				$filtered_image = wp_img_tag_add_srcset_and_sizes_attr( $filtered_image, $context, $attachment_id );
  1914 				$filtered_image = wp_img_tag_add_srcset_and_sizes_attr( $filtered_image, $context, $attachment_id );
  1839 			}
  1915 			}
  1840 
  1916 
  1841 			// Add 'loading' attribute if applicable.
  1917 			// Add loading optimization attributes if applicable.
  1842 			if ( $add_img_loading_attr && false === strpos( $filtered_image, ' loading=' ) ) {
  1918 			$filtered_image = wp_img_tag_add_loading_optimization_attrs( $filtered_image, $context );
  1843 				$filtered_image = wp_img_tag_add_loading_attr( $filtered_image, $context );
       
  1844 			}
       
  1845 
  1919 
  1846 			/**
  1920 			/**
  1847 			 * Filters an img tag within the content for a given context.
  1921 			 * Filters an img tag within the content for a given context.
  1848 			 *
  1922 			 *
  1849 			 * @since 6.0.0
  1923 			 * @since 6.0.0
  1868 		// Filter an iframe match.
  1942 		// Filter an iframe match.
  1869 		if ( isset( $iframes[ $match[0] ] ) ) {
  1943 		if ( isset( $iframes[ $match[0] ] ) ) {
  1870 			$filtered_iframe = $match[0];
  1944 			$filtered_iframe = $match[0];
  1871 
  1945 
  1872 			// Add 'loading' attribute if applicable.
  1946 			// Add 'loading' attribute if applicable.
  1873 			if ( $add_iframe_loading_attr && false === strpos( $filtered_iframe, ' loading=' ) ) {
  1947 			if ( $add_iframe_loading_attr && ! str_contains( $filtered_iframe, ' loading=' ) ) {
  1874 				$filtered_iframe = wp_iframe_tag_add_loading_attr( $filtered_iframe, $context );
  1948 				$filtered_iframe = wp_iframe_tag_add_loading_attr( $filtered_iframe, $context );
  1875 			}
  1949 			}
  1876 
  1950 
  1877 			if ( $filtered_iframe !== $match[0] ) {
  1951 			if ( $filtered_iframe !== $match[0] ) {
  1878 				$content = str_replace( $match[0], $filtered_iframe, $content );
  1952 				$content = str_replace( $match[0], $filtered_iframe, $content );
  1888 
  1962 
  1889 	return $content;
  1963 	return $content;
  1890 }
  1964 }
  1891 
  1965 
  1892 /**
  1966 /**
  1893  * Adds `loading` attribute to an `img` HTML tag.
  1967  * Adds optimization attributes to an `img` HTML tag.
  1894  *
  1968  *
  1895  * @since 5.5.0
  1969  * @since 6.3.0
  1896  *
  1970  *
  1897  * @param string $image   The HTML `img` tag where the attribute should be added.
  1971  * @param string $image   The HTML `img` tag where the attribute should be added.
  1898  * @param string $context Additional context to pass to the filters.
  1972  * @param string $context Additional context to pass to the filters.
  1899  * @return string Converted `img` tag with `loading` attribute added.
  1973  * @return string Converted `img` tag with optimization attributes added.
  1900  */
  1974  */
  1901 function wp_img_tag_add_loading_attr( $image, $context ) {
  1975 function wp_img_tag_add_loading_optimization_attrs( $image, $context ) {
  1902 	// Get loading attribute value to use. This must occur before the conditional check below so that even images that
  1976 	$width             = preg_match( '/ width=["\']([0-9]+)["\']/', $image, $match_width ) ? (int) $match_width[1] : null;
  1903 	// are ineligible for being lazy-loaded are considered.
  1977 	$height            = preg_match( '/ height=["\']([0-9]+)["\']/', $image, $match_height ) ? (int) $match_height[1] : null;
  1904 	$value = wp_get_loading_attr_default( $context );
  1978 	$loading_val       = preg_match( '/ loading=["\']([A-Za-z]+)["\']/', $image, $match_loading ) ? $match_loading[1] : null;
  1905 
  1979 	$fetchpriority_val = preg_match( '/ fetchpriority=["\']([A-Za-z]+)["\']/', $image, $match_fetchpriority ) ? $match_fetchpriority[1] : null;
  1906 	// Images should have source and dimension attributes for the `loading` attribute to be added.
  1980 	$decoding_val      = preg_match( '/ decoding=["\']([A-Za-z]+)["\']/', $image, $match_decoding ) ? $match_decoding[1] : null;
  1907 	if ( false === strpos( $image, ' src="' ) || false === strpos( $image, ' width="' ) || false === strpos( $image, ' height="' ) ) {
  1981 
       
  1982 	/*
       
  1983 	 * Get loading optimization attributes to use.
       
  1984 	 * This must occur before the conditional check below so that even images
       
  1985 	 * that are ineligible for being lazy-loaded are considered.
       
  1986 	 */
       
  1987 	$optimization_attrs = wp_get_loading_optimization_attributes(
       
  1988 		'img',
       
  1989 		array(
       
  1990 			'width'         => $width,
       
  1991 			'height'        => $height,
       
  1992 			'loading'       => $loading_val,
       
  1993 			'fetchpriority' => $fetchpriority_val,
       
  1994 			'decoding'      => $decoding_val,
       
  1995 		),
       
  1996 		$context
       
  1997 	);
       
  1998 
       
  1999 	// Images should have source for the loading optimization attributes to be added.
       
  2000 	if ( ! str_contains( $image, ' src="' ) ) {
  1908 		return $image;
  2001 		return $image;
  1909 	}
  2002 	}
  1910 
  2003 
  1911 	/**
  2004 	if ( empty( $decoding_val ) ) {
  1912 	 * Filters the `loading` attribute value to add to an image. Default `lazy`.
  2005 		/**
  1913 	 *
  2006 		 * Filters the `decoding` attribute value to add to an image. Default `async`.
  1914 	 * Returning `false` or an empty string will not add the attribute.
  2007 		 *
  1915 	 * Returning `true` will add the default value.
  2008 		 * Returning a falsey value will omit the attribute.
  1916 	 *
  2009 		 *
  1917 	 * @since 5.5.0
  2010 		 * @since 6.1.0
  1918 	 *
  2011 		 *
  1919 	 * @param string|bool $value   The `loading` attribute value. Returning a falsey value will result in
  2012 		 * @param string|false|null $value      The `decoding` attribute value. Returning a falsey value
  1920 	 *                             the attribute being omitted for the image.
  2013 		 *                                      will result in the attribute being omitted for the image.
  1921 	 * @param string      $image   The HTML `img` tag to be filtered.
  2014 		 *                                      Otherwise, it may be: 'async', 'sync', or 'auto'. Defaults to false.
  1922 	 * @param string      $context Additional context about how the function was called or where the img tag is.
  2015 		 * @param string            $image      The HTML `img` tag to be filtered.
  1923 	 */
  2016 		 * @param string            $context    Additional context about how the function was called
  1924 	$value = apply_filters( 'wp_img_tag_add_loading_attr', $value, $image, $context );
  2017 		 *                                      or where the img tag is.
  1925 
  2018 		 */
  1926 	if ( $value ) {
  2019 		$filtered_decoding_attr = apply_filters(
  1927 		if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
  2020 			'wp_img_tag_add_decoding_attr',
  1928 			$value = 'lazy';
  2021 			isset( $optimization_attrs['decoding'] ) ? $optimization_attrs['decoding'] : false,
  1929 		}
  2022 			$image,
  1930 
  2023 			$context
  1931 		return str_replace( '<img', '<img loading="' . esc_attr( $value ) . '"', $image );
  2024 		);
       
  2025 
       
  2026 		// Validate the values after filtering.
       
  2027 		if ( isset( $optimization_attrs['decoding'] ) && ! $filtered_decoding_attr ) {
       
  2028 			// Unset `decoding` attribute if `$filtered_decoding_attr` is set to `false`.
       
  2029 			unset( $optimization_attrs['decoding'] );
       
  2030 		} elseif ( in_array( $filtered_decoding_attr, array( 'async', 'sync', 'auto' ), true ) ) {
       
  2031 			$optimization_attrs['decoding'] = $filtered_decoding_attr;
       
  2032 		}
       
  2033 
       
  2034 		if ( ! empty( $optimization_attrs['decoding'] ) ) {
       
  2035 			$image = str_replace( '<img', '<img decoding="' . esc_attr( $optimization_attrs['decoding'] ) . '"', $image );
       
  2036 		}
       
  2037 	}
       
  2038 
       
  2039 	// Images should have dimension attributes for the 'loading' and 'fetchpriority' attributes to be added.
       
  2040 	if ( ! str_contains( $image, ' width="' ) || ! str_contains( $image, ' height="' ) ) {
       
  2041 		return $image;
       
  2042 	}
       
  2043 
       
  2044 	// Retained for backward compatibility.
       
  2045 	$loading_attrs_enabled = wp_lazy_loading_enabled( 'img', $context );
       
  2046 
       
  2047 	if ( empty( $loading_val ) && $loading_attrs_enabled ) {
       
  2048 		/**
       
  2049 		 * Filters the `loading` attribute value to add to an image. Default `lazy`.
       
  2050 		 *
       
  2051 		 * Returning `false` or an empty string will not add the attribute.
       
  2052 		 * Returning `true` will add the default value.
       
  2053 		 *
       
  2054 		 * @since 5.5.0
       
  2055 		 *
       
  2056 		 * @param string|bool $value   The `loading` attribute value. Returning a falsey value will result in
       
  2057 		 *                             the attribute being omitted for the image.
       
  2058 		 * @param string      $image   The HTML `img` tag to be filtered.
       
  2059 		 * @param string      $context Additional context about how the function was called or where the img tag is.
       
  2060 		 */
       
  2061 		$filtered_loading_attr = apply_filters(
       
  2062 			'wp_img_tag_add_loading_attr',
       
  2063 			isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false,
       
  2064 			$image,
       
  2065 			$context
       
  2066 		);
       
  2067 
       
  2068 		// Validate the values after filtering.
       
  2069 		if ( isset( $optimization_attrs['loading'] ) && ! $filtered_loading_attr ) {
       
  2070 			// Unset `loading` attributes if `$filtered_loading_attr` is set to `false`.
       
  2071 			unset( $optimization_attrs['loading'] );
       
  2072 		} elseif ( in_array( $filtered_loading_attr, array( 'lazy', 'eager' ), true ) ) {
       
  2073 			/*
       
  2074 			 * If the filter changed the loading attribute to "lazy" when a fetchpriority attribute
       
  2075 			 * with value "high" is already present, trigger a warning since those two attribute
       
  2076 			 * values should be mutually exclusive.
       
  2077 			 *
       
  2078 			 * The same warning is present in `wp_get_loading_optimization_attributes()`, and here it
       
  2079 			 * is only intended for the specific scenario where the above filtered caused the problem.
       
  2080 			 */
       
  2081 			if ( isset( $optimization_attrs['fetchpriority'] ) && 'high' === $optimization_attrs['fetchpriority'] &&
       
  2082 				( isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false ) !== $filtered_loading_attr &&
       
  2083 				'lazy' === $filtered_loading_attr
       
  2084 			) {
       
  2085 				_doing_it_wrong(
       
  2086 					__FUNCTION__,
       
  2087 					__( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
       
  2088 					'6.3.0'
       
  2089 				);
       
  2090 			}
       
  2091 
       
  2092 			// The filtered value will still be respected.
       
  2093 			$optimization_attrs['loading'] = $filtered_loading_attr;
       
  2094 		}
       
  2095 
       
  2096 		if ( ! empty( $optimization_attrs['loading'] ) ) {
       
  2097 			$image = str_replace( '<img', '<img loading="' . esc_attr( $optimization_attrs['loading'] ) . '"', $image );
       
  2098 		}
       
  2099 	}
       
  2100 
       
  2101 	if ( empty( $fetchpriority_val ) && ! empty( $optimization_attrs['fetchpriority'] ) ) {
       
  2102 		$image = str_replace( '<img', '<img fetchpriority="' . esc_attr( $optimization_attrs['fetchpriority'] ) . '"', $image );
  1932 	}
  2103 	}
  1933 
  2104 
  1934 	return $image;
  2105 	return $image;
  1935 }
  2106 }
  1936 
  2107 
  1969 
  2140 
  1970 	if ( true === $add ) {
  2141 	if ( true === $add ) {
  1971 		$image_meta = wp_get_attachment_metadata( $attachment_id );
  2142 		$image_meta = wp_get_attachment_metadata( $attachment_id );
  1972 		$size_array = wp_image_src_get_dimensions( $image_src, $image_meta, $attachment_id );
  2143 		$size_array = wp_image_src_get_dimensions( $image_src, $image_meta, $attachment_id );
  1973 
  2144 
  1974 		if ( $size_array ) {
  2145 		if ( $size_array && $size_array[0] && $size_array[1] ) {
       
  2146 			// If the width is enforced through style (e.g. in an inline image), calculate the dimension attributes.
       
  2147 			$style_width = preg_match( '/style="width:\s*(\d+)px;"/', $image, $match_width ) ? (int) $match_width[1] : 0;
       
  2148 			if ( $style_width ) {
       
  2149 				$size_array[1] = (int) round( $size_array[1] * $style_width / $size_array[0] );
       
  2150 				$size_array[0] = $style_width;
       
  2151 			}
       
  2152 
  1975 			$hw = trim( image_hwstring( $size_array[0], $size_array[1] ) );
  2153 			$hw = trim( image_hwstring( $size_array[0], $size_array[1] ) );
  1976 			return str_replace( '<img', "<img {$hw}", $image );
  2154 			return str_replace( '<img', "<img {$hw}", $image );
  1977 		}
  2155 		}
  1978 	}
  2156 	}
  1979 
  2157 
  2021  * @param string $iframe  The HTML `iframe` tag where the attribute should be added.
  2199  * @param string $iframe  The HTML `iframe` tag where the attribute should be added.
  2022  * @param string $context Additional context to pass to the filters.
  2200  * @param string $context Additional context to pass to the filters.
  2023  * @return string Converted `iframe` tag with `loading` attribute added.
  2201  * @return string Converted `iframe` tag with `loading` attribute added.
  2024  */
  2202  */
  2025 function wp_iframe_tag_add_loading_attr( $iframe, $context ) {
  2203 function wp_iframe_tag_add_loading_attr( $iframe, $context ) {
  2026 	// Iframes with fallback content (see `wp_filter_oembed_result()`) should not be lazy-loaded because they are
  2204 	/*
  2027 	// visually hidden initially.
  2205 	 * Get loading attribute value to use. This must occur before the conditional check below so that even iframes that
  2028 	if ( false !== strpos( $iframe, ' data-secret="' ) ) {
  2206 	 * are ineligible for being lazy-loaded are considered.
       
  2207 	 */
       
  2208 	$optimization_attrs = wp_get_loading_optimization_attributes(
       
  2209 		'iframe',
       
  2210 		array(
       
  2211 			/*
       
  2212 			 * The concrete values for width and height are not important here for now
       
  2213 			 * since fetchpriority is not yet supported for iframes.
       
  2214 			 * TODO: Use WP_HTML_Tag_Processor to extract actual values once support is
       
  2215 			 * added.
       
  2216 			 */
       
  2217 			'width'   => str_contains( $iframe, ' width="' ) ? 100 : null,
       
  2218 			'height'  => str_contains( $iframe, ' height="' ) ? 100 : null,
       
  2219 			// This function is never called when a 'loading' attribute is already present.
       
  2220 			'loading' => null,
       
  2221 		),
       
  2222 		$context
       
  2223 	);
       
  2224 
       
  2225 	// Iframes should have source and dimension attributes for the `loading` attribute to be added.
       
  2226 	if ( ! str_contains( $iframe, ' src="' ) || ! str_contains( $iframe, ' width="' ) || ! str_contains( $iframe, ' height="' ) ) {
  2029 		return $iframe;
  2227 		return $iframe;
  2030 	}
  2228 	}
  2031 
  2229 
  2032 	// Get loading attribute value to use. This must occur before the conditional check below so that even iframes that
  2230 	$value = isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false;
  2033 	// are ineligible for being lazy-loaded are considered.
       
  2034 	$value = wp_get_loading_attr_default( $context );
       
  2035 
       
  2036 	// Iframes should have source and dimension attributes for the `loading` attribute to be added.
       
  2037 	if ( false === strpos( $iframe, ' src="' ) || false === strpos( $iframe, ' width="' ) || false === strpos( $iframe, ' height="' ) ) {
       
  2038 		return $iframe;
       
  2039 	}
       
  2040 
  2231 
  2041 	/**
  2232 	/**
  2042 	 * Filters the `loading` attribute value to add to an iframe. Default `lazy`.
  2233 	 * Filters the `loading` attribute value to add to an iframe. Default `lazy`.
  2043 	 *
  2234 	 *
  2044 	 * Returning `false` or an empty string will not add the attribute.
  2235 	 * Returning `false` or an empty string will not add the attribute.
  2103  *
  2294  *
  2104  * @param string[] $attr Array of thumbnail attributes including src, class, alt, title, keyed by attribute name.
  2295  * @param string[] $attr Array of thumbnail attributes including src, class, alt, title, keyed by attribute name.
  2105  */
  2296  */
  2106 function _wp_post_thumbnail_class_filter_remove( $attr ) {
  2297 function _wp_post_thumbnail_class_filter_remove( $attr ) {
  2107 	remove_filter( 'wp_get_attachment_image_attributes', '_wp_post_thumbnail_class_filter' );
  2298 	remove_filter( 'wp_get_attachment_image_attributes', '_wp_post_thumbnail_class_filter' );
       
  2299 }
       
  2300 
       
  2301 /**
       
  2302  * Overrides the context used in {@see wp_get_attachment_image()}. Internal use only.
       
  2303  *
       
  2304  * Uses the {@see 'begin_fetch_post_thumbnail_html'} and {@see 'end_fetch_post_thumbnail_html'}
       
  2305  * action hooks to dynamically add/remove itself so as to only filter post thumbnails.
       
  2306  *
       
  2307  * @ignore
       
  2308  * @since 6.3.0
       
  2309  * @access private
       
  2310  *
       
  2311  * @param string $context The context for rendering an attachment image.
       
  2312  * @return string Modified context set to 'the_post_thumbnail'.
       
  2313  */
       
  2314 function _wp_post_thumbnail_context_filter( $context ) {
       
  2315 	return 'the_post_thumbnail';
       
  2316 }
       
  2317 
       
  2318 /**
       
  2319  * Adds the '_wp_post_thumbnail_context_filter' callback to the 'wp_get_attachment_image_context'
       
  2320  * filter hook. Internal use only.
       
  2321  *
       
  2322  * @ignore
       
  2323  * @since 6.3.0
       
  2324  * @access private
       
  2325  */
       
  2326 function _wp_post_thumbnail_context_filter_add() {
       
  2327 	add_filter( 'wp_get_attachment_image_context', '_wp_post_thumbnail_context_filter' );
       
  2328 }
       
  2329 
       
  2330 /**
       
  2331  * Removes the '_wp_post_thumbnail_context_filter' callback from the 'wp_get_attachment_image_context'
       
  2332  * filter hook. Internal use only.
       
  2333  *
       
  2334  * @ignore
       
  2335  * @since 6.3.0
       
  2336  * @access private
       
  2337  */
       
  2338 function _wp_post_thumbnail_context_filter_remove() {
       
  2339 	remove_filter( 'wp_get_attachment_image_context', '_wp_post_thumbnail_context_filter' );
  2108 }
  2340 }
  2109 
  2341 
  2110 add_shortcode( 'wp_caption', 'img_caption_shortcode' );
  2342 add_shortcode( 'wp_caption', 'img_caption_shortcode' );
  2111 add_shortcode( 'caption', 'img_caption_shortcode' );
  2343 add_shortcode( 'caption', 'img_caption_shortcode' );
  2112 
  2344 
  2144 	if ( ! isset( $attr['caption'] ) ) {
  2376 	if ( ! isset( $attr['caption'] ) ) {
  2145 		if ( preg_match( '#((?:<a [^>]+>\s*)?<img [^>]+>(?:\s*</a>)?)(.*)#is', $content, $matches ) ) {
  2377 		if ( preg_match( '#((?:<a [^>]+>\s*)?<img [^>]+>(?:\s*</a>)?)(.*)#is', $content, $matches ) ) {
  2146 			$content         = $matches[1];
  2378 			$content         = $matches[1];
  2147 			$attr['caption'] = trim( $matches[2] );
  2379 			$attr['caption'] = trim( $matches[2] );
  2148 		}
  2380 		}
  2149 	} elseif ( strpos( $attr['caption'], '<' ) !== false ) {
  2381 	} elseif ( str_contains( $attr['caption'], '<' ) ) {
  2150 		$attr['caption'] = wp_kses( $attr['caption'], 'post' );
  2382 		$attr['caption'] = wp_kses( $attr['caption'], 'post' );
  2151 	}
  2383 	}
  2152 
  2384 
  2153 	/**
  2385 	/**
  2154 	 * Filters the default caption shortcode output.
  2386 	 * Filters the default caption shortcode output.
  2277  *
  2509  *
  2278  * This implements the functionality of the Gallery Shortcode for displaying
  2510  * This implements the functionality of the Gallery Shortcode for displaying
  2279  * WordPress images on a post.
  2511  * WordPress images on a post.
  2280  *
  2512  *
  2281  * @since 2.5.0
  2513  * @since 2.5.0
       
  2514  * @since 2.8.0 Added the `$attr` parameter to set the shortcode output. New attributes included
       
  2515  *              such as `size`, `itemtag`, `icontag`, `captiontag`, and columns. Changed markup from
       
  2516  *              `div` tags to `dl`, `dt` and `dd` tags. Support more than one gallery on the
       
  2517  *              same page.
       
  2518  * @since 2.9.0 Added support for `include` and `exclude` to shortcode.
       
  2519  * @since 3.5.0 Use get_post() instead of global `$post`. Handle mapping of `ids` to `include`
       
  2520  *              and `orderby`.
       
  2521  * @since 3.6.0 Added validation for tags used in gallery shortcode. Add orientation information to items.
       
  2522  * @since 3.7.0 Introduced the `link` attribute.
       
  2523  * @since 3.9.0 `html5` gallery support, accepting 'itemtag', 'icontag', and 'captiontag' attributes.
       
  2524  * @since 4.0.0 Removed use of `extract()`.
       
  2525  * @since 4.1.0 Added attribute to `wp_get_attachment_link()` to output `aria-describedby`.
       
  2526  * @since 4.2.0 Passed the shortcode instance ID to `post_gallery` and `post_playlist` filters.
       
  2527  * @since 4.6.0 Standardized filter docs to match documentation standards for PHP.
       
  2528  * @since 5.1.0 Code cleanup for WPCS 1.0.0 coding standards.
       
  2529  * @since 5.3.0 Saved progress of intermediate image creation after upload.
       
  2530  * @since 5.5.0 Ensured that galleries can be output as a list of links in feeds.
       
  2531  * @since 5.6.0 Replaced order-style PHP type conversion functions with typecasts. Fix logic for
       
  2532  *              an array of image dimensions.
  2282  *
  2533  *
  2283  * @param array $attr {
  2534  * @param array $attr {
  2284  *     Attributes of the gallery shortcode.
  2535  *     Attributes of the gallery shortcode.
  2285  *
  2536  *
  2286  *     @type string       $order      Order of the images in the gallery. Default 'ASC'. Accepts 'ASC', 'DESC'.
  2537  *     @type string       $order      Order of the images in the gallery. Default 'ASC'. Accepts 'ASC', 'DESC'.
  2306  */
  2557  */
  2307 function gallery_shortcode( $attr ) {
  2558 function gallery_shortcode( $attr ) {
  2308 	$post = get_post();
  2559 	$post = get_post();
  2309 
  2560 
  2310 	static $instance = 0;
  2561 	static $instance = 0;
  2311 	$instance++;
  2562 	++$instance;
  2312 
  2563 
  2313 	if ( ! empty( $attr['ids'] ) ) {
  2564 	if ( ! empty( $attr['ids'] ) ) {
  2314 		// 'ids' is explicitly ordered, unless you specify otherwise.
  2565 		// 'ids' is explicitly ordered, unless you specify otherwise.
  2315 		if ( empty( $attr['orderby'] ) ) {
  2566 		if ( empty( $attr['orderby'] ) ) {
  2316 			$attr['orderby'] = 'post__in';
  2567 			$attr['orderby'] = 'post__in';
  2375 		$attachments = array();
  2626 		$attachments = array();
  2376 		foreach ( $_attachments as $key => $val ) {
  2627 		foreach ( $_attachments as $key => $val ) {
  2377 			$attachments[ $val->ID ] = $_attachments[ $key ];
  2628 			$attachments[ $val->ID ] = $_attachments[ $key ];
  2378 		}
  2629 		}
  2379 	} elseif ( ! empty( $atts['exclude'] ) ) {
  2630 	} elseif ( ! empty( $atts['exclude'] ) ) {
  2380 		$attachments = get_children(
  2631 		$post_parent_id = $id;
       
  2632 		$attachments    = get_children(
  2381 			array(
  2633 			array(
  2382 				'post_parent'    => $id,
  2634 				'post_parent'    => $id,
  2383 				'exclude'        => $atts['exclude'],
  2635 				'exclude'        => $atts['exclude'],
  2384 				'post_status'    => 'inherit',
  2636 				'post_status'    => 'inherit',
  2385 				'post_type'      => 'attachment',
  2637 				'post_type'      => 'attachment',
  2387 				'order'          => $atts['order'],
  2639 				'order'          => $atts['order'],
  2388 				'orderby'        => $atts['orderby'],
  2640 				'orderby'        => $atts['orderby'],
  2389 			)
  2641 			)
  2390 		);
  2642 		);
  2391 	} else {
  2643 	} else {
  2392 		$attachments = get_children(
  2644 		$post_parent_id = $id;
       
  2645 		$attachments    = get_children(
  2393 			array(
  2646 			array(
  2394 				'post_parent'    => $id,
  2647 				'post_parent'    => $id,
  2395 				'post_status'    => 'inherit',
  2648 				'post_status'    => 'inherit',
  2396 				'post_type'      => 'attachment',
  2649 				'post_type'      => 'attachment',
  2397 				'post_mime_type' => 'image',
  2650 				'post_mime_type' => 'image',
  2398 				'order'          => $atts['order'],
  2651 				'order'          => $atts['order'],
  2399 				'orderby'        => $atts['orderby'],
  2652 				'orderby'        => $atts['orderby'],
  2400 			)
  2653 			)
  2401 		);
  2654 		);
       
  2655 	}
       
  2656 
       
  2657 	if ( ! empty( $post_parent_id ) ) {
       
  2658 		$post_parent = get_post( $post_parent_id );
       
  2659 
       
  2660 		// Terminate the shortcode execution if the user cannot read the post or it is password-protected.
       
  2661 		if ( ! is_post_publicly_viewable( $post_parent->ID ) && ! current_user_can( 'read_post', $post_parent->ID )
       
  2662 			|| post_password_required( $post_parent )
       
  2663 		) {
       
  2664 			return '';
       
  2665 		}
  2402 	}
  2666 	}
  2403 
  2667 
  2404 	if ( empty( $attachments ) ) {
  2668 	if ( empty( $attachments ) ) {
  2405 		return '';
  2669 		return '';
  2406 	}
  2670 	}
  2554 	<# if ( data.thumb && data.thumb.src ) { #>
  2818 	<# if ( data.thumb && data.thumb.src ) { #>
  2555 		<img src="{{ data.thumb.src }}" alt="" />
  2819 		<img src="{{ data.thumb.src }}" alt="" />
  2556 	<# } #>
  2820 	<# } #>
  2557 	<div class="wp-playlist-caption">
  2821 	<div class="wp-playlist-caption">
  2558 		<span class="wp-playlist-item-meta wp-playlist-item-title">
  2822 		<span class="wp-playlist-item-meta wp-playlist-item-title">
  2559 		<?php
  2823 			<# if ( data.meta.album || data.meta.artist ) { #>
  2560 			/* translators: %s: Playlist item title. */
  2824 				<?php
  2561 			printf( _x( '&#8220;%s&#8221;', 'playlist item title' ), '{{ data.title }}' );
  2825 				/* translators: %s: Playlist item title. */
  2562 		?>
  2826 				printf( _x( '&#8220;%s&#8221;', 'playlist item title' ), '{{ data.title }}' );
       
  2827 				?>
       
  2828 			<# } else { #>
       
  2829 				{{ data.title }}
       
  2830 			<# } #>
  2563 		</span>
  2831 		</span>
  2564 		<# if ( data.meta.album ) { #><span class="wp-playlist-item-meta wp-playlist-item-album">{{ data.meta.album }}</span><# } #>
  2832 		<# if ( data.meta.album ) { #><span class="wp-playlist-item-meta wp-playlist-item-album">{{ data.meta.album }}</span><# } #>
  2565 		<# if ( data.meta.artist ) { #><span class="wp-playlist-item-meta wp-playlist-item-artist">{{ data.meta.artist }}</span><# } #>
  2833 		<# if ( data.meta.artist ) { #><span class="wp-playlist-item-meta wp-playlist-item-artist">{{ data.meta.artist }}</span><# } #>
  2566 	</div>
  2834 	</div>
  2567 </script>
  2835 </script>
  2570 		<a class="wp-playlist-caption" href="{{ data.src }}">
  2838 		<a class="wp-playlist-caption" href="{{ data.src }}">
  2571 			{{ data.index ? ( data.index + '. ' ) : '' }}
  2839 			{{ data.index ? ( data.index + '. ' ) : '' }}
  2572 			<# if ( data.caption ) { #>
  2840 			<# if ( data.caption ) { #>
  2573 				{{ data.caption }}
  2841 				{{ data.caption }}
  2574 			<# } else { #>
  2842 			<# } else { #>
  2575 				<span class="wp-playlist-item-title">
       
  2576 				<?php
       
  2577 					/* translators: %s: Playlist item title. */
       
  2578 					printf( _x( '&#8220;%s&#8221;', 'playlist item title' ), '{{{ data.title }}}' );
       
  2579 				?>
       
  2580 				</span>
       
  2581 				<# if ( data.artists && data.meta.artist ) { #>
  2843 				<# if ( data.artists && data.meta.artist ) { #>
  2582 				<span class="wp-playlist-item-artist"> &mdash; {{ data.meta.artist }}</span>
  2844 					<span class="wp-playlist-item-title">
       
  2845 						<?php
       
  2846 						/* translators: %s: Playlist item title. */
       
  2847 						printf( _x( '&#8220;%s&#8221;', 'playlist item title' ), '{{{ data.title }}}' );
       
  2848 						?>
       
  2849 					</span>
       
  2850 					<span class="wp-playlist-item-artist"> &mdash; {{ data.meta.artist }}</span>
       
  2851 				<# } else { #>
       
  2852 					<span class="wp-playlist-item-title">{{{ data.title }}}</span>
  2583 				<# } #>
  2853 				<# } #>
  2584 			<# } #>
  2854 			<# } #>
  2585 		</a>
  2855 		</a>
  2586 		<# if ( data.meta.length_formatted ) { #>
  2856 		<# if ( data.meta.length_formatted ) { #>
  2587 		<div class="wp-playlist-item-length">{{ data.meta.length_formatted }}</div>
  2857 		<div class="wp-playlist-item-length">{{ data.meta.length_formatted }}</div>
  2590 </script>
  2860 </script>
  2591 	<?php
  2861 	<?php
  2592 }
  2862 }
  2593 
  2863 
  2594 /**
  2864 /**
  2595  * Outputs and enqueue default scripts and styles for playlists.
  2865  * Outputs and enqueues default scripts and styles for playlists.
  2596  *
  2866  *
  2597  * @since 3.9.0
  2867  * @since 3.9.0
  2598  *
  2868  *
  2599  * @param string $type Type of playlist. Accepts 'audio' or 'video'.
  2869  * @param string $type Type of playlist. Accepts 'audio' or 'video'.
  2600  */
  2870  */
  2647 function wp_playlist_shortcode( $attr ) {
  2917 function wp_playlist_shortcode( $attr ) {
  2648 	global $content_width;
  2918 	global $content_width;
  2649 	$post = get_post();
  2919 	$post = get_post();
  2650 
  2920 
  2651 	static $instance = 0;
  2921 	static $instance = 0;
  2652 	$instance++;
  2922 	++$instance;
  2653 
  2923 
  2654 	if ( ! empty( $attr['ids'] ) ) {
  2924 	if ( ! empty( $attr['ids'] ) ) {
  2655 		// 'ids' is explicitly ordered, unless you specify otherwise.
  2925 		// 'ids' is explicitly ordered, unless you specify otherwise.
  2656 		if ( empty( $attr['orderby'] ) ) {
  2926 		if ( empty( $attr['orderby'] ) ) {
  2657 			$attr['orderby'] = 'post__in';
  2927 			$attr['orderby'] = 'post__in';
  2725 	} else {
  2995 	} else {
  2726 		$args['post_parent'] = $id;
  2996 		$args['post_parent'] = $id;
  2727 		$attachments         = get_children( $args );
  2997 		$attachments         = get_children( $args );
  2728 	}
  2998 	}
  2729 
  2999 
       
  3000 	if ( ! empty( $args['post_parent'] ) ) {
       
  3001 		$post_parent = get_post( $id );
       
  3002 
       
  3003 		// Terminate the shortcode execution if the user cannot read the post or it is password-protected.
       
  3004 		if ( ! current_user_can( 'read_post', $post_parent->ID ) || post_password_required( $post_parent ) ) {
       
  3005 			return '';
       
  3006 		}
       
  3007 	}
       
  3008 
  2730 	if ( empty( $attachments ) ) {
  3009 	if ( empty( $attachments ) ) {
  2731 		return '';
  3010 		return '';
  2732 	}
  3011 	}
  2733 
  3012 
  2734 	if ( is_feed() ) {
  3013 	if ( is_feed() ) {
  2804 				list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'full' );
  3083 				list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'full' );
  2805 				$track['image']               = compact( 'src', 'width', 'height' );
  3084 				$track['image']               = compact( 'src', 'width', 'height' );
  2806 				list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'thumbnail' );
  3085 				list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'thumbnail' );
  2807 				$track['thumb']               = compact( 'src', 'width', 'height' );
  3086 				$track['thumb']               = compact( 'src', 'width', 'height' );
  2808 			} else {
  3087 			} else {
  2809 				$src            = wp_mime_type_icon( $attachment->ID );
  3088 				$src            = wp_mime_type_icon( $attachment->ID, '.svg' );
  2810 				$width          = 48;
  3089 				$width          = 48;
  2811 				$height         = 64;
  3090 				$height         = 64;
  2812 				$track['image'] = compact( 'src', 'width', 'height' );
  3091 				$track['image'] = compact( 'src', 'width', 'height' );
  2813 				$track['thumb'] = compact( 'src', 'width', 'height' );
  3092 				$track['thumb'] = compact( 'src', 'width', 'height' );
  2814 			}
  3093 			}
  2872  * @param string $url The media element URL.
  3151  * @param string $url The media element URL.
  2873  * @return string Fallback HTML.
  3152  * @return string Fallback HTML.
  2874  */
  3153  */
  2875 function wp_mediaelement_fallback( $url ) {
  3154 function wp_mediaelement_fallback( $url ) {
  2876 	/**
  3155 	/**
  2877 	 * Filters the Mediaelement fallback output for no-JS.
  3156 	 * Filters the MediaElement fallback output for no-JS.
  2878 	 *
  3157 	 *
  2879 	 * @since 3.6.0
  3158 	 * @since 3.6.0
  2880 	 *
  3159 	 *
  2881 	 * @param string $output Fallback output for no-JS.
  3160 	 * @param string $output Fallback output for no-JS.
  2882 	 * @param string $url    Media file URL.
  3161 	 * @param string $url    Media file URL.
  2961  */
  3240  */
  2962 function wp_audio_shortcode( $attr, $content = '' ) {
  3241 function wp_audio_shortcode( $attr, $content = '' ) {
  2963 	$post_id = get_post() ? get_the_ID() : 0;
  3242 	$post_id = get_post() ? get_the_ID() : 0;
  2964 
  3243 
  2965 	static $instance = 0;
  3244 	static $instance = 0;
  2966 	$instance++;
  3245 	++$instance;
  2967 
  3246 
  2968 	/**
  3247 	/**
  2969 	 * Filters the default audio shortcode output.
  3248 	 * Filters the default audio shortcode output.
  2970 	 *
  3249 	 *
  2971 	 * If the filtered output isn't empty, it will be used instead of generating the default audio template.
  3250 	 * If the filtered output isn't empty, it will be used instead of generating the default audio template.
  2972 	 *
  3251 	 *
  2973 	 * @since 3.6.0
  3252 	 * @since 3.6.0
  2974 	 *
  3253 	 *
  2975 	 * @param string $html     Empty variable to be replaced with shortcode markup.
  3254 	 * @param string $html     Empty variable to be replaced with shortcode markup.
  2976 	 * @param array  $attr     Attributes of the shortcode. @see wp_audio_shortcode()
  3255 	 * @param array  $attr     Attributes of the shortcode. See {@see wp_audio_shortcode()}.
  2977 	 * @param string $content  Shortcode content.
  3256 	 * @param string $content  Shortcode content.
  2978 	 * @param int    $instance Unique numeric ID of this audio shortcode instance.
  3257 	 * @param int    $instance Unique numeric ID of this audio shortcode instance.
  2979 	 */
  3258 	 */
  2980 	$override = apply_filters( 'wp_audio_shortcode_override', '', $attr, $content, $instance );
  3259 	$override = apply_filters( 'wp_audio_shortcode_override', '', $attr, $content, $instance );
  2981 
  3260 
  3166  *     @type int    $height   Height of the video embed in pixels. Default 360.
  3445  *     @type int    $height   Height of the video embed in pixels. Default 360.
  3167  *     @type int    $width    Width of the video embed in pixels. Default $content_width or 640.
  3446  *     @type int    $width    Width of the video embed in pixels. Default $content_width or 640.
  3168  *     @type string $poster   The 'poster' attribute for the `<video>` element. Default empty.
  3447  *     @type string $poster   The 'poster' attribute for the `<video>` element. Default empty.
  3169  *     @type string $loop     The 'loop' attribute for the `<video>` element. Default empty.
  3448  *     @type string $loop     The 'loop' attribute for the `<video>` element. Default empty.
  3170  *     @type string $autoplay The 'autoplay' attribute for the `<video>` element. Default empty.
  3449  *     @type string $autoplay The 'autoplay' attribute for the `<video>` element. Default empty.
       
  3450  *     @type string $muted    The 'muted' attribute for the `<video>` element. Default false.
  3171  *     @type string $preload  The 'preload' attribute for the `<video>` element.
  3451  *     @type string $preload  The 'preload' attribute for the `<video>` element.
  3172  *                            Default 'metadata'.
  3452  *                            Default 'metadata'.
  3173  *     @type string $class    The 'class' attribute for the `<video>` element.
  3453  *     @type string $class    The 'class' attribute for the `<video>` element.
  3174  *                            Default 'wp-video-shortcode'.
  3454  *                            Default 'wp-video-shortcode'.
  3175  * }
  3455  * }
  3179 function wp_video_shortcode( $attr, $content = '' ) {
  3459 function wp_video_shortcode( $attr, $content = '' ) {
  3180 	global $content_width;
  3460 	global $content_width;
  3181 	$post_id = get_post() ? get_the_ID() : 0;
  3461 	$post_id = get_post() ? get_the_ID() : 0;
  3182 
  3462 
  3183 	static $instance = 0;
  3463 	static $instance = 0;
  3184 	$instance++;
  3464 	++$instance;
  3185 
  3465 
  3186 	/**
  3466 	/**
  3187 	 * Filters the default video shortcode output.
  3467 	 * Filters the default video shortcode output.
  3188 	 *
  3468 	 *
  3189 	 * If the filtered output isn't empty, it will be used instead of generating
  3469 	 * If the filtered output isn't empty, it will be used instead of generating
  3192 	 * @since 3.6.0
  3472 	 * @since 3.6.0
  3193 	 *
  3473 	 *
  3194 	 * @see wp_video_shortcode()
  3474 	 * @see wp_video_shortcode()
  3195 	 *
  3475 	 *
  3196 	 * @param string $html     Empty variable to be replaced with shortcode markup.
  3476 	 * @param string $html     Empty variable to be replaced with shortcode markup.
  3197 	 * @param array  $attr     Attributes of the shortcode. @see wp_video_shortcode()
  3477 	 * @param array  $attr     Attributes of the shortcode. See {@see wp_video_shortcode()}.
  3198 	 * @param string $content  Video shortcode content.
  3478 	 * @param string $content  Video shortcode content.
  3199 	 * @param int    $instance Unique numeric ID of this video shortcode instance.
  3479 	 * @param int    $instance Unique numeric ID of this video shortcode instance.
  3200 	 */
  3480 	 */
  3201 	$override = apply_filters( 'wp_video_shortcode_override', '', $attr, $content, $instance );
  3481 	$override = apply_filters( 'wp_video_shortcode_override', '', $attr, $content, $instance );
  3202 
  3482 
  3210 	$defaults_atts = array(
  3490 	$defaults_atts = array(
  3211 		'src'      => '',
  3491 		'src'      => '',
  3212 		'poster'   => '',
  3492 		'poster'   => '',
  3213 		'loop'     => '',
  3493 		'loop'     => '',
  3214 		'autoplay' => '',
  3494 		'autoplay' => '',
       
  3495 		'muted'    => 'false',
  3215 		'preload'  => 'metadata',
  3496 		'preload'  => 'metadata',
  3216 		'width'    => 640,
  3497 		'width'    => 640,
  3217 		'height'   => 360,
  3498 		'height'   => 360,
  3218 		'class'    => 'wp-video-shortcode',
  3499 		'class'    => 'wp-video-shortcode',
  3219 	);
  3500 	);
  3300 		wp_enqueue_style( 'wp-mediaelement' );
  3581 		wp_enqueue_style( 'wp-mediaelement' );
  3301 		wp_enqueue_script( 'wp-mediaelement' );
  3582 		wp_enqueue_script( 'wp-mediaelement' );
  3302 		wp_enqueue_script( 'mediaelement-vimeo' );
  3583 		wp_enqueue_script( 'mediaelement-vimeo' );
  3303 	}
  3584 	}
  3304 
  3585 
  3305 	// MediaElement.js has issues with some URL formats for Vimeo and YouTube,
  3586 	/*
  3306 	// so update the URL to prevent the ME.js player from breaking.
  3587 	 * MediaElement.js has issues with some URL formats for Vimeo and YouTube,
       
  3588 	 * so update the URL to prevent the ME.js player from breaking.
       
  3589 	 */
  3307 	if ( 'mediaelement' === $library ) {
  3590 	if ( 'mediaelement' === $library ) {
  3308 		if ( $is_youtube ) {
  3591 		if ( $is_youtube ) {
  3309 			// Remove `feature` query arg and force SSL - see #40866.
  3592 			// Remove `feature` query arg and force SSL - see #40866.
  3310 			$atts['src'] = remove_query_arg( 'feature', $atts['src'] );
  3593 			$atts['src'] = remove_query_arg( 'feature', $atts['src'] );
  3311 			$atts['src'] = set_url_scheme( $atts['src'], 'https' );
  3594 			$atts['src'] = set_url_scheme( $atts['src'], 'https' );
  3337 		'width'    => absint( $atts['width'] ),
  3620 		'width'    => absint( $atts['width'] ),
  3338 		'height'   => absint( $atts['height'] ),
  3621 		'height'   => absint( $atts['height'] ),
  3339 		'poster'   => esc_url( $atts['poster'] ),
  3622 		'poster'   => esc_url( $atts['poster'] ),
  3340 		'loop'     => wp_validate_boolean( $atts['loop'] ),
  3623 		'loop'     => wp_validate_boolean( $atts['loop'] ),
  3341 		'autoplay' => wp_validate_boolean( $atts['autoplay'] ),
  3624 		'autoplay' => wp_validate_boolean( $atts['autoplay'] ),
       
  3625 		'muted'    => wp_validate_boolean( $atts['muted'] ),
  3342 		'preload'  => $atts['preload'],
  3626 		'preload'  => $atts['preload'],
  3343 	);
  3627 	);
  3344 
  3628 
  3345 	// These ones should just be omitted altogether if they are blank.
  3629 	// These ones should just be omitted altogether if they are blank.
  3346 	foreach ( array( 'poster', 'loop', 'autoplay', 'preload' ) as $a ) {
  3630 	foreach ( array( 'poster', 'loop', 'autoplay', 'preload', 'muted' ) as $a ) {
  3347 		if ( empty( $html_atts[ $a ] ) ) {
  3631 		if ( empty( $html_atts[ $a ] ) ) {
  3348 			unset( $html_atts[ $a ] );
  3632 			unset( $html_atts[ $a ] );
  3349 		}
  3633 		}
  3350 	}
  3634 	}
  3351 
  3635 
  3381 			$html .= sprintf( $source, $type['type'], esc_url( $url ) );
  3665 			$html .= sprintf( $source, $type['type'], esc_url( $url ) );
  3382 		}
  3666 		}
  3383 	}
  3667 	}
  3384 
  3668 
  3385 	if ( ! empty( $content ) ) {
  3669 	if ( ! empty( $content ) ) {
  3386 		if ( false !== strpos( $content, "\n" ) ) {
  3670 		if ( str_contains( $content, "\n" ) ) {
  3387 			$content = str_replace( array( "\r\n", "\n", "\t" ), '', $content );
  3671 			$content = str_replace( array( "\r\n", "\n", "\t" ), '', $content );
  3388 		}
  3672 		}
  3389 		$html .= trim( $content );
  3673 		$html .= trim( $content );
  3390 	}
  3674 	}
  3391 
  3675 
  3586 	$file     = get_attached_file( $attachment->ID );
  3870 	$file     = get_attached_file( $attachment->ID );
  3587 	$filename = wp_basename( $file );
  3871 	$filename = wp_basename( $file );
  3588 
  3872 
  3589 	$objects = array( 'attachment' );
  3873 	$objects = array( 'attachment' );
  3590 
  3874 
  3591 	if ( false !== strpos( $filename, '.' ) ) {
  3875 	if ( str_contains( $filename, '.' ) ) {
  3592 		$objects[] = 'attachment:' . substr( $filename, strrpos( $filename, '.' ) + 1 );
  3876 		$objects[] = 'attachment:' . substr( $filename, strrpos( $filename, '.' ) + 1 );
  3593 	}
  3877 	}
  3594 
  3878 
  3595 	if ( ! empty( $attachment->post_mime_type ) ) {
  3879 	if ( ! empty( $attachment->post_mime_type ) ) {
  3596 		$objects[] = 'attachment:' . $attachment->post_mime_type;
  3880 		$objects[] = 'attachment:' . $attachment->post_mime_type;
  3597 
  3881 
  3598 		if ( false !== strpos( $attachment->post_mime_type, '/' ) ) {
  3882 		if ( str_contains( $attachment->post_mime_type, '/' ) ) {
  3599 			foreach ( explode( '/', $attachment->post_mime_type ) as $token ) {
  3883 			foreach ( explode( '/', $attachment->post_mime_type ) as $token ) {
  3600 				if ( ! empty( $token ) ) {
  3884 				if ( ! empty( $token ) ) {
  3601 					$objects[] = "attachment:$token";
  3885 					$objects[] = "attachment:$token";
  3602 				}
  3886 				}
  3603 			}
  3887 			}
  3637 function get_taxonomies_for_attachments( $output = 'names' ) {
  3921 function get_taxonomies_for_attachments( $output = 'names' ) {
  3638 	$taxonomies = array();
  3922 	$taxonomies = array();
  3639 
  3923 
  3640 	foreach ( get_taxonomies( array(), 'objects' ) as $taxonomy ) {
  3924 	foreach ( get_taxonomies( array(), 'objects' ) as $taxonomy ) {
  3641 		foreach ( $taxonomy->object_type as $object_type ) {
  3925 		foreach ( $taxonomy->object_type as $object_type ) {
  3642 			if ( 'attachment' === $object_type || 0 === strpos( $object_type, 'attachment:' ) ) {
  3926 			if ( 'attachment' === $object_type || str_starts_with( $object_type, 'attachment:' ) ) {
  3643 				if ( 'names' === $output ) {
  3927 				if ( 'names' === $output ) {
  3644 					$taxonomies[] = $taxonomy->name;
  3928 					$taxonomies[] = $taxonomy->name;
  3645 				} else {
  3929 				} else {
  3646 					$taxonomies[ $taxonomy->name ] = $taxonomy;
  3930 					$taxonomies[ $taxonomy->name ] = $taxonomy;
  3647 				}
  3931 				}
  3655 
  3939 
  3656 /**
  3940 /**
  3657  * Determines whether the value is an acceptable type for GD image functions.
  3941  * Determines whether the value is an acceptable type for GD image functions.
  3658  *
  3942  *
  3659  * In PHP 8.0, the GD extension uses GdImage objects for its data structures.
  3943  * In PHP 8.0, the GD extension uses GdImage objects for its data structures.
  3660  * This function checks if the passed value is either a resource of type `gd`
  3944  * This function checks if the passed value is either a GdImage object instance
  3661  * or a GdImage object instance. Any other type will return false.
  3945  * or a resource of type `gd`. Any other type will return false.
  3662  *
  3946  *
  3663  * @since 5.6.0
  3947  * @since 5.6.0
  3664  *
  3948  *
  3665  * @param resource|GdImage|false $image A value to check the type for.
  3949  * @param resource|GdImage|false $image A value to check the type for.
  3666  * @return bool True if $image is either a GD image resource or GdImage instance,
  3950  * @return bool True if `$image` is either a GD image resource or a GdImage instance,
  3667  *              false otherwise.
  3951  *              false otherwise.
  3668  */
  3952  */
  3669 function is_gd_image( $image ) {
  3953 function is_gd_image( $image ) {
  3670 	if ( is_resource( $image ) && 'gd' === get_resource_type( $image )
  3954 	if ( $image instanceof GdImage
  3671 		|| is_object( $image ) && $image instanceof GdImage
  3955 		|| is_resource( $image ) && 'gd' === get_resource_type( $image )
  3672 	) {
  3956 	) {
  3673 		return true;
  3957 		return true;
  3674 	}
  3958 	}
  3675 
  3959 
  3676 	return false;
  3960 	return false;
  3677 }
  3961 }
  3678 
  3962 
  3679 /**
  3963 /**
  3680  * Create new GD image resource with transparency support
  3964  * Creates a new GD image resource with transparency support.
  3681  *
  3965  *
  3682  * @todo Deprecate if possible.
  3966  * @todo Deprecate if possible.
  3683  *
  3967  *
  3684  * @since 2.9.0
  3968  * @since 2.9.0
  3685  *
  3969  *
  3700 
  3984 
  3701 	return $img;
  3985 	return $img;
  3702 }
  3986 }
  3703 
  3987 
  3704 /**
  3988 /**
  3705  * Based on a supplied width/height example, return the biggest possible dimensions based on the max width/height.
  3989  * Based on a supplied width/height example, returns the biggest possible dimensions based on the max width/height.
  3706  *
  3990  *
  3707  * @since 2.9.0
  3991  * @since 2.9.0
  3708  *
  3992  *
  3709  * @see wp_constrain_dimensions()
  3993  * @see wp_constrain_dimensions()
  3710  *
  3994  *
  3763  *                                  a WP_Error object otherwise.
  4047  *                                  a WP_Error object otherwise.
  3764  */
  4048  */
  3765 function wp_get_image_editor( $path, $args = array() ) {
  4049 function wp_get_image_editor( $path, $args = array() ) {
  3766 	$args['path'] = $path;
  4050 	$args['path'] = $path;
  3767 
  4051 
       
  4052 	// If the mime type is not set in args, try to extract and set it from the file.
  3768 	if ( ! isset( $args['mime_type'] ) ) {
  4053 	if ( ! isset( $args['mime_type'] ) ) {
  3769 		$file_info = wp_check_filetype( $args['path'] );
  4054 		$file_info = wp_check_filetype( $args['path'] );
  3770 
  4055 
  3771 		// If $file_info['type'] is false, then we let the editor attempt to
  4056 		/*
  3772 		// figure out the file type, rather than forcing a failure based on extension.
  4057 		 * If $file_info['type'] is false, then we let the editor attempt to
       
  4058 		 * figure out the file type, rather than forcing a failure based on extension.
       
  4059 		 */
  3773 		if ( isset( $file_info ) && $file_info['type'] ) {
  4060 		if ( isset( $file_info ) && $file_info['type'] ) {
  3774 			$args['mime_type'] = $file_info['type'];
  4061 			$args['mime_type'] = $file_info['type'];
       
  4062 		}
       
  4063 	}
       
  4064 
       
  4065 	// Check and set the output mime type mapped to the input type.
       
  4066 	if ( isset( $args['mime_type'] ) ) {
       
  4067 		/** This filter is documented in wp-includes/class-wp-image-editor.php */
       
  4068 		$output_format = apply_filters( 'image_editor_output_format', array(), $path, $args['mime_type'] );
       
  4069 		if ( isset( $output_format[ $args['mime_type'] ] ) ) {
       
  4070 			$args['output_mime_type'] = $output_format[ $args['mime_type'] ];
  3775 		}
  4071 		}
  3776 	}
  4072 	}
  3777 
  4073 
  3778 	$implementation = _wp_image_editor_choose( $args );
  4074 	$implementation = _wp_image_editor_choose( $args );
  3779 
  4075 
  3816  */
  4112  */
  3817 function _wp_image_editor_choose( $args = array() ) {
  4113 function _wp_image_editor_choose( $args = array() ) {
  3818 	require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
  4114 	require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
  3819 	require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
  4115 	require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
  3820 	require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
  4116 	require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
       
  4117 	require_once ABSPATH . WPINC . '/class-avif-info.php';
  3821 	/**
  4118 	/**
  3822 	 * Filters the list of image editing library classes.
  4119 	 * Filters the list of image editing library classes.
  3823 	 *
  4120 	 *
  3824 	 * @since 3.5.0
  4121 	 * @since 3.5.0
  3825 	 *
  4122 	 *
  3826 	 * @param string[] $image_editors Array of available image editor class names. Defaults are
  4123 	 * @param string[] $image_editors Array of available image editor class names. Defaults are
  3827 	 *                                'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD'.
  4124 	 *                                'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD'.
  3828 	 */
  4125 	 */
  3829 	$implementations = apply_filters( 'wp_image_editors', array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
  4126 	$implementations = apply_filters( 'wp_image_editors', array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
       
  4127 	$supports_input  = false;
  3830 
  4128 
  3831 	foreach ( $implementations as $implementation ) {
  4129 	foreach ( $implementations as $implementation ) {
  3832 		if ( ! call_user_func( array( $implementation, 'test' ), $args ) ) {
  4130 		if ( ! call_user_func( array( $implementation, 'test' ), $args ) ) {
  3833 			continue;
  4131 			continue;
  3834 		}
  4132 		}
  3835 
  4133 
       
  4134 		// Implementation should support the passed mime type.
  3836 		if ( isset( $args['mime_type'] ) &&
  4135 		if ( isset( $args['mime_type'] ) &&
  3837 			! call_user_func(
  4136 			! call_user_func(
  3838 				array( $implementation, 'supports_mime_type' ),
  4137 				array( $implementation, 'supports_mime_type' ),
  3839 				$args['mime_type']
  4138 				$args['mime_type']
  3840 			) ) {
  4139 			) ) {
  3841 			continue;
  4140 			continue;
  3842 		}
  4141 		}
  3843 
  4142 
       
  4143 		// Implementation should support requested methods.
  3844 		if ( isset( $args['methods'] ) &&
  4144 		if ( isset( $args['methods'] ) &&
  3845 			array_diff( $args['methods'], get_class_methods( $implementation ) ) ) {
  4145 			array_diff( $args['methods'], get_class_methods( $implementation ) ) ) {
  3846 
  4146 
  3847 			continue;
  4147 			continue;
  3848 		}
  4148 		}
  3849 
  4149 
       
  4150 		// Implementation should ideally support the output mime type as well if set and different than the passed type.
       
  4151 		if (
       
  4152 			isset( $args['mime_type'] ) &&
       
  4153 			isset( $args['output_mime_type'] ) &&
       
  4154 			$args['mime_type'] !== $args['output_mime_type'] &&
       
  4155 			! call_user_func( array( $implementation, 'supports_mime_type' ), $args['output_mime_type'] )
       
  4156 		) {
       
  4157 			/*
       
  4158 			 * This implementation supports the input type but not the output type.
       
  4159 			 * Keep looking to see if we can find an implementation that supports both.
       
  4160 			 */
       
  4161 			$supports_input = $implementation;
       
  4162 			continue;
       
  4163 		}
       
  4164 
       
  4165 		// Favor the implementation that supports both input and output mime types.
  3850 		return $implementation;
  4166 		return $implementation;
  3851 	}
  4167 	}
  3852 
  4168 
  3853 	return false;
  4169 	return $supports_input;
  3854 }
  4170 }
  3855 
  4171 
  3856 /**
  4172 /**
  3857  * Prints default Plupload arguments.
  4173  * Prints default Plupload arguments.
  3858  *
  4174  *
  3860  */
  4176  */
  3861 function wp_plupload_default_settings() {
  4177 function wp_plupload_default_settings() {
  3862 	$wp_scripts = wp_scripts();
  4178 	$wp_scripts = wp_scripts();
  3863 
  4179 
  3864 	$data = $wp_scripts->get_data( 'wp-plupload', 'data' );
  4180 	$data = $wp_scripts->get_data( 'wp-plupload', 'data' );
  3865 	if ( $data && false !== strpos( $data, '_wpPluploadSettings' ) ) {
  4181 	if ( $data && str_contains( $data, '_wpPluploadSettings' ) ) {
  3866 		return;
  4182 		return;
  3867 	}
  4183 	}
  3868 
  4184 
  3869 	$max_upload_size    = wp_max_upload_size();
  4185 	$max_upload_size    = wp_max_upload_size();
  3870 	$allowed_extensions = array_keys( get_allowed_mime_types() );
  4186 	$allowed_extensions = array_keys( get_allowed_mime_types() );
  3889 	/*
  4205 	/*
  3890 	 * Currently only iOS Safari supports multiple files uploading,
  4206 	 * Currently only iOS Safari supports multiple files uploading,
  3891 	 * but iOS 7.x has a bug that prevents uploading of videos when enabled.
  4207 	 * but iOS 7.x has a bug that prevents uploading of videos when enabled.
  3892 	 * See #29602.
  4208 	 * See #29602.
  3893 	 */
  4209 	 */
  3894 	if ( wp_is_mobile() && strpos( $_SERVER['HTTP_USER_AGENT'], 'OS 7_' ) !== false &&
  4210 	if ( wp_is_mobile()
  3895 		strpos( $_SERVER['HTTP_USER_AGENT'], 'like Mac OS X' ) !== false ) {
  4211 		&& str_contains( $_SERVER['HTTP_USER_AGENT'], 'OS 7_' )
  3896 
  4212 		&& str_contains( $_SERVER['HTTP_USER_AGENT'], 'like Mac OS X' )
       
  4213 	) {
  3897 		$defaults['multi_selection'] = false;
  4214 		$defaults['multi_selection'] = false;
  3898 	}
  4215 	}
  3899 
  4216 
  3900 	// Check if WebP images can be edited.
  4217 	// Check if WebP images can be edited.
  3901 	if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
  4218 	if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
  3902 		$defaults['webp_upload_error'] = true;
  4219 		$defaults['webp_upload_error'] = true;
       
  4220 	}
       
  4221 
       
  4222 	// Check if AVIF images can be edited.
       
  4223 	if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) {
       
  4224 		$defaults['avif_upload_error'] = true;
  3903 	}
  4225 	}
  3904 
  4226 
  3905 	/**
  4227 	/**
  3906 	 * Filters the Plupload default settings.
  4228 	 * Filters the Plupload default settings.
  3907 	 *
  4229 	 *
  4005 	if ( 'attachment' !== $attachment->post_type ) {
  4327 	if ( 'attachment' !== $attachment->post_type ) {
  4006 		return;
  4328 		return;
  4007 	}
  4329 	}
  4008 
  4330 
  4009 	$meta = wp_get_attachment_metadata( $attachment->ID );
  4331 	$meta = wp_get_attachment_metadata( $attachment->ID );
  4010 	if ( false !== strpos( $attachment->post_mime_type, '/' ) ) {
  4332 	if ( str_contains( $attachment->post_mime_type, '/' ) ) {
  4011 		list( $type, $subtype ) = explode( '/', $attachment->post_mime_type );
  4333 		list( $type, $subtype ) = explode( '/', $attachment->post_mime_type );
  4012 	} else {
  4334 	} else {
  4013 		list( $type, $subtype ) = array( $attachment->post_mime_type, '' );
  4335 		list( $type, $subtype ) = array( $attachment->post_mime_type, '' );
  4014 	}
  4336 	}
  4015 
  4337 
  4033 		'modified'      => strtotime( $attachment->post_modified_gmt ) * 1000,
  4355 		'modified'      => strtotime( $attachment->post_modified_gmt ) * 1000,
  4034 		'menuOrder'     => $attachment->menu_order,
  4356 		'menuOrder'     => $attachment->menu_order,
  4035 		'mime'          => $attachment->post_mime_type,
  4357 		'mime'          => $attachment->post_mime_type,
  4036 		'type'          => $type,
  4358 		'type'          => $type,
  4037 		'subtype'       => $subtype,
  4359 		'subtype'       => $subtype,
  4038 		'icon'          => wp_mime_type_icon( $attachment->ID ),
  4360 		'icon'          => wp_mime_type_icon( $attachment->ID, '.svg' ),
  4039 		'dateFormatted' => mysql2date( __( 'F j, Y' ), $attachment->post_date ),
  4361 		'dateFormatted' => mysql2date( __( 'F j, Y' ), $attachment->post_date ),
  4040 		'nonces'        => array(
  4362 		'nonces'        => array(
  4041 			'update' => false,
  4363 			'update' => false,
  4042 			'delete' => false,
  4364 			'delete' => false,
  4043 			'edit'   => false,
  4365 			'edit'   => false,
  4131 				);
  4453 				);
  4132 			} elseif ( isset( $meta['sizes'][ $size ] ) ) {
  4454 			} elseif ( isset( $meta['sizes'][ $size ] ) ) {
  4133 				// Nothing from the filter, so consult image metadata if we have it.
  4455 				// Nothing from the filter, so consult image metadata if we have it.
  4134 				$size_meta = $meta['sizes'][ $size ];
  4456 				$size_meta = $meta['sizes'][ $size ];
  4135 
  4457 
  4136 				// We have the actual image size, but might need to further constrain it if content_width is narrower.
  4458 				/*
  4137 				// Thumbnail, medium, and full sizes are also checked against the site's height/width options.
  4459 				 * We have the actual image size, but might need to further constrain it if content_width is narrower.
       
  4460 				 * Thumbnail, medium, and full sizes are also checked against the site's height/width options.
       
  4461 				 */
  4138 				list( $width, $height ) = image_constrain_size_for_editor( $size_meta['width'], $size_meta['height'], $size, 'edit' );
  4462 				list( $width, $height ) = image_constrain_size_for_editor( $size_meta['width'], $size_meta['height'], $size, 'edit' );
  4139 
  4463 
  4140 				$sizes[ $size ] = array(
  4464 				$sizes[ $size ] = array(
  4141 					'height'      => $height,
  4465 					'height'      => $height,
  4142 					'width'       => $width,
  4466 					'width'       => $width,
  4202 			list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'full' );
  4526 			list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'full' );
  4203 			$response['image']            = compact( 'src', 'width', 'height' );
  4527 			$response['image']            = compact( 'src', 'width', 'height' );
  4204 			list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'thumbnail' );
  4528 			list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'thumbnail' );
  4205 			$response['thumb']            = compact( 'src', 'width', 'height' );
  4529 			$response['thumb']            = compact( 'src', 'width', 'height' );
  4206 		} else {
  4530 		} else {
  4207 			$src               = wp_mime_type_icon( $attachment->ID );
  4531 			$src               = wp_mime_type_icon( $attachment->ID, '.svg' );
  4208 			$width             = 48;
  4532 			$width             = 48;
  4209 			$height            = 64;
  4533 			$height            = 64;
  4210 			$response['image'] = compact( 'src', 'width', 'height' );
  4534 			$response['image'] = compact( 'src', 'width', 'height' );
  4211 			$response['thumb'] = compact( 'src', 'width', 'height' );
  4535 			$response['thumb'] = compact( 'src', 'width', 'height' );
  4212 		}
  4536 		}
  4226 	/**
  4550 	/**
  4227 	 * Filters the attachment data prepared for JavaScript.
  4551 	 * Filters the attachment data prepared for JavaScript.
  4228 	 *
  4552 	 *
  4229 	 * @since 3.5.0
  4553 	 * @since 3.5.0
  4230 	 *
  4554 	 *
  4231 	 * @param array       $response   Array of prepared attachment data. @see wp_prepare_attachment_for_js().
  4555 	 * @param array       $response   Array of prepared attachment data. See {@see wp_prepare_attachment_for_js()}.
  4232 	 * @param WP_Post     $attachment Attachment object.
  4556 	 * @param WP_Post     $attachment Attachment object.
  4233 	 * @param array|false $meta       Array of attachment meta data, or false if there is none.
  4557 	 * @param array|false $meta       Array of attachment meta data, or false if there is none.
  4234 	 */
  4558 	 */
  4235 	return apply_filters( 'wp_prepare_attachment_for_js', $response, $attachment, $meta );
  4559 	return apply_filters( 'wp_prepare_attachment_for_js', $response, $attachment, $meta );
  4236 }
  4560 }
  4246  * @global WP_Locale $wp_locale     WordPress date and time locale object.
  4570  * @global WP_Locale $wp_locale     WordPress date and time locale object.
  4247  *
  4571  *
  4248  * @param array $args {
  4572  * @param array $args {
  4249  *     Arguments for enqueuing media scripts.
  4573  *     Arguments for enqueuing media scripts.
  4250  *
  4574  *
  4251  *     @type int|WP_Post $post A post object or ID.
  4575  *     @type int|WP_Post $post Post ID or post object.
  4252  * }
  4576  * }
  4253  */
  4577  */
  4254 function wp_enqueue_media( $args = array() ) {
  4578 function wp_enqueue_media( $args = array() ) {
  4255 	// Enqueue me just once per page, please.
  4579 	// Enqueue me just once per page, please.
  4256 	if ( did_action( 'wp_enqueue_media' ) ) {
  4580 	if ( did_action( 'wp_enqueue_media' ) ) {
  4262 	$defaults = array(
  4586 	$defaults = array(
  4263 		'post' => null,
  4587 		'post' => null,
  4264 	);
  4588 	);
  4265 	$args     = wp_parse_args( $args, $defaults );
  4589 	$args     = wp_parse_args( $args, $defaults );
  4266 
  4590 
  4267 	// We're going to pass the old thickbox media tabs to `media_upload_tabs`
  4591 	/*
  4268 	// to ensure plugins will work. We will then unset those tabs.
  4592 	 * We're going to pass the old thickbox media tabs to `media_upload_tabs`
       
  4593 	 * to ensure plugins will work. We will then unset those tabs.
       
  4594 	 */
  4269 	$tabs = array(
  4595 	$tabs = array(
  4270 		// handler action suffix => tab label
  4596 		// handler action suffix => tab label
  4271 		'type'     => '',
  4597 		'type'     => '',
  4272 		'type_url' => '',
  4598 		'type_url' => '',
  4273 		'gallery'  => '',
  4599 		'gallery'  => '',
  4314 	 *                        on whether any audio files exist in the media library.
  4640 	 *                        on whether any audio files exist in the media library.
  4315 	 */
  4641 	 */
  4316 	$show_audio_playlist = apply_filters( 'media_library_show_audio_playlist', true );
  4642 	$show_audio_playlist = apply_filters( 'media_library_show_audio_playlist', true );
  4317 	if ( null === $show_audio_playlist ) {
  4643 	if ( null === $show_audio_playlist ) {
  4318 		$show_audio_playlist = $wpdb->get_var(
  4644 		$show_audio_playlist = $wpdb->get_var(
  4319 			"
  4645 			"SELECT ID
  4320 			SELECT ID
       
  4321 			FROM $wpdb->posts
  4646 			FROM $wpdb->posts
  4322 			WHERE post_type = 'attachment'
  4647 			WHERE post_type = 'attachment'
  4323 			AND post_mime_type LIKE 'audio%'
  4648 			AND post_mime_type LIKE 'audio%'
  4324 			LIMIT 1
  4649 			LIMIT 1"
  4325 		"
       
  4326 		);
  4650 		);
  4327 	}
  4651 	}
  4328 
  4652 
  4329 	/**
  4653 	/**
  4330 	 * Allows showing or hiding the "Create Video Playlist" button in the media library.
  4654 	 * Allows showing or hiding the "Create Video Playlist" button in the media library.
  4344 	 *                        on whether any video files exist in the media library.
  4668 	 *                        on whether any video files exist in the media library.
  4345 	 */
  4669 	 */
  4346 	$show_video_playlist = apply_filters( 'media_library_show_video_playlist', true );
  4670 	$show_video_playlist = apply_filters( 'media_library_show_video_playlist', true );
  4347 	if ( null === $show_video_playlist ) {
  4671 	if ( null === $show_video_playlist ) {
  4348 		$show_video_playlist = $wpdb->get_var(
  4672 		$show_video_playlist = $wpdb->get_var(
  4349 			"
  4673 			"SELECT ID
  4350 			SELECT ID
       
  4351 			FROM $wpdb->posts
  4674 			FROM $wpdb->posts
  4352 			WHERE post_type = 'attachment'
  4675 			WHERE post_type = 'attachment'
  4353 			AND post_mime_type LIKE 'video%'
  4676 			AND post_mime_type LIKE 'video%'
  4354 			LIMIT 1
  4677 			LIMIT 1"
  4355 		"
       
  4356 		);
  4678 		);
  4357 	}
  4679 	}
  4358 
  4680 
  4359 	/**
  4681 	/**
  4360 	 * Allows overriding the list of months displayed in the media library.
  4682 	 * Allows overriding the list of months displayed in the media library.
  4366 	 *
  4688 	 *
  4367 	 * @since 4.7.4
  4689 	 * @since 4.7.4
  4368 	 *
  4690 	 *
  4369 	 * @link https://core.trac.wordpress.org/ticket/31071
  4691 	 * @link https://core.trac.wordpress.org/ticket/31071
  4370 	 *
  4692 	 *
  4371 	 * @param array|null $months An array of objects with `month` and `year`
  4693 	 * @param stdClass[]|null $months An array of objects with `month` and `year`
  4372 	 *                           properties, or `null` (or any other non-array value)
  4694 	 *                                properties, or `null` for default behavior.
  4373 	 *                           for default behavior.
       
  4374 	 */
  4695 	 */
  4375 	$months = apply_filters( 'media_library_months_with_files', null );
  4696 	$months = apply_filters( 'media_library_months_with_files', null );
  4376 	if ( ! is_array( $months ) ) {
  4697 	if ( ! is_array( $months ) ) {
  4377 		$months = $wpdb->get_results(
  4698 		$months = $wpdb->get_results(
  4378 			$wpdb->prepare(
  4699 			$wpdb->prepare(
  4379 				"
  4700 				"SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
  4380 			SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
  4701 				FROM $wpdb->posts
  4381 			FROM $wpdb->posts
  4702 				WHERE post_type = %s
  4382 			WHERE post_type = %s
  4703 				ORDER BY post_date DESC",
  4383 			ORDER BY post_date DESC
       
  4384 		",
       
  4385 				'attachment'
  4704 				'attachment'
  4386 			)
  4705 			)
  4387 		);
  4706 		);
  4388 	}
  4707 	}
  4389 	foreach ( $months as $month_year ) {
  4708 	foreach ( $months as $month_year ) {
  4409 		'tabUrl'            => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),
  4728 		'tabUrl'            => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),
  4410 		'mimeTypes'         => wp_list_pluck( get_post_mime_types(), 0 ),
  4729 		'mimeTypes'         => wp_list_pluck( get_post_mime_types(), 0 ),
  4411 		/** This filter is documented in wp-admin/includes/media.php */
  4730 		/** This filter is documented in wp-admin/includes/media.php */
  4412 		'captions'          => ! apply_filters( 'disable_captions', '' ),
  4731 		'captions'          => ! apply_filters( 'disable_captions', '' ),
  4413 		'nonce'             => array(
  4732 		'nonce'             => array(
  4414 			'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ),
  4733 			'sendToEditor'           => wp_create_nonce( 'media-send-to-editor' ),
       
  4734 			'setAttachmentThumbnail' => wp_create_nonce( 'set-attachment-thumbnail' ),
  4415 		),
  4735 		),
  4416 		'post'              => array(
  4736 		'post'              => array(
  4417 			'id' => 0,
  4737 			'id' => 0,
  4418 		),
  4738 		),
  4419 		'defaultProps'      => $props,
  4739 		'defaultProps'      => $props,
  4507 		'deletePermanently'           => __( 'Delete permanently' ),
  4827 		'deletePermanently'           => __( 'Delete permanently' ),
  4508 		'errorDeleting'               => __( 'Error in deleting the attachment.' ),
  4828 		'errorDeleting'               => __( 'Error in deleting the attachment.' ),
  4509 		'apply'                       => __( 'Apply' ),
  4829 		'apply'                       => __( 'Apply' ),
  4510 		'filterByDate'                => __( 'Filter by date' ),
  4830 		'filterByDate'                => __( 'Filter by date' ),
  4511 		'filterByType'                => __( 'Filter by type' ),
  4831 		'filterByType'                => __( 'Filter by type' ),
  4512 		'searchLabel'                 => __( 'Search' ),
  4832 		'searchLabel'                 => __( 'Search media' ),
  4513 		'searchMediaLabel'            => __( 'Search media' ),          // Backward compatibility pre-5.3.
  4833 		'searchMediaLabel'            => __( 'Search media' ),          // Backward compatibility pre-5.3.
  4514 		'searchMediaPlaceholder'      => __( 'Search media items...' ), // Placeholder (no ellipsis), backward compatibility pre-5.3.
  4834 		'searchMediaPlaceholder'      => __( 'Search media items...' ), // Placeholder (no ellipsis), backward compatibility pre-5.3.
  4515 		/* translators: %d: Number of attachments found in a search. */
  4835 		/* translators: %d: Number of attachments found in a search. */
  4516 		'mediaFound'                  => __( 'Number of media items found: %d' ),
  4836 		'mediaFound'                  => __( 'Number of media items found: %d' ),
  4517 		'noMedia'                     => __( 'No media items found.' ),
  4837 		'noMedia'                     => __( 'No media items found.' ),
  4613 	 */
  4933 	 */
  4614 	$strings = apply_filters( 'media_view_strings', $strings, $post );
  4934 	$strings = apply_filters( 'media_view_strings', $strings, $post );
  4615 
  4935 
  4616 	$strings['settings'] = $settings;
  4936 	$strings['settings'] = $settings;
  4617 
  4937 
  4618 	// Ensure we enqueue media-editor first, that way media-views
  4938 	/*
  4619 	// is registered internally before we try to localize it. See #24724.
  4939 	 * Ensure we enqueue media-editor first, that way media-views
       
  4940 	 * is registered internally before we try to localize it. See #24724.
       
  4941 	 */
  4620 	wp_enqueue_script( 'media-editor' );
  4942 	wp_enqueue_script( 'media-editor' );
  4621 	wp_localize_script( 'media-views', '_wpMediaViewsL10n', $strings );
  4943 	wp_localize_script( 'media-views', '_wpMediaViewsL10n', $strings );
  4622 
  4944 
  4623 	wp_enqueue_script( 'media-audiovideo' );
  4945 	wp_enqueue_script( 'media-audiovideo' );
  4624 	wp_enqueue_style( 'media-views' );
  4946 	wp_enqueue_style( 'media-views' );
  4691 	 */
  5013 	 */
  4692 	return (array) apply_filters( 'get_attached_media', $children, $type, $post );
  5014 	return (array) apply_filters( 'get_attached_media', $children, $type, $post );
  4693 }
  5015 }
  4694 
  5016 
  4695 /**
  5017 /**
  4696  * Check the content HTML for a audio, video, object, embed, or iframe tags.
  5018  * Checks the HTML content for an audio, video, object, embed, or iframe tags.
  4697  *
  5019  *
  4698  * @since 3.6.0
  5020  * @since 3.6.0
  4699  *
  5021  *
  4700  * @param string   $content A string of HTML which might contain media elements.
  5022  * @param string   $content A string of HTML which might contain media elements.
  4701  * @param string[] $types   An array of media types: 'audio', 'video', 'object', 'embed', or 'iframe'.
  5023  * @param string[] $types   An array of media types: 'audio', 'video', 'object', 'embed', or 'iframe'.
  4759 		foreach ( $matches as $shortcode ) {
  5081 		foreach ( $matches as $shortcode ) {
  4760 			if ( 'gallery' === $shortcode[2] ) {
  5082 			if ( 'gallery' === $shortcode[2] ) {
  4761 				$srcs = array();
  5083 				$srcs = array();
  4762 
  5084 
  4763 				$shortcode_attrs = shortcode_parse_atts( $shortcode[3] );
  5085 				$shortcode_attrs = shortcode_parse_atts( $shortcode[3] );
  4764 				if ( ! is_array( $shortcode_attrs ) ) {
       
  4765 					$shortcode_attrs = array();
       
  4766 				}
       
  4767 
  5086 
  4768 				// Specify the post ID of the gallery we're viewing if the shortcode doesn't reference another post already.
  5087 				// Specify the post ID of the gallery we're viewing if the shortcode doesn't reference another post already.
  4769 				if ( ! isset( $shortcode_attrs['id'] ) ) {
  5088 				if ( ! isset( $shortcode_attrs['id'] ) ) {
  4770 					$shortcode[3] .= ' id="' . (int) $post->ID . '"';
  5089 					$shortcode[3] .= ' id="' . (int) $post->ID . '"';
  4771 				}
  5090 				}
  4894 	 */
  5213 	 */
  4895 	return apply_filters( 'get_post_galleries', $galleries, $post );
  5214 	return apply_filters( 'get_post_galleries', $galleries, $post );
  4896 }
  5215 }
  4897 
  5216 
  4898 /**
  5217 /**
  4899  * Check a specified post's content for gallery and, if present, return the first
  5218  * Checks a specified post's content for gallery and, if present, return the first
  4900  *
  5219  *
  4901  * @since 3.6.0
  5220  * @since 3.6.0
  4902  *
  5221  *
  4903  * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
  5222  * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
  4904  * @param bool        $html Optional. Whether to return HTML or data. Default is true.
  5223  * @param bool        $html Optional. Whether to return HTML or data. Default is true.
  4919 	 */
  5238 	 */
  4920 	return apply_filters( 'get_post_gallery', $gallery, $post, $galleries );
  5239 	return apply_filters( 'get_post_gallery', $gallery, $post, $galleries );
  4921 }
  5240 }
  4922 
  5241 
  4923 /**
  5242 /**
  4924  * Retrieve the image srcs from galleries from a post's content, if present
  5243  * Retrieves the image srcs from galleries from a post's content, if present.
  4925  *
  5244  *
  4926  * @since 3.6.0
  5245  * @since 3.6.0
  4927  *
  5246  *
  4928  * @see get_post_galleries()
  5247  * @see get_post_galleries()
  4929  *
  5248  *
  4935 	$galleries = get_post_galleries( $post, false );
  5254 	$galleries = get_post_galleries( $post, false );
  4936 	return wp_list_pluck( $galleries, 'src' );
  5255 	return wp_list_pluck( $galleries, 'src' );
  4937 }
  5256 }
  4938 
  5257 
  4939 /**
  5258 /**
  4940  * Checks a post's content for galleries and return the image srcs for the first found gallery
  5259  * Checks a post's content for galleries and return the image srcs for the first found gallery.
  4941  *
  5260  *
  4942  * @since 3.6.0
  5261  * @since 3.6.0
  4943  *
  5262  *
  4944  * @see get_post_gallery()
  5263  * @see get_post_gallery()
  4945  *
  5264  *
  5001 	// Force the protocols to match if needed.
  5320 	// Force the protocols to match if needed.
  5002 	if ( isset( $image_path['scheme'] ) && ( $image_path['scheme'] !== $site_url['scheme'] ) ) {
  5321 	if ( isset( $image_path['scheme'] ) && ( $image_path['scheme'] !== $site_url['scheme'] ) ) {
  5003 		$path = str_replace( $image_path['scheme'], $site_url['scheme'], $path );
  5322 		$path = str_replace( $image_path['scheme'], $site_url['scheme'], $path );
  5004 	}
  5323 	}
  5005 
  5324 
  5006 	if ( 0 === strpos( $path, $dir['baseurl'] . '/' ) ) {
  5325 	if ( str_starts_with( $path, $dir['baseurl'] . '/' ) ) {
  5007 		$path = substr( $path, strlen( $dir['baseurl'] . '/' ) );
  5326 		$path = substr( $path, strlen( $dir['baseurl'] . '/' ) );
  5008 	}
  5327 	}
  5009 
  5328 
  5010 	$sql = $wpdb->prepare(
  5329 	$sql = $wpdb->prepare(
  5011 		"SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
  5330 		"SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
  5074  * Finds and exports attachments associated with an email address.
  5393  * Finds and exports attachments associated with an email address.
  5075  *
  5394  *
  5076  * @since 4.9.6
  5395  * @since 4.9.6
  5077  *
  5396  *
  5078  * @param string $email_address The attachment owner email address.
  5397  * @param string $email_address The attachment owner email address.
  5079  * @param int    $page          Attachment page.
  5398  * @param int    $page          Attachment page number.
  5080  * @return array An array of personal data.
  5399  * @return array {
       
  5400  *     An array of personal data.
       
  5401  *
       
  5402  *     @type array[] $data An array of personal data arrays.
       
  5403  *     @type bool    $done Whether the exporter is finished.
       
  5404  * }
  5081  */
  5405  */
  5082 function wp_media_personal_data_exporter( $email_address, $page = 1 ) {
  5406 function wp_media_personal_data_exporter( $email_address, $page = 1 ) {
  5083 	// Limit us to 50 attachments at a time to avoid timing out.
  5407 	// Limit us to 50 attachments at a time to avoid timing out.
  5084 	$number = 50;
  5408 	$number = 50;
  5085 	$page   = (int) $page;
  5409 	$page   = (int) $page;
  5134 		'done' => $done,
  5458 		'done' => $done,
  5135 	);
  5459 	);
  5136 }
  5460 }
  5137 
  5461 
  5138 /**
  5462 /**
  5139  * Add additional default image sub-sizes.
  5463  * Adds additional default image sub-sizes.
  5140  *
  5464  *
  5141  * These sizes are meant to enhance the way WordPress displays images on the front-end on larger,
  5465  * These sizes are meant to enhance the way WordPress displays images on the front-end on larger,
  5142  * high-density devices. They make it possible to generate more suitable `srcset` and `sizes` attributes
  5466  * high-density devices. They make it possible to generate more suitable `srcset` and `sizes` attributes
  5143  * when the users upload large images.
  5467  * when the users upload large images.
  5144  *
  5468  *
  5171 /**
  5495 /**
  5172  * Allows PHP's getimagesize() to be debuggable when necessary.
  5496  * Allows PHP's getimagesize() to be debuggable when necessary.
  5173  *
  5497  *
  5174  * @since 5.7.0
  5498  * @since 5.7.0
  5175  * @since 5.8.0 Added support for WebP images.
  5499  * @since 5.8.0 Added support for WebP images.
       
  5500  * @since 6.5.0 Added support for AVIF images.
  5176  *
  5501  *
  5177  * @param string $filename   The file path.
  5502  * @param string $filename   The file path.
  5178  * @param array  $image_info Optional. Extended image information (passed by reference).
  5503  * @param array  $image_info Optional. Extended image information (passed by reference).
  5179  * @return array|false Array of image information or false on failure.
  5504  * @return array|false Array of image information or false on failure.
  5180  */
  5505  */
  5181 function wp_getimagesize( $filename, array &$image_info = null ) {
  5506 function wp_getimagesize( $filename, ?array &$image_info = null ) {
  5182 	// Don't silence errors when in debug mode, unless running unit tests.
  5507 	// Don't silence errors when in debug mode, unless running unit tests.
  5183 	if ( defined( 'WP_DEBUG' ) && WP_DEBUG
  5508 	if ( defined( 'WP_DEBUG' ) && WP_DEBUG
  5184 		&& ! defined( 'WP_RUN_CORE_TESTS' )
  5509 		&& ! defined( 'WP_RUN_CORE_TESTS' )
  5185 	) {
  5510 	) {
  5186 		if ( 2 === func_num_args() ) {
  5511 		if ( 2 === func_num_args() ) {
  5197 		 * even when it's able to provide image size information.
  5522 		 * even when it's able to provide image size information.
  5198 		 *
  5523 		 *
  5199 		 * See https://core.trac.wordpress.org/ticket/42480
  5524 		 * See https://core.trac.wordpress.org/ticket/42480
  5200 		 */
  5525 		 */
  5201 		if ( 2 === func_num_args() ) {
  5526 		if ( 2 === func_num_args() ) {
  5202 			// phpcs:ignore WordPress.PHP.NoSilencedErrors
       
  5203 			$info = @getimagesize( $filename, $image_info );
  5527 			$info = @getimagesize( $filename, $image_info );
  5204 		} else {
  5528 		} else {
  5205 			// phpcs:ignore WordPress.PHP.NoSilencedErrors
       
  5206 			$info = @getimagesize( $filename );
  5529 			$info = @getimagesize( $filename );
  5207 		}
  5530 		}
  5208 	}
  5531 	}
  5209 
  5532 
  5210 	if ( false !== $info ) {
  5533 	if (
       
  5534 		! empty( $info ) &&
       
  5535 		// Some PHP versions return 0x0 sizes from `getimagesize` for unrecognized image formats, including AVIFs.
       
  5536 		! ( empty( $info[0] ) && empty( $info[1] ) )
       
  5537 	) {
  5211 		return $info;
  5538 		return $info;
  5212 	}
  5539 	}
  5213 
  5540 
  5214 	// For PHP versions that don't support WebP images,
  5541 	/*
  5215 	// extract the image size info from the file headers.
  5542 	 * For PHP versions that don't support WebP images,
       
  5543 	 * extract the image size info from the file headers.
       
  5544 	 */
  5216 	if ( 'image/webp' === wp_get_image_mime( $filename ) ) {
  5545 	if ( 'image/webp' === wp_get_image_mime( $filename ) ) {
  5217 		$webp_info = wp_get_webp_info( $filename );
  5546 		$webp_info = wp_get_webp_info( $filename );
  5218 		$width     = $webp_info['width'];
  5547 		$width     = $webp_info['width'];
  5219 		$height    = $webp_info['height'];
  5548 		$height    = $webp_info['height'];
  5220 
  5549 
  5232 				'mime' => 'image/webp',
  5561 				'mime' => 'image/webp',
  5233 			);
  5562 			);
  5234 		}
  5563 		}
  5235 	}
  5564 	}
  5236 
  5565 
       
  5566 	// For PHP versions that don't support AVIF images, extract the image size info from the file headers.
       
  5567 	if ( 'image/avif' === wp_get_image_mime( $filename ) ) {
       
  5568 		$avif_info = wp_get_avif_info( $filename );
       
  5569 
       
  5570 		$width  = $avif_info['width'];
       
  5571 		$height = $avif_info['height'];
       
  5572 
       
  5573 		// Mimic the native return format.
       
  5574 		if ( $width && $height ) {
       
  5575 			return array(
       
  5576 				$width,
       
  5577 				$height,
       
  5578 				IMAGETYPE_AVIF,
       
  5579 				sprintf(
       
  5580 					'width="%d" height="%d"',
       
  5581 					$width,
       
  5582 					$height
       
  5583 				),
       
  5584 				'mime' => 'image/avif',
       
  5585 			);
       
  5586 		}
       
  5587 	}
       
  5588 
  5237 	// The image could not be parsed.
  5589 	// The image could not be parsed.
  5238 	return false;
  5590 	return false;
       
  5591 }
       
  5592 
       
  5593 /**
       
  5594  * Extracts meta information about an AVIF file: width, height, bit depth, and number of channels.
       
  5595  *
       
  5596  * @since 6.5.0
       
  5597  *
       
  5598  * @param string $filename Path to an AVIF file.
       
  5599  * @return array {
       
  5600  *     An array of AVIF image information.
       
  5601  *
       
  5602  *     @type int|false $width        Image width on success, false on failure.
       
  5603  *     @type int|false $height       Image height on success, false on failure.
       
  5604  *     @type int|false $bit_depth    Image bit depth on success, false on failure.
       
  5605  *     @type int|false $num_channels Image number of channels on success, false on failure.
       
  5606  * }
       
  5607  */
       
  5608 function wp_get_avif_info( $filename ) {
       
  5609 	$results = array(
       
  5610 		'width'        => false,
       
  5611 		'height'       => false,
       
  5612 		'bit_depth'    => false,
       
  5613 		'num_channels' => false,
       
  5614 	);
       
  5615 
       
  5616 	if ( 'image/avif' !== wp_get_image_mime( $filename ) ) {
       
  5617 		return $results;
       
  5618 	}
       
  5619 
       
  5620 	// Parse the file using libavifinfo's PHP implementation.
       
  5621 	require_once ABSPATH . WPINC . '/class-avif-info.php';
       
  5622 
       
  5623 	$handle = fopen( $filename, 'rb' );
       
  5624 	if ( $handle ) {
       
  5625 		$parser  = new Avifinfo\Parser( $handle );
       
  5626 		$success = $parser->parse_ftyp() && $parser->parse_file();
       
  5627 		fclose( $handle );
       
  5628 		if ( $success ) {
       
  5629 			$results = $parser->features->primary_item_features;
       
  5630 		}
       
  5631 	}
       
  5632 	return $results;
  5239 }
  5633 }
  5240 
  5634 
  5241 /**
  5635 /**
  5242  * Extracts meta information about a WebP file: width, height, and type.
  5636  * Extracts meta information about a WebP file: width, height, and type.
  5243  *
  5637  *
  5271 	// Make sure we got enough bytes.
  5665 	// Make sure we got enough bytes.
  5272 	if ( strlen( $magic ) < 40 ) {
  5666 	if ( strlen( $magic ) < 40 ) {
  5273 		return compact( 'width', 'height', 'type' );
  5667 		return compact( 'width', 'height', 'type' );
  5274 	}
  5668 	}
  5275 
  5669 
  5276 	// The headers are a little different for each of the three formats.
  5670 	/*
  5277 	// Header values based on WebP docs, see https://developers.google.com/speed/webp/docs/riff_container.
  5671 	 * The headers are a little different for each of the three formats.
       
  5672 	 * Header values based on WebP docs, see https://developers.google.com/speed/webp/docs/riff_container.
       
  5673 	 */
  5278 	switch ( substr( $magic, 12, 4 ) ) {
  5674 	switch ( substr( $magic, 12, 4 ) ) {
  5279 		// Lossy WebP.
  5675 		// Lossy WebP.
  5280 		case 'VP8 ':
  5676 		case 'VP8 ':
  5281 			$parts  = unpack( 'v2', substr( $magic, 26, 4 ) );
  5677 			$parts  = unpack( 'v2', substr( $magic, 26, 4 ) );
  5282 			$width  = (int) ( $parts[1] & 0x3FFF );
  5678 			$width  = (int) ( $parts[1] & 0x3FFF );
  5304 
  5700 
  5305 	return compact( 'width', 'height', 'type' );
  5701 	return compact( 'width', 'height', 'type' );
  5306 }
  5702 }
  5307 
  5703 
  5308 /**
  5704 /**
  5309  * Gets the default value to use for a `loading` attribute on an element.
  5705  * Gets loading optimization attributes.
  5310  *
  5706  *
  5311  * This function should only be called for a tag and context if lazy-loading is generally enabled.
  5707  * This function returns an array of attributes that should be merged into the given attributes array to optimize
  5312  *
  5708  * loading performance. Potential attributes returned by this function are:
  5313  * The function usually returns 'lazy', but uses certain heuristics to guess whether the current element is likely to
  5709  * - `loading` attribute with a value of "lazy"
  5314  * appear above the fold, in which case it returns a boolean `false`, which will lead to the `loading` attribute being
  5710  * - `fetchpriority` attribute with a value of "high"
  5315  * omitted on the element. The purpose of this refinement is to avoid lazy-loading elements that are within the initial
  5711  * - `decoding` attribute with a value of "async"
  5316  * viewport, which can have a negative performance impact.
  5712  *
  5317  *
  5713  * If any of these attributes are already present in the given attributes, they will not be modified. Note that no
  5318  * Under the hood, the function uses {@see wp_increase_content_media_count()} every time it is called for an element
  5714  * element should have both `loading="lazy"` and `fetchpriority="high"`, so the function will trigger a warning in case
  5319  * within the main content. If the element is the very first content element, the `loading` attribute will be omitted.
  5715  * both attributes are present with those values.
  5320  * This default threshold of 1 content element to omit the `loading` attribute for can be customized using the
  5716  *
  5321  * {@see 'wp_omit_loading_attr_threshold'} filter.
  5717  * @since 6.3.0
  5322  *
  5718  *
  5323  * @since 5.9.0
  5719  * @global WP_Query $wp_query WordPress Query object.
  5324  *
  5720  *
  5325  * @param string $context Context for the element for which the `loading` attribute value is requested.
  5721  * @param string $tag_name The tag name.
  5326  * @return string|bool The default `loading` attribute value. Either 'lazy', 'eager', or a boolean `false`, to indicate
  5722  * @param array  $attr     Array of the attributes for the tag.
  5327  *                     that the `loading` attribute should be skipped.
  5723  * @param string $context  Context for the element for which the loading optimization attribute is requested.
  5328  */
  5724  * @return array Loading optimization attributes.
  5329 function wp_get_loading_attr_default( $context ) {
  5725  */
  5330 	// Only elements with 'the_content' or 'the_post_thumbnail' context have special handling.
  5726 function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
  5331 	if ( 'the_content' !== $context && 'the_post_thumbnail' !== $context ) {
  5727 	global $wp_query;
  5332 		return 'lazy';
  5728 
  5333 	}
  5729 	/**
  5334 
  5730 	 * Filters whether to short-circuit loading optimization attributes.
  5335 	// Only elements within the main query loop have special handling.
  5731 	 *
  5336 	if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
  5732 	 * Returning an array from the filter will effectively short-circuit the loading of optimization attributes,
  5337 		return 'lazy';
  5733 	 * returning that value instead.
  5338 	}
  5734 	 *
  5339 
  5735 	 * @since 6.4.0
  5340 	// Increase the counter since this is a main query content element.
  5736 	 *
  5341 	$content_media_count = wp_increase_content_media_count();
  5737 	 * @param array|false $loading_attrs False by default, or array of loading optimization attributes to short-circuit.
  5342 
  5738 	 * @param string      $tag_name      The tag name.
  5343 	// If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
  5739 	 * @param array       $attr          Array of the attributes for the tag.
  5344 	if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
  5740 	 * @param string      $context       Context for the element for which the loading optimization attribute is requested.
  5345 		return false;
  5741 	 */
  5346 	}
  5742 	$loading_attrs = apply_filters( 'pre_wp_get_loading_optimization_attributes', false, $tag_name, $attr, $context );
  5347 
  5743 
  5348 	// For elements after the threshold, lazy-load them as usual.
  5744 	if ( is_array( $loading_attrs ) ) {
  5349 	return 'lazy';
  5745 		return $loading_attrs;
       
  5746 	}
       
  5747 
       
  5748 	$loading_attrs = array();
       
  5749 
       
  5750 	/*
       
  5751 	 * Skip lazy-loading for the overall block template, as it is handled more granularly.
       
  5752 	 * The skip is also applicable for `fetchpriority`.
       
  5753 	 */
       
  5754 	if ( 'template' === $context ) {
       
  5755 		/** This filter is documented in wp-includes/media.php */
       
  5756 		return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
       
  5757 	}
       
  5758 
       
  5759 	// For now this function only supports images and iframes.
       
  5760 	if ( 'img' !== $tag_name && 'iframe' !== $tag_name ) {
       
  5761 		/** This filter is documented in wp-includes/media.php */
       
  5762 		return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
       
  5763 	}
       
  5764 
       
  5765 	/*
       
  5766 	 * Skip programmatically created images within content blobs as they need to be handled together with the other
       
  5767 	 * images within the post content or widget content.
       
  5768 	 * Without this clause, they would already be considered within their own context which skews the image count and
       
  5769 	 * can result in the first post content image being lazy-loaded or an image further down the page being marked as a
       
  5770 	 * high priority.
       
  5771 	 */
       
  5772 	if (
       
  5773 		'the_content' !== $context && doing_filter( 'the_content' ) ||
       
  5774 		'widget_text_content' !== $context && doing_filter( 'widget_text_content' ) ||
       
  5775 		'widget_block_content' !== $context && doing_filter( 'widget_block_content' )
       
  5776 	) {
       
  5777 		/** This filter is documented in wp-includes/media.php */
       
  5778 		return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
       
  5779 
       
  5780 	}
       
  5781 
       
  5782 	/*
       
  5783 	 * Add `decoding` with a value of "async" for every image unless it has a
       
  5784 	 * conflicting `decoding` attribute already present.
       
  5785 	 */
       
  5786 	if ( 'img' === $tag_name ) {
       
  5787 		if ( isset( $attr['decoding'] ) ) {
       
  5788 			$loading_attrs['decoding'] = $attr['decoding'];
       
  5789 		} else {
       
  5790 			$loading_attrs['decoding'] = 'async';
       
  5791 		}
       
  5792 	}
       
  5793 
       
  5794 	// For any resources, width and height must be provided, to avoid layout shifts.
       
  5795 	if ( ! isset( $attr['width'], $attr['height'] ) ) {
       
  5796 		/** This filter is documented in wp-includes/media.php */
       
  5797 		return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
       
  5798 	}
       
  5799 
       
  5800 	/*
       
  5801 	 * The key function logic starts here.
       
  5802 	 */
       
  5803 	$maybe_in_viewport    = null;
       
  5804 	$increase_count       = false;
       
  5805 	$maybe_increase_count = false;
       
  5806 
       
  5807 	// Logic to handle a `loading` attribute that is already provided.
       
  5808 	if ( isset( $attr['loading'] ) ) {
       
  5809 		/*
       
  5810 		 * Interpret "lazy" as not in viewport. Any other value can be
       
  5811 		 * interpreted as in viewport (realistically only "eager" or `false`
       
  5812 		 * to force-omit the attribute are other potential values).
       
  5813 		 */
       
  5814 		if ( 'lazy' === $attr['loading'] ) {
       
  5815 			$maybe_in_viewport = false;
       
  5816 		} else {
       
  5817 			$maybe_in_viewport = true;
       
  5818 		}
       
  5819 	}
       
  5820 
       
  5821 	// Logic to handle a `fetchpriority` attribute that is already provided.
       
  5822 	if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
       
  5823 		/*
       
  5824 		 * If the image was already determined to not be in the viewport (e.g.
       
  5825 		 * from an already provided `loading` attribute), trigger a warning.
       
  5826 		 * Otherwise, the value can be interpreted as in viewport, since only
       
  5827 		 * the most important in-viewport image should have `fetchpriority` set
       
  5828 		 * to "high".
       
  5829 		 */
       
  5830 		if ( false === $maybe_in_viewport ) {
       
  5831 			_doing_it_wrong(
       
  5832 				__FUNCTION__,
       
  5833 				__( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
       
  5834 				'6.3.0'
       
  5835 			);
       
  5836 			/*
       
  5837 			 * Set `fetchpriority` here for backward-compatibility as we should
       
  5838 			 * not override what a developer decided, even though it seems
       
  5839 			 * incorrect.
       
  5840 			 */
       
  5841 			$loading_attrs['fetchpriority'] = 'high';
       
  5842 		} else {
       
  5843 			$maybe_in_viewport = true;
       
  5844 		}
       
  5845 	}
       
  5846 
       
  5847 	if ( null === $maybe_in_viewport ) {
       
  5848 		$header_enforced_contexts = array(
       
  5849 			'template_part_' . WP_TEMPLATE_PART_AREA_HEADER => true,
       
  5850 			'get_header_image_tag' => true,
       
  5851 		);
       
  5852 
       
  5853 		/**
       
  5854 		 * Filters the header-specific contexts.
       
  5855 		 *
       
  5856 		 * @since 6.4.0
       
  5857 		 *
       
  5858 		 * @param array $default_header_enforced_contexts Map of contexts for which elements should be considered
       
  5859 		 *                                                in the header of the page, as $context => $enabled
       
  5860 		 *                                                pairs. The $enabled should always be true.
       
  5861 		 */
       
  5862 		$header_enforced_contexts = apply_filters( 'wp_loading_optimization_force_header_contexts', $header_enforced_contexts );
       
  5863 
       
  5864 		// Consider elements with these header-specific contexts to be in viewport.
       
  5865 		if ( isset( $header_enforced_contexts[ $context ] ) ) {
       
  5866 			$maybe_in_viewport    = true;
       
  5867 			$maybe_increase_count = true;
       
  5868 		} elseif ( ! is_admin() && in_the_loop() && is_main_query() ) {
       
  5869 			/*
       
  5870 			 * Get the content media count, since this is a main query
       
  5871 			 * content element. This is accomplished by "increasing"
       
  5872 			 * the count by zero, as the only way to get the count is
       
  5873 			 * to call this function.
       
  5874 			 * The actual count increase happens further below, based
       
  5875 			 * on the `$increase_count` flag set here.
       
  5876 			 */
       
  5877 			$content_media_count = wp_increase_content_media_count( 0 );
       
  5878 			$increase_count      = true;
       
  5879 
       
  5880 			// If the count so far is below the threshold, `loading` attribute is omitted.
       
  5881 			if ( $content_media_count < wp_omit_loading_attr_threshold() ) {
       
  5882 				$maybe_in_viewport = true;
       
  5883 			} else {
       
  5884 				$maybe_in_viewport = false;
       
  5885 			}
       
  5886 		} elseif (
       
  5887 			// Only apply for main query but before the loop.
       
  5888 			$wp_query->before_loop && $wp_query->is_main_query()
       
  5889 			/*
       
  5890 			 * Any image before the loop, but after the header has started should not be lazy-loaded,
       
  5891 			 * except when the footer has already started which can happen when the current template
       
  5892 			 * does not include any loop.
       
  5893 			 */
       
  5894 			&& did_action( 'get_header' ) && ! did_action( 'get_footer' )
       
  5895 			) {
       
  5896 			$maybe_in_viewport    = true;
       
  5897 			$maybe_increase_count = true;
       
  5898 		}
       
  5899 	}
       
  5900 
       
  5901 	/*
       
  5902 	 * If the element is in the viewport (`true`), potentially add
       
  5903 	 * `fetchpriority` with a value of "high". Otherwise, i.e. if the element
       
  5904 	 * is not not in the viewport (`false`) or it is unknown (`null`), add
       
  5905 	 * `loading` with a value of "lazy".
       
  5906 	 */
       
  5907 	if ( $maybe_in_viewport ) {
       
  5908 		$loading_attrs = wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr );
       
  5909 	} else {
       
  5910 		// Only add `loading="lazy"` if the feature is enabled.
       
  5911 		if ( wp_lazy_loading_enabled( $tag_name, $context ) ) {
       
  5912 			$loading_attrs['loading'] = 'lazy';
       
  5913 		}
       
  5914 	}
       
  5915 
       
  5916 	/*
       
  5917 	 * If flag was set based on contextual logic above, increase the content
       
  5918 	 * media count, either unconditionally, or based on whether the image size
       
  5919 	 * is larger than the threshold.
       
  5920 	 */
       
  5921 	if ( $increase_count ) {
       
  5922 		wp_increase_content_media_count();
       
  5923 	} elseif ( $maybe_increase_count ) {
       
  5924 		/** This filter is documented in wp-includes/media.php */
       
  5925 		$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );
       
  5926 
       
  5927 		if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
       
  5928 			wp_increase_content_media_count();
       
  5929 		}
       
  5930 	}
       
  5931 
       
  5932 	/**
       
  5933 	 * Filters the loading optimization attributes.
       
  5934 	 *
       
  5935 	 * @since 6.4.0
       
  5936 	 *
       
  5937 	 * @param array  $loading_attrs The loading optimization attributes.
       
  5938 	 * @param string $tag_name      The tag name.
       
  5939 	 * @param array  $attr          Array of the attributes for the tag.
       
  5940 	 * @param string $context       Context for the element for which the loading optimization attribute is requested.
       
  5941 	 */
       
  5942 	return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
  5350 }
  5943 }
  5351 
  5944 
  5352 /**
  5945 /**
  5353  * Gets the threshold for how many of the first content media elements to not lazy-load.
  5946  * Gets the threshold for how many of the first content media elements to not lazy-load.
  5354  *
  5947  *
  5355  * This function runs the {@see 'wp_omit_loading_attr_threshold'} filter, which uses a default threshold value of 1.
  5948  * This function runs the {@see 'wp_omit_loading_attr_threshold'} filter, which uses a default threshold value of 3.
  5356  * The filter is only run once per page load, unless the `$force` parameter is used.
  5949  * The filter is only run once per page load, unless the `$force` parameter is used.
  5357  *
  5950  *
  5358  * @since 5.9.0
  5951  * @since 5.9.0
  5359  *
  5952  *
  5360  * @param bool $force Optional. If set to true, the filter will be (re-)applied even if it already has been before.
  5953  * @param bool $force Optional. If set to true, the filter will be (re-)applied even if it already has been before.
  5371 		 *
  5964 		 *
  5372 		 * For these first content media elements, the `loading` attribute will be omitted. By default, this is the case
  5965 		 * For these first content media elements, the `loading` attribute will be omitted. By default, this is the case
  5373 		 * for only the very first content media element.
  5966 		 * for only the very first content media element.
  5374 		 *
  5967 		 *
  5375 		 * @since 5.9.0
  5968 		 * @since 5.9.0
       
  5969 		 * @since 6.3.0 The default threshold was changed from 1 to 3.
  5376 		 *
  5970 		 *
  5377 		 * @param int $omit_threshold The number of media elements where the `loading` attribute will not be added. Default 1.
  5971 		 * @param int $omit_threshold The number of media elements where the `loading` attribute will not be added. Default 3.
  5378 		 */
  5972 		 */
  5379 		$omit_threshold = apply_filters( 'wp_omit_loading_attr_threshold', 1 );
  5973 		$omit_threshold = apply_filters( 'wp_omit_loading_attr_threshold', 3 );
  5380 	}
  5974 	}
  5381 
  5975 
  5382 	return $omit_threshold;
  5976 	return $omit_threshold;
  5383 }
  5977 }
  5384 
  5978 
  5396 
  5990 
  5397 	$content_media_count += $amount;
  5991 	$content_media_count += $amount;
  5398 
  5992 
  5399 	return $content_media_count;
  5993 	return $content_media_count;
  5400 }
  5994 }
       
  5995 
       
  5996 /**
       
  5997  * Determines whether to add `fetchpriority='high'` to loading attributes.
       
  5998  *
       
  5999  * @since 6.3.0
       
  6000  * @access private
       
  6001  *
       
  6002  * @param array  $loading_attrs Array of the loading optimization attributes for the element.
       
  6003  * @param string $tag_name      The tag name.
       
  6004  * @param array  $attr          Array of the attributes for the element.
       
  6005  * @return array Updated loading optimization attributes for the element.
       
  6006  */
       
  6007 function wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr ) {
       
  6008 	// For now, adding `fetchpriority="high"` is only supported for images.
       
  6009 	if ( 'img' !== $tag_name ) {
       
  6010 		return $loading_attrs;
       
  6011 	}
       
  6012 
       
  6013 	if ( isset( $attr['fetchpriority'] ) ) {
       
  6014 		/*
       
  6015 		 * While any `fetchpriority` value could be set in `$loading_attrs`,
       
  6016 		 * for consistency we only do it for `fetchpriority="high"` since that
       
  6017 		 * is the only possible value that WordPress core would apply on its
       
  6018 		 * own.
       
  6019 		 */
       
  6020 		if ( 'high' === $attr['fetchpriority'] ) {
       
  6021 			$loading_attrs['fetchpriority'] = 'high';
       
  6022 			wp_high_priority_element_flag( false );
       
  6023 		}
       
  6024 
       
  6025 		return $loading_attrs;
       
  6026 	}
       
  6027 
       
  6028 	// Lazy-loading and `fetchpriority="high"` are mutually exclusive.
       
  6029 	if ( isset( $loading_attrs['loading'] ) && 'lazy' === $loading_attrs['loading'] ) {
       
  6030 		return $loading_attrs;
       
  6031 	}
       
  6032 
       
  6033 	if ( ! wp_high_priority_element_flag() ) {
       
  6034 		return $loading_attrs;
       
  6035 	}
       
  6036 
       
  6037 	/**
       
  6038 	 * Filters the minimum square-pixels threshold for an image to be eligible as the high-priority image.
       
  6039 	 *
       
  6040 	 * @since 6.3.0
       
  6041 	 *
       
  6042 	 * @param int $threshold Minimum square-pixels threshold. Default 50000.
       
  6043 	 */
       
  6044 	$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );
       
  6045 
       
  6046 	if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
       
  6047 		$loading_attrs['fetchpriority'] = 'high';
       
  6048 		wp_high_priority_element_flag( false );
       
  6049 	}
       
  6050 
       
  6051 	return $loading_attrs;
       
  6052 }
       
  6053 
       
  6054 /**
       
  6055  * Accesses a flag that indicates if an element is a possible candidate for `fetchpriority='high'`.
       
  6056  *
       
  6057  * @since 6.3.0
       
  6058  * @access private
       
  6059  *
       
  6060  * @param bool $value Optional. Used to change the static variable. Default null.
       
  6061  * @return bool Returns true if high-priority element was marked already, otherwise false.
       
  6062  */
       
  6063 function wp_high_priority_element_flag( $value = null ) {
       
  6064 	static $high_priority_element = true;
       
  6065 
       
  6066 	if ( is_bool( $value ) ) {
       
  6067 		$high_priority_element = $value;
       
  6068 	}
       
  6069 
       
  6070 	return $high_priority_element;
       
  6071 }