wp/wp-admin/includes/image.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    26 	$src_file = $src;
    26 	$src_file = $src;
    27 	if ( is_numeric( $src ) ) { // Handle int as attachment ID.
    27 	if ( is_numeric( $src ) ) { // Handle int as attachment ID.
    28 		$src_file = get_attached_file( $src );
    28 		$src_file = get_attached_file( $src );
    29 
    29 
    30 		if ( ! file_exists( $src_file ) ) {
    30 		if ( ! file_exists( $src_file ) ) {
    31 			// If the file doesn't exist, attempt a URL fopen on the src link.
    31 			/*
    32 			// This can occur with certain file replication plugins.
    32 			 * If the file doesn't exist, attempt a URL fopen on the src link.
       
    33 			 * This can occur with certain file replication plugins.
       
    34 			 */
    33 			$src = _load_image_to_edit_path( $src, 'full' );
    35 			$src = _load_image_to_edit_path( $src, 'full' );
    34 		} else {
    36 		} else {
    35 			$src = $src_file;
    37 			$src = $src_file;
    36 		}
    38 		}
    37 	}
    39 	}
   157 function wp_update_image_subsizes( $attachment_id ) {
   159 function wp_update_image_subsizes( $attachment_id ) {
   158 	$image_meta = wp_get_attachment_metadata( $attachment_id );
   160 	$image_meta = wp_get_attachment_metadata( $attachment_id );
   159 	$image_file = wp_get_original_image_path( $attachment_id );
   161 	$image_file = wp_get_original_image_path( $attachment_id );
   160 
   162 
   161 	if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
   163 	if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
   162 		// Previously failed upload?
   164 		/*
   163 		// If there is an uploaded file, make all sub-sizes and generate all of the attachment meta.
   165 		 * Previously failed upload?
       
   166 		 * If there is an uploaded file, make all sub-sizes and generate all of the attachment meta.
       
   167 		 */
   164 		if ( ! empty( $image_file ) ) {
   168 		if ( ! empty( $image_file ) ) {
   165 			$image_meta = wp_create_image_subsizes( $image_file, $attachment_id );
   169 			$image_meta = wp_create_image_subsizes( $image_file, $attachment_id );
   166 		} else {
   170 		} else {
   167 			return new WP_Error( 'invalid_attachment', __( 'The attached file cannot be found.' ) );
   171 			return new WP_Error( 'invalid_attachment', __( 'The attached file cannot be found.' ) );
   168 		}
   172 		}
   188 
   192 
   189 /**
   193 /**
   190  * Updates the attached file and image meta data when the original image was edited.
   194  * Updates the attached file and image meta data when the original image was edited.
   191  *
   195  *
   192  * @since 5.3.0
   196  * @since 5.3.0
       
   197  * @since 6.0.0 The `$filesize` value was added to the returned array.
   193  * @access private
   198  * @access private
   194  *
   199  *
   195  * @param array  $saved_data    The data returned from WP_Image_Editor after successfully saving an image.
   200  * @param array  $saved_data    The data returned from WP_Image_Editor after successfully saving an image.
   196  * @param string $original_file Path to the original file.
   201  * @param string $original_file Path to the original file.
   197  * @param array  $image_meta    The image meta data.
   202  * @param array  $image_meta    The image meta data.
   209 	$image_meta['height'] = $saved_data['height'];
   214 	$image_meta['height'] = $saved_data['height'];
   210 
   215 
   211 	// Make the file path relative to the upload dir.
   216 	// Make the file path relative to the upload dir.
   212 	$image_meta['file'] = _wp_relative_upload_path( $new_file );
   217 	$image_meta['file'] = _wp_relative_upload_path( $new_file );
   213 
   218 
       
   219 	// Add image file size.
       
   220 	$image_meta['filesize'] = wp_filesize( $new_file );
       
   221 
   214 	// Store the original image file name in image_meta.
   222 	// Store the original image file name in image_meta.
   215 	$image_meta['original_image'] = wp_basename( $original_file );
   223 	$image_meta['original_image'] = wp_basename( $original_file );
   216 
       
   217 	// Add image file size.
       
   218 	$image_meta['filesize'] = wp_filesize( $new_file );
       
   219 
   224 
   220 	return $image_meta;
   225 	return $image_meta;
   221 }
   226 }
   222 
   227 
   223 /**
   228 /**
   280 		 * @param string $file          Full path to the uploaded image file.
   285 		 * @param string $file          Full path to the uploaded image file.
   281 		 * @param int    $attachment_id Attachment post ID.
   286 		 * @param int    $attachment_id Attachment post ID.
   282 		 */
   287 		 */
   283 		$threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id );
   288 		$threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id );
   284 
   289 
   285 		// If the original image's dimensions are over the threshold,
   290 		/*
   286 		// scale the image and use it as the "full" size.
   291 		 * If the original image's dimensions are over the threshold,
       
   292 		 * scale the image and use it as the "full" size.
       
   293 		 */
   287 		if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) {
   294 		if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) {
   288 			$editor = wp_get_image_editor( $file );
   295 			$editor = wp_get_image_editor( $file );
   289 
   296 
   290 			if ( is_wp_error( $editor ) ) {
   297 			if ( is_wp_error( $editor ) ) {
   291 				// This image cannot be edited.
   298 				// This image cannot be edited.
   301 				$resized = $editor->maybe_exif_rotate();
   308 				$resized = $editor->maybe_exif_rotate();
   302 				$rotated = $resized;
   309 				$rotated = $resized;
   303 			}
   310 			}
   304 
   311 
   305 			if ( ! is_wp_error( $resized ) ) {
   312 			if ( ! is_wp_error( $resized ) ) {
   306 				// Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg".
   313 				/*
   307 				// This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
   314 				 * Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg".
       
   315 				 * This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
       
   316 				 */
   308 				$saved = $editor->save( $editor->generate_filename( 'scaled' ) );
   317 				$saved = $editor->save( $editor->generate_filename( 'scaled' ) );
   309 
   318 
   310 				if ( ! is_wp_error( $saved ) ) {
   319 				if ( ! is_wp_error( $saved ) ) {
   311 					$image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
   320 					$image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
   312 
   321 
   472 
   481 
   473 	return $image_meta;
   482 	return $image_meta;
   474 }
   483 }
   475 
   484 
   476 /**
   485 /**
   477  * Generate attachment meta data and create image sub-sizes for images.
   486  * Copy parent attachment properties to newly cropped image.
       
   487  *
       
   488  * @since 6.5.0
       
   489  *
       
   490  * @param string $cropped              Path to the cropped image file.
       
   491  * @param int    $parent_attachment_id Parent file Attachment ID.
       
   492  * @param string $context              Control calling the function.
       
   493  * @return array Properties of attachment.
       
   494  */
       
   495 function wp_copy_parent_attachment_properties( $cropped, $parent_attachment_id, $context = '' ) {
       
   496 	$parent          = get_post( $parent_attachment_id );
       
   497 	$parent_url      = wp_get_attachment_url( $parent->ID );
       
   498 	$parent_basename = wp_basename( $parent_url );
       
   499 	$url             = str_replace( wp_basename( $parent_url ), wp_basename( $cropped ), $parent_url );
       
   500 
       
   501 	$size       = wp_getimagesize( $cropped );
       
   502 	$image_type = $size ? $size['mime'] : 'image/jpeg';
       
   503 
       
   504 	$sanitized_post_title = sanitize_file_name( $parent->post_title );
       
   505 	$use_original_title   = (
       
   506 		( '' !== trim( $parent->post_title ) ) &&
       
   507 		/*
       
   508 		 * Check if the original image has a title other than the "filename" default,
       
   509 		 * meaning the image had a title when originally uploaded or its title was edited.
       
   510 		 */
       
   511 		( $parent_basename !== $sanitized_post_title ) &&
       
   512 		( pathinfo( $parent_basename, PATHINFO_FILENAME ) !== $sanitized_post_title )
       
   513 	);
       
   514 	$use_original_description = ( '' !== trim( $parent->post_content ) );
       
   515 
       
   516 	$attachment = array(
       
   517 		'post_title'     => $use_original_title ? $parent->post_title : wp_basename( $cropped ),
       
   518 		'post_content'   => $use_original_description ? $parent->post_content : $url,
       
   519 		'post_mime_type' => $image_type,
       
   520 		'guid'           => $url,
       
   521 		'context'        => $context,
       
   522 	);
       
   523 
       
   524 	// Copy the image caption attribute (post_excerpt field) from the original image.
       
   525 	if ( '' !== trim( $parent->post_excerpt ) ) {
       
   526 		$attachment['post_excerpt'] = $parent->post_excerpt;
       
   527 	}
       
   528 
       
   529 	// Copy the image alt text attribute from the original image.
       
   530 	if ( '' !== trim( $parent->_wp_attachment_image_alt ) ) {
       
   531 		$attachment['meta_input'] = array(
       
   532 			'_wp_attachment_image_alt' => wp_slash( $parent->_wp_attachment_image_alt ),
       
   533 		);
       
   534 	}
       
   535 
       
   536 	$attachment['post_parent'] = $parent_attachment_id;
       
   537 
       
   538 	return $attachment;
       
   539 }
       
   540 
       
   541 /**
       
   542  * Generates attachment meta data and create image sub-sizes for images.
   478  *
   543  *
   479  * @since 2.1.0
   544  * @since 2.1.0
       
   545  * @since 6.0.0 The `$filesize` value was added to the returned array.
   480  *
   546  *
   481  * @param int    $attachment_id Attachment ID to process.
   547  * @param int    $attachment_id Attachment ID to process.
   482  * @param string $file          Filepath of the attached image.
   548  * @param string $file          Filepath of the attached image.
   483  * @return array Metadata for attachment.
   549  * @return array Metadata for attachment.
   484  */
   550  */
   655 	 */
   721 	 */
   656 	return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id, 'create' );
   722 	return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id, 'create' );
   657 }
   723 }
   658 
   724 
   659 /**
   725 /**
   660  * Convert a fraction string to a decimal.
   726  * Converts a fraction string to a decimal.
   661  *
   727  *
   662  * @since 2.5.0
   728  * @since 2.5.0
   663  *
   729  *
   664  * @param string $str Fraction string.
   730  * @param string $str Fraction string.
   665  * @return int|float Returns calculated fraction or integer 0 on invalid input.
   731  * @return int|float Returns calculated fraction or integer 0 on invalid input.
   688 	if ( ! is_numeric( $numerator ) || ! is_numeric( $denominator ) ) {
   754 	if ( ! is_numeric( $numerator ) || ! is_numeric( $denominator ) ) {
   689 		return 0;
   755 		return 0;
   690 	}
   756 	}
   691 
   757 
   692 	// The denominator must not be zero.
   758 	// The denominator must not be zero.
   693 	if ( 0 == $denominator ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison -- Deliberate loose comparison.
   759 	if ( 0 == $denominator ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- Deliberate loose comparison.
   694 		return 0;
   760 		return 0;
   695 	}
   761 	}
   696 
   762 
   697 	return $numerator / $denominator;
   763 	return $numerator / $denominator;
   698 }
   764 }
   699 
   765 
   700 /**
   766 /**
   701  * Convert the exif date format to a unix timestamp.
   767  * Converts the exif date format to a unix timestamp.
   702  *
   768  *
   703  * @since 2.5.0
   769  * @since 2.5.0
   704  *
   770  *
   705  * @param string $str A date string expected to be in Exif format (Y:m:d H:i:s).
   771  * @param string $str A date string expected to be in Exif format (Y:m:d H:i:s).
   706  * @return int|false The unix timestamp, or false on failure.
   772  * @return int|false The unix timestamp, or false on failure.
   711 
   777 
   712 	return strtotime( "{$y}-{$m}-{$d} {$time}" );
   778 	return strtotime( "{$y}-{$m}-{$d} {$time}" );
   713 }
   779 }
   714 
   780 
   715 /**
   781 /**
   716  * Get extended image metadata, exif or iptc as available.
   782  * Gets extended image metadata, exif or iptc as available.
   717  *
   783  *
   718  * Retrieves the EXIF metadata aperture, credit, camera, caption, copyright, iso
   784  * Retrieves the EXIF metadata aperture, credit, camera, caption, copyright, iso
   719  * created_timestamp, focal_length, shutter_speed, and title.
   785  * created_timestamp, focal_length, shutter_speed, and title.
   720  *
   786  *
   721  * The IPTC metadata that is retrieved is APP13, credit, byline, created date
   787  * The IPTC metadata that is retrieved is APP13, credit, byline, created date
   770 			if ( defined( 'WP_DEBUG' ) && WP_DEBUG
   836 			if ( defined( 'WP_DEBUG' ) && WP_DEBUG
   771 				&& ! defined( 'WP_RUN_CORE_TESTS' )
   837 				&& ! defined( 'WP_RUN_CORE_TESTS' )
   772 			) {
   838 			) {
   773 				$iptc = iptcparse( $info['APP13'] );
   839 				$iptc = iptcparse( $info['APP13'] );
   774 			} else {
   840 			} else {
   775 				// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silencing notice and warning is intentional. See https://core.trac.wordpress.org/ticket/42480
   841 				// Silencing notice and warning is intentional. See https://core.trac.wordpress.org/ticket/42480
   776 				$iptc = @iptcparse( $info['APP13'] );
   842 				$iptc = @iptcparse( $info['APP13'] );
   777 			}
   843 			}
   778 
   844 
   779 			if ( ! is_array( $iptc ) ) {
   845 			if ( ! is_array( $iptc ) ) {
   780 				$iptc = array();
   846 				$iptc = array();
   843 		if ( defined( 'WP_DEBUG' ) && WP_DEBUG
   909 		if ( defined( 'WP_DEBUG' ) && WP_DEBUG
   844 			&& ! defined( 'WP_RUN_CORE_TESTS' )
   910 			&& ! defined( 'WP_RUN_CORE_TESTS' )
   845 		) {
   911 		) {
   846 			$exif = exif_read_data( $file );
   912 			$exif = exif_read_data( $file );
   847 		} else {
   913 		} else {
   848 			// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silencing notice and warning is intentional. See https://core.trac.wordpress.org/ticket/42480
   914 			// Silencing notice and warning is intentional. See https://core.trac.wordpress.org/ticket/42480
   849 			$exif = @exif_read_data( $file );
   915 			$exif = @exif_read_data( $file );
   850 		}
   916 		}
   851 
   917 
   852 		if ( ! is_array( $exif ) ) {
   918 		if ( ! is_array( $exif ) ) {
   853 			$exif = array();
   919 			$exif = array();
   854 		}
   920 		}
   855 
   921 
       
   922 		$exif_description = '';
       
   923 		$exif_usercomment = '';
   856 		if ( ! empty( $exif['ImageDescription'] ) ) {
   924 		if ( ! empty( $exif['ImageDescription'] ) ) {
       
   925 			$exif_description = trim( $exif['ImageDescription'] );
       
   926 		}
       
   927 
       
   928 		if ( ! empty( $exif['COMPUTED']['UserComment'] ) ) {
       
   929 			$exif_usercomment = trim( $exif['COMPUTED']['UserComment'] );
       
   930 		}
       
   931 
       
   932 		if ( $exif_description ) {
   857 			mbstring_binary_safe_encoding();
   933 			mbstring_binary_safe_encoding();
   858 			$description_length = strlen( $exif['ImageDescription'] );
   934 			$description_length = strlen( $exif_description );
   859 			reset_mbstring_encoding();
   935 			reset_mbstring_encoding();
   860 
       
   861 			if ( empty( $meta['title'] ) && $description_length < 80 ) {
   936 			if ( empty( $meta['title'] ) && $description_length < 80 ) {
   862 				// Assume the title is stored in ImageDescription.
   937 				// Assume the title is stored in ImageDescription.
   863 				$meta['title'] = trim( $exif['ImageDescription'] );
   938 				$meta['title'] = $exif_description;
   864 			}
   939 			}
   865 
   940 
   866 			if ( empty( $meta['caption'] ) && ! empty( $exif['COMPUTED']['UserComment'] ) ) {
   941 			// If both user comments and description are present.
   867 				$meta['caption'] = trim( $exif['COMPUTED']['UserComment'] );
   942 			if ( empty( $meta['caption'] ) && $exif_description && $exif_usercomment ) {
       
   943 				if ( ! empty( $meta['title'] ) && $exif_description === $meta['title'] ) {
       
   944 					$caption = $exif_usercomment;
       
   945 				} else {
       
   946 					if ( $exif_description === $exif_usercomment ) {
       
   947 						$caption = $exif_description;
       
   948 					} else {
       
   949 						$caption = trim( $exif_description . ' ' . $exif_usercomment );
       
   950 					}
       
   951 				}
       
   952 				$meta['caption'] = $caption;
       
   953 			}
       
   954 
       
   955 			if ( empty( $meta['caption'] ) && $exif_usercomment ) {
       
   956 				$meta['caption'] = $exif_usercomment;
   868 			}
   957 			}
   869 
   958 
   870 			if ( empty( $meta['caption'] ) ) {
   959 			if ( empty( $meta['caption'] ) ) {
   871 				$meta['caption'] = trim( $exif['ImageDescription'] );
   960 				$meta['caption'] = $exif_description;
       
   961 			}
       
   962 		} elseif ( empty( $meta['caption'] ) && $exif_usercomment ) {
       
   963 			$meta['caption']    = $exif_usercomment;
       
   964 			$description_length = strlen( $exif_usercomment );
       
   965 			if ( empty( $meta['title'] ) && $description_length < 80 ) {
       
   966 				$meta['title'] = trim( $exif_usercomment );
   872 			}
   967 			}
   873 		} elseif ( empty( $meta['caption'] ) && ! empty( $exif['Comments'] ) ) {
   968 		} elseif ( empty( $meta['caption'] ) && ! empty( $exif['Comments'] ) ) {
   874 			$meta['caption'] = trim( $exif['Comments'] );
   969 			$meta['caption'] = trim( $exif['Comments'] );
   875 		}
   970 		}
   876 
   971 
   941 	 * @param int    $image_type Type of image, one of the `IMAGETYPE_XXX` constants.
  1036 	 * @param int    $image_type Type of image, one of the `IMAGETYPE_XXX` constants.
   942 	 * @param array  $iptc       IPTC data.
  1037 	 * @param array  $iptc       IPTC data.
   943 	 * @param array  $exif       EXIF data.
  1038 	 * @param array  $exif       EXIF data.
   944 	 */
  1039 	 */
   945 	return apply_filters( 'wp_read_image_metadata', $meta, $file, $image_type, $iptc, $exif );
  1040 	return apply_filters( 'wp_read_image_metadata', $meta, $file, $image_type, $iptc, $exif );
   946 
  1041 }
   947 }
  1042 
   948 
  1043 /**
   949 /**
  1044  * Validates that file is an image.
   950  * Validate that file is an image.
       
   951  *
  1045  *
   952  * @since 2.5.0
  1046  * @since 2.5.0
   953  *
  1047  *
   954  * @param string $path File path to test if valid image.
  1048  * @param string $path File path to test if valid image.
   955  * @return bool True if valid image, false if not valid image.
  1049  * @return bool True if valid image, false if not valid image.
   958 	$size = wp_getimagesize( $path );
  1052 	$size = wp_getimagesize( $path );
   959 	return ! empty( $size );
  1053 	return ! empty( $size );
   960 }
  1054 }
   961 
  1055 
   962 /**
  1056 /**
   963  * Validate that file is suitable for displaying within a web page.
  1057  * Validates that file is suitable for displaying within a web page.
   964  *
  1058  *
   965  * @since 2.5.0
  1059  * @since 2.5.0
   966  *
  1060  *
   967  * @param string $path File path to test.
  1061  * @param string $path File path to test.
   968  * @return bool True if suitable, false if not suitable.
  1062  * @return bool True if suitable, false if not suitable.
   969  */
  1063  */
   970 function file_is_displayable_image( $path ) {
  1064 function file_is_displayable_image( $path ) {
   971 	$displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO, IMAGETYPE_WEBP );
  1065 	$displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO, IMAGETYPE_WEBP, IMAGETYPE_AVIF );
   972 
  1066 
   973 	$info = wp_getimagesize( $path );
  1067 	$info = wp_getimagesize( $path );
   974 	if ( empty( $info ) ) {
  1068 	if ( empty( $info ) ) {
   975 		$result = false;
  1069 		$result = false;
   976 	} elseif ( ! in_array( $info[2], $displayable_image_types, true ) ) {
  1070 	} elseif ( ! in_array( $info[2], $displayable_image_types, true ) ) {
   989 	 */
  1083 	 */
   990 	return apply_filters( 'file_is_displayable_image', $result, $path );
  1084 	return apply_filters( 'file_is_displayable_image', $result, $path );
   991 }
  1085 }
   992 
  1086 
   993 /**
  1087 /**
   994  * Load an image resource for editing.
  1088  * Loads an image resource for editing.
   995  *
  1089  *
   996  * @since 2.9.0
  1090  * @since 2.9.0
   997  *
  1091  *
   998  * @param int          $attachment_id Attachment ID.
  1092  * @param int          $attachment_id Attachment ID.
   999  * @param string       $mime_type     Image mime type.
  1093  * @param string       $mime_type     Image mime type.
  1050 
  1144 
  1051 	return $image;
  1145 	return $image;
  1052 }
  1146 }
  1053 
  1147 
  1054 /**
  1148 /**
  1055  * Retrieve the path or URL of an attachment's attached file.
  1149  * Retrieves the path or URL of an attachment's attached file.
  1056  *
  1150  *
  1057  * If the attached file is not present on the local filesystem (usually due to replication plugins),
  1151  * If the attached file is not present on the local filesystem (usually due to replication plugins),
  1058  * then the URL of the file is returned if `allow_url_fopen` is supported.
  1152  * then the URL of the file is returned if `allow_url_fopen` is supported.
  1059  *
  1153  *
  1060  * @since 3.4.0
  1154  * @since 3.4.0
  1118 	 */
  1212 	 */
  1119 	return apply_filters( 'load_image_to_edit_path', $filepath, $attachment_id, $size );
  1213 	return apply_filters( 'load_image_to_edit_path', $filepath, $attachment_id, $size );
  1120 }
  1214 }
  1121 
  1215 
  1122 /**
  1216 /**
  1123  * Copy an existing image file.
  1217  * Copies an existing image file.
  1124  *
  1218  *
  1125  * @since 3.4.0
  1219  * @since 3.4.0
  1126  * @access private
  1220  * @access private
  1127  *
  1221  *
  1128  * @param int $attachment_id Attachment ID.
  1222  * @param int $attachment_id Attachment ID.