/** * Retrieve the URL for an attachment. * * @since 2.1.0 * * @global string $pagenow * * @param int $post_id Optional. Attachment ID. Default 0. * @return string|false Attachment URL, otherwise false. */ function wp_get_attachment_url($post_id = 0) { $post_id = (int) $post_id; if (!($post = get_post($post_id))) { return false; } if ('attachment' != $post->post_type) { return false; } $url = ''; // Get attached file. if ($file = get_post_meta($post->ID, '_wp_attached_file', true)) { // Get upload directory. if (($uploads = wp_upload_dir()) && false === $uploads['error']) { // Check that the upload base exists in the file location. if (0 === strpos($file, $uploads['basedir'])) { // Replace file location with url location. $url = str_replace($uploads['basedir'], $uploads['baseurl'], $file); } elseif (false !== strpos($file, 'wp-content/uploads')) { // Get the directory name relative to the basedir (back compat for pre-2.7 uploads) $url = trailingslashit($uploads['baseurl'] . '/' . _wp_get_attachment_relative_path($file)) . basename($file); } else { // It's a newly-uploaded file, therefore $file is relative to the basedir. $url = $uploads['baseurl'] . "/{$file}"; } } } /* * If any of the above options failed, Fallback on the GUID as used pre-2.7, * not recommended to rely upon this. */ if (empty($url)) { $url = get_the_guid($post->ID); } // On SSL front-end, URLs should be HTTPS. if (is_ssl() && !is_admin() && 'wp-login.php' !== $GLOBALS['pagenow']) { $url = set_url_scheme($url); } /** * Filter the attachment URL. * * @since 2.1.0 * * @param string $url URL for the given attachment. * @param int $post_id Attachment ID. */ $url = apply_filters('wp_get_attachment_url', $url, $post->ID); if (empty($url)) { return false; } return $url; }
/** * A helper function to calculate the image sources to include in a 'srcset' attribute. * * @since 4.4.0 * * @param array $size_array Array of width and height values in pixels (in that order). * @param string $image_src The 'src' of the image. * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * @param int $attachment_id Optional. The image attachment ID to pass to the filter. Default 0. * @return string|bool The 'srcset' attribute value. False on error or when only one source exists. */ function wp_calculate_image_srcset($size_array, $image_src, $image_meta, $attachment_id = 0) { /** * Let plugins pre-filter the image meta to be able to fix inconsistencies in the stored data. * * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * @param array $size_array Array of width and height values in pixels (in that order). * @param string $image_src The 'src' of the image. * @param int $attachment_id The image attachment ID or 0 if not supplied. */ $image_meta = apply_filters('wp_calculate_image_srcset_meta', $image_meta, $size_array, $image_src, $attachment_id); if (empty($image_meta['sizes'])) { return false; } $image_sizes = $image_meta['sizes']; // Get the width and height of the image. $image_width = (int) $size_array[0]; $image_height = (int) $size_array[1]; // Bail early if error/no width. if ($image_width < 1) { return false; } $image_basename = wp_basename($image_meta['file']); /* * WordPress flattens animated GIFs into one frame when generating intermediate sizes. * To avoid hiding animation in user content, if src is a full size GIF, a srcset attribute is not generated. * If src is an intermediate size GIF, the full size is excluded from srcset to keep a flattened GIF from becoming animated. */ if (!isset($image_sizes['thumbnail']['mime-type']) || 'image/gif' !== $image_sizes['thumbnail']['mime-type']) { $image_sizes['full'] = array('width' => $image_meta['width'], 'height' => $image_meta['height'], 'file' => $image_basename); } elseif (strpos($image_src, $image_meta['file'])) { return false; } // Retrieve the uploads sub-directory from the full size image. $dirname = _wp_get_attachment_relative_path($image_meta['file']); if ($dirname) { $dirname = trailingslashit($dirname); } $image_baseurl = _wp_upload_dir_baseurl(); $image_baseurl = trailingslashit($image_baseurl) . $dirname; // Calculate the image aspect ratio. $image_ratio = $image_height / $image_width; /* * Images that have been edited in WordPress after being uploaded will * contain a unique hash. Look for that hash and use it later to filter * out images that are leftovers from previous versions. */ $image_edited = preg_match('/-e[0-9]{13}/', wp_basename($image_src), $image_edit_hash); /** * Filter the maximum image width to be included in a 'srcset' attribute. * * @since 4.4.0 * * @param int $max_width The maximum image width to be included in the 'srcset'. Default '1600'. * @param array $size_array Array of width and height values in pixels (in that order). */ $max_srcset_image_width = apply_filters('max_srcset_image_width', 1600, $size_array); // Array to hold URL candidates. $sources = array(); /** * To make sure the ID matches our image src, we will check to see if any sizes in our attachment * meta match our $image_src. If no mathces are found we don't return a srcset to avoid serving * an incorrect image. See #35045. */ $src_matched = false; /* * Loop through available images. Only use images that are resized * versions of the same edit. */ foreach ($image_sizes as $image) { // If the file name is part of the `src`, we've confirmed a match. if (!$src_matched && false !== strpos($image_src, $dirname . $image['file'])) { $src_matched = true; } // Filter out images that are from previous edits. if ($image_edited && !strpos($image['file'], $image_edit_hash[0])) { continue; } /* * Filter out images that are wider than '$max_srcset_image_width' unless * that file is in the 'src' attribute. */ if ($max_srcset_image_width && $image['width'] > $max_srcset_image_width && false === strpos($image_src, $image['file'])) { continue; } // Calculate the new image ratio. if ($image['width']) { $image_ratio_compare = $image['height'] / $image['width']; } else { $image_ratio_compare = 0; } // If the new ratio differs by less than 0.002, use it. if (abs($image_ratio - $image_ratio_compare) < 0.002) { // Add the URL, descriptor, and value to the sources array to be returned. $sources[$image['width']] = array('url' => $image_baseurl . $image['file'], 'descriptor' => 'w', 'value' => $image['width']); } } /** * Filter an image's 'srcset' sources. * * @since 4.4.0 * * @param array $sources { * One or more arrays of source data to include in the 'srcset'. * * @type array $width { * @type string $url The URL of an image source. * @type string $descriptor The descriptor type used in the image candidate string, * either 'w' or 'x'. * @type int $value The source width if paired with a 'w' descriptor, or a * pixel density value if paired with an 'x' descriptor. * } * } * @param array $size_array Array of width and height values in pixels (in that order). * @param string $image_src The 'src' of the image. * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * @param int $attachment_id Image attachment ID or 0. */ $sources = apply_filters('wp_calculate_image_srcset', $sources, $size_array, $image_src, $image_meta, $attachment_id); // Only return a 'srcset' value if there is more than one source. if (!$src_matched || count($sources) < 2) { return false; } $srcset = ''; foreach ($sources as $source) { $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', '; } return rtrim($srcset, ', '); }
/** * A helper function to calculate the image sources to include in a 'srcset' attribute. * * @since 4.4.0 * * @param array $size_array Array of width and height values in pixels (in that order). * @param string $image_src The 'src' of the image. * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * @param int $attachment_id Optional. The image attachment ID to pass to the filter. Default 0. * @return string|bool The 'srcset' attribute value. False on error or when only one source exists. */ function wp_calculate_image_srcset($size_array, $image_src, $image_meta, $attachment_id = 0) { /** * Let plugins pre-filter the image meta to be able to fix inconsistencies in the stored data. * * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * @param array $size_array Array of width and height values in pixels (in that order). * @param string $image_src The 'src' of the image. * @param int $attachment_id The image attachment ID or 0 if not supplied. */ $image_meta = apply_filters('wp_calculate_image_srcset_meta', $image_meta, $size_array, $image_src, $attachment_id); if (empty($image_meta['sizes']) || !isset($image_meta['file']) || strlen($image_meta['file']) < 4) { return false; } $image_sizes = $image_meta['sizes']; // Get the width and height of the image. $image_width = (int) $size_array[0]; $image_height = (int) $size_array[1]; // Bail early if error/no width. if ($image_width < 1) { return false; } $image_basename = wp_basename($image_meta['file']); /* * WordPress flattens animated GIFs into one frame when generating intermediate sizes. * To avoid hiding animation in user content, if src is a full size GIF, a srcset attribute is not generated. * If src is an intermediate size GIF, the full size is excluded from srcset to keep a flattened GIF from becoming animated. */ if (!isset($image_sizes['thumbnail']['mime-type']) || 'image/gif' !== $image_sizes['thumbnail']['mime-type']) { $image_sizes[] = array('width' => $image_meta['width'], 'height' => $image_meta['height'], 'file' => $image_basename); } elseif (strpos($image_src, $image_meta['file'])) { return false; } // Retrieve the uploads sub-directory from the full size image. $dirname = _wp_get_attachment_relative_path($image_meta['file']); if ($dirname) { $dirname = trailingslashit($dirname); } $upload_dir = wp_get_upload_dir(); $image_baseurl = trailingslashit($upload_dir['baseurl']) . $dirname; /* * If currently on HTTPS, prefer HTTPS URLs when we know they're supported by the domain * (which is to say, when they share the domain name of the current request). */ if (is_ssl() && 'https' !== substr($image_baseurl, 0, 5) && parse_url($image_baseurl, PHP_URL_HOST) === $_SERVER['HTTP_HOST']) { $image_baseurl = set_url_scheme($image_baseurl, 'https'); } /* * Images that have been edited in WordPress after being uploaded will * contain a unique hash. Look for that hash and use it later to filter * out images that are leftovers from previous versions. */ $image_edited = preg_match('/-e[0-9]{13}/', wp_basename($image_src), $image_edit_hash); /** * Filters the maximum image width to be included in a 'srcset' attribute. * * @since 4.4.0 * * @param int $max_width The maximum image width to be included in the 'srcset'. Default '1600'. * @param array $size_array Array of width and height values in pixels (in that order). */ $max_srcset_image_width = apply_filters('max_srcset_image_width', 1600, $size_array); // Array to hold URL candidates. $sources = array(); /** * To make sure the ID matches our image src, we will check to see if any sizes in our attachment * meta match our $image_src. If no matches are found we don't return a srcset to avoid serving * an incorrect image. See #35045. */ $src_matched = false; /* * Loop through available images. Only use images that are resized * versions of the same edit. */ foreach ($image_sizes as $image) { $is_src = false; // Check if image meta isn't corrupted. if (!is_array($image)) { continue; } // If the file name is part of the `src`, we've confirmed a match. if (!$src_matched && false !== strpos($image_src, $dirname . $image['file'])) { $src_matched = $is_src = true; } // Filter out images that are from previous edits. if ($image_edited && !strpos($image['file'], $image_edit_hash[0])) { continue; } /* * Filters out images that are wider than '$max_srcset_image_width' unless * that file is in the 'src' attribute. */ if ($max_srcset_image_width && $image['width'] > $max_srcset_image_width && !$is_src) { continue; } // If the image dimensions are within 1px of the expected size, use it. if (wp_image_matches_ratio($image_width, $image_height, $image['width'], $image['height'])) { // Add the URL, descriptor, and value to the sources array to be returned. $source = array('url' => $image_baseurl . $image['file'], 'descriptor' => 'w', 'value' => $image['width']); // The 'src' image has to be the first in the 'srcset', because of a bug in iOS8. See #35030. if ($is_src) { $sources = array($image['width'] => $source) + $sources; } else { $sources[$image['width']] = $source; } } } /** * Filters an image's 'srcset' sources. * * @since 4.4.0 * * @param array $sources { * One or more arrays of source data to include in the 'srcset'. * * @type array $width { * @type string $url The URL of an image source. * @type string $descriptor The descriptor type used in the image candidate string, * either 'w' or 'x'. * @type int $value The source width if paired with a 'w' descriptor, or a * pixel density value if paired with an 'x' descriptor. * } * } * @param array $size_array Array of width and height values in pixels (in that order). * @param string $image_src The 'src' of the image. * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * @param int $attachment_id Image attachment ID or 0. */ $sources = apply_filters('wp_calculate_image_srcset', $sources, $size_array, $image_src, $image_meta, $attachment_id); // Only return a 'srcset' value if there is more than one source. if (!$src_matched || count($sources) < 2) { return false; } $srcset = ''; foreach ($sources as $source) { $srcset .= str_replace(' ', '%20', $source['url']) . ' ' . $source['value'] . $source['descriptor'] . ', '; } return rtrim($srcset, ', '); }
/** * Fix sources for large image * * @param array $sources { * One or more arrays of source data to include in the 'srcset'. * * @type array $width { * @type string $url The URL of an image source. * @type string $descriptor The descriptor type used in the image candidate string, * either 'w' or 'x'. * @type int $value The source width if paired with a 'w' descriptor, or a * pixel density value if paired with an 'x' descriptor. * } * } * @param array $size_array Array of width and height values in pixels (in that order). * @param string $image_src The 'src' of the image. * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * @param int $attachment_id Image attachment ID or 0. */ public function set_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id) { if (!isset($size_array[0]) || 860 !== $size_array[0]) { return $sources; } $image_sizes = $image_meta['sizes']; $src_matched = false; // Retrieve the uploads sub-directory from the full size image. $dirname = _wp_get_attachment_relative_path($image_meta['file']); if ($dirname) { $dirname = trailingslashit($dirname); } $image_baseurl = _wp_upload_dir_baseurl(); $image_baseurl = trailingslashit($image_baseurl) . $dirname; $image_edited = preg_match('/-e[0-9]{13}/', wp_basename($image_src), $image_edit_hash); // Get the width and height of the image. $image_width = (int) $size_array[0]; $image_height = (int) $size_array[1]; // Calculate the image aspect ratio. $image_ratio = $image_height / $image_width; /** * Filter the maximum image width to be included in a 'srcset' attribute. * * @since 4.4.0 * * @param int $max_width The maximum image width to be included in the 'srcset'. Default '1600'. * @param array $size_array Array of width and height values in pixels (in that order). */ $max_srcset_image_width = apply_filters('max_srcset_image_width', 1600, $size_array); foreach ($image_sizes as $image) { // If the file name is part of the `src`, we've confirmed a match. if (!$src_matched && false !== strpos($image_src, $dirname . $image['file'])) { $src_matched = true; } // Filter out images that are from previous edits. if ($image_edited && !strpos($image['file'], $image_edit_hash[0])) { continue; } /* * Filter out images that are wider than '$max_srcset_image_width' unless * that file is in the 'src' attribute. */ if ($max_srcset_image_width && $image['width'] > $max_srcset_image_width && false === strpos($image_src, $image['file'])) { continue; } // Calculate the new image ratio. if ($image['width']) { $image_ratio_compare = $image['height'] / $image['width']; } else { $image_ratio_compare = 0; } // If the new ratio differs by less than 0.002, use it. if (abs($image_ratio - $image_ratio_compare) < 0.005) { // Add the URL, descriptor, and value to the sources array to be returned. $sources[$image['width']] = array('url' => $image_baseurl . $image['file'], 'descriptor' => 'w', 'value' => $image['width']); } } return $sources; }