Turns out, image_get_intermediate_size()
was easy enough to adapt to my needs. Instead of using wp_get_attachment_metadata
to get an existing image's available sizes I use my own function to get all available image sizes (essentially the same as the function proposed here). I then simply return the name rather than the array of meta data.
It works, I need to do some more thorough testing though and may need to make some changes (not sure I actually want to return 'thumbnail' for sizes smaller than 'thumbnail' for example)...
Here is my entire function (it is a static method of a utility class):
/**
* Return the closest named size from an array of width and height values.
*
* Based off of WordPress's image_get_intermediate_size()
* If the size matches an existing size then it will be used. If there is no
* direct match, then the nearest image size larger than the specified size
* will be used. If nothing is found, then the function will return false.
* Uses get_image_sizes() to get all available sizes.
*
* @param array|string $size Image size. Accepts an array of width and height (in that order).
* @return false|string $data named image size e.g. 'thumbnail'.
*/
public static function get_named_size( $size ) {
$image_sizes = self::get_image_sizes();
$data = array();
// Find the best match when '$size' is an array.
if ( is_array( $size ) ) {
$candidates = array();
foreach ( $image_sizes as $_size => $data ) {
// If there's an exact match to an existing image size, short circuit.
if ( $data['width'] == $size[0] && $data['height'] == $size[1] ) {
$candidates[ $data['width'] * $data['height'] ] = array( $_size, $data );
break;
}
// If it's not an exact match, consider larger sizes with the same aspect ratio.
if ( $data['width'] >= $size[0] && $data['height'] >= $size[1] ) {
if ( wp_image_matches_ratio( $data['width'], $data['height'], $size[0], $size[1] ) ) {
$candidates[ $data['width'] * $data['height'] ] = array( $_size, $data );
}
}
}
if ( ! empty( $candidates ) ) {
// Sort the array by size if we have more than one candidate.
if ( 1 < count( $candidates ) ) {
ksort( $candidates );
}
$data = array_shift( $candidates );
$data = $data[0];
/*
* When the size requested is smaller than the thumbnail dimensions, we
* fall back to the thumbnail size to maintain backwards compatibility with
* pre 4.6 versions of WordPress.
*/
} elseif ( ! empty( $image_sizes['thumbnail'] ) && $image_sizes['thumbnail']['width'] >= $size[0] && $image_sizes['thumbnail']['width'] >= $size[1] ) {
$data = 'thumbnail';
} else {
return false;
}
} elseif ( ! empty( $image_sizes[ $size ] ) ) {
$data = $size;
}
// If we still don't have a match at this point, return false.
if ( empty( $data ) ) {
return false;
}
return $data;
}
Which I can use (close enough to) how I initially wanted:
$image_size = My_Utility_Class::get_named_size( array( 150, 150 ) ); // returns 'thumbnail'