File: /wordpress/mu-plugins/photon-subsizes/photon-subsizes.php
<?php
/**
* Plugin Name: Photon Image Subsizes
* Description: Eliminate the storage cost of intermediate image sizes by delegating requests to Photon.
* Version: 1.0.0
*/
require_once __DIR__ . '/photon-subsizes-cli.php';
define( 'PHOTON_SUBSIZES_FILE_EXTENSIONS', array( '.gif', '.jpg', '.jpeg', '.png', '.webp' ) );
// Use a large priority to increase the chance we can override a custom image editor
add_filter( 'wp_image_editors', 'photon_subsizes_override_image_editors', 99999 );
// TODO: Look into disabling scaled images (apparently for things like retina displays)
// TODO: Only add crop settings query params for standard uploads locations
// Use a large priority to allow the Jetpack photon module to replace URLs first.
// Otherwise, we augment URLs that Jetpack would have remapped to photon,
// and it undermines Jetpack's application of subsize crop settings.
add_filter( 'wp_calculate_image_srcset', 'photon_subsizes_calculate_image_srcset', 99999 );
// Use a large priority to allow other plugins to add their own image_downsize filter
// response before we augment it with photon-subsizes query params
add_filter( 'image_downsize', 'photon_subsizes_image_downsize', 99999, 3 );
add_filter( 'image_get_intermediate_size', 'photon_subsizes_image_get_intermediate_size', 99999, 2 );
add_action( 'plugins_loaded', function () {
if ( false === has_filter( 'the_content', array( 'Jetpack_Photon', 'filter_the_content' ) ) ) {
add_filter( 'the_content', 'photon_subsizes_filter_the_content' );
}
} );
/**
* Override list of image editors with compatible editors that do not generate image subsizes
*
* @param string[] $image_editors Image editor class names
* @return string[] Image editor classes that extend the original classes and disable image subsize generation
*/
function photon_subsizes_override_image_editors( $image_editors ) {
// Server features required by photon-subsizes won't work for non-standard uploads locations
if ( photon_subsizes_site_has_standard_uploads_location() ) {
return array_map( 'photon_subsizes_declare_image_editor', $image_editors );
}
return $image_editors;
}
function photon_subsizes_site_has_standard_uploads_location() {
// Based on
// https://github.com/WordPress/wordpress-develop/blob/c7f5af72dcc4f36365656fdbfccfa0fb51fefd1f/src/wp-includes/default-constants.php#L156
$default_uploads_baseurl = network_site_url( '/wp-content/uploads/' );
$default_uploads_basedir = '/srv/htdocs/wp-content/uploads/';
$uploads = wp_get_upload_dir();
return (
// Using trailingslashit() to make sure we avoid false positives like
// `/wp-content/uploads-enhanced` being seen as a descendant of `/wp-content/uploads`
0 === strpos( trailingslashit( $uploads['basedir'] ), trailingslashit( $default_uploads_basedir ) ) &&
0 === strpos( trailingslashit( $uploads['baseurl'] ), trailingslashit( $default_uploads_baseurl ) )
);
}
/**
* Answers whether a path is for a photon-supported file type
*
* @param string $path Path to an image file
* @return boolean Whether the file extension represents a photon-supported file type
*/
function photon_subsizes_has_supported_image_extension( $path ) {
foreach ( PHOTON_SUBSIZES_FILE_EXTENSIONS as $extension ) {
// TODO: FIX: This should be case-insensitive.
if ( photon_subsizes_string_ends_with( $path, $extension ) ) {
return true;
}
}
return false;
}
/**
* Answers whether the specified MIME type is supported by Photon
*
* @param string $mime_type The MIME type
*
* @return boolean Whether the MIME type is supported by Photon
*/
function photon_subsizes_is_photon_mime_type( $mime_type ) {
return false !== $mime_type && (
"image/gif" === $mime_type ||
"image/png" === $mime_type ||
"image/jpeg" === $mime_type ||
"image/webp" === $mime_type
);
}
/**
* Declare an image editor class to extend the specified image editor to stop saving image subsizes
*
* @param string $editor_parent_class Image editor class name
* @return string Name of the derived, overriding image editor class
*/
function photon_subsizes_declare_image_editor( $editor_parent_class ) {
if ( ! class_exists( $editor_parent_class ) ) {
return false;
}
$editor_subclass = "Photon_Subsizes_$editor_parent_class";
if ( class_exists( $editor_subclass ) ) {
return $editor_subclass;
}
if ( 'Photon_Subsizes_WP_Image_Editor_Imagick' === $editor_subclass ) {
require_once __DIR__ . '/class.photon-subsizes-wp-image-editor-imagick.php';
return $editor_subclass;
} else if ( 'Photon_Subsizes_WP_Image_Editor_GD' === $editor_subclass ) {
require_once __DIR__ . '/class.photon-subsizes-wp-image-editor-gd.php';
return $editor_subclass;
} else if ( method_exists( $editor_parent_class, 'make_subsize' ) ) {
require_once __DIR__ . '/trait.photon-subsizes-image-editor-with-make-subsize.php';
$editor_trait = 'Photon_Subsizes_Image_Editor_With_Make_Subsize';
} else {
require_once __DIR__ . '/trait.photon-subsizes-image-editor.php';
$editor_trait = 'Photon_Subsizes_Image_Editor';
}
// Validate everything necessary for eval() even if we've checked it before.
// That way, earlier code may change without compromising the security of the eval.
if (
! class_exists( $editor_subclass ) &&
class_exists( $editor_parent_class ) &&
is_subclass_of( $editor_parent_class, 'WP_Image_Editor' ) &&
trait_exists( $editor_trait )
) {
// ATTENTION: eval() is incredibly dangerous but appears to be necessary here
// because we are extending an image editor class which is determined at runtime.
eval(
'class ' . $editor_subclass . ' extends ' . $editor_parent_class . '{
use ' . $editor_trait . ';
}'
);
return $editor_subclass;
}
return false;
}
/**
* Filter `calculate_image_srcset` to add photon subsizes query params to image URLs where needed
*
* @param array $source Array of srcset items which have 'url', 'descriptor', and 'value' entries
*
* @return array Array of augmented srcset info
*/
function photon_subsizes_calculate_image_srcset( $sources ) {
if ( is_array( $sources ) ) {
foreach ( $sources as &$source ) {
$source['url'] = photon_subsizes_map_url( $source['url'] );
}
}
return $sources;
}
/**
* Filter `image_downsize` to add crop settings to image URLs
*
* @param bool|array $downsize Whether to short-circuit the image_downsize
* @param int $attachment_id The attachment ID
* @param array|string $size Image size name or array of width and height values
*
* @return array An array of image downsize info with the image URL augmented with
* photon subsize query params (if needed)
*/
function photon_subsizes_image_downsize( $downsize, $attachment_id, $size ) {
if ( false === $downsize ) {
$filter_priority = has_filter( 'image_downsize', __FUNCTION__ );
if ( is_int( $filter_priority ) ) {
remove_filter( 'image_downsize', __FUNCTION__, $filter_priority );
}
$downsize = image_downsize( $attachment_id, $size );
if ( is_int( $filter_priority ) ) {
add_filter( 'image_downsize', __FUNCTION__, $filter_priority, 3 );
}
}
if ( is_array( $downsize ) && isset( $downsize[0] ) && 'string' === gettype( $downsize[0] ) ) {
$downsize[0] = photon_subsizes_map_url( $downsize[0] );
}
return $downsize;
}
/**
* Filter `image_get_intermediate_size` to add crop settings to image URLs
*
* @param array $data Associative array of image data
* @param int $post_id Attachment ID
*
* @return array An array of image data that includes an image subsize URL
*/
function photon_subsizes_image_get_intermediate_size( $data, $post_id ) {
if ( ! isset( $data['mime-type'] ) || ! photon_subsizes_is_photon_mime_type( $data['mime-type'] ) ) {
return $data;
}
$url = false;
if ( isset( $data['url'] ) ) {
$url = photon_subsizes_map_url( $data['url'] );
} else if (
isset( $data['file'], $data['width'], $data['height'] ) && (
( isset( $data['virtual'] ) && $data['virtual'] ) ||
( isset( $data['path'] ) && 0 === strncmp( $data['path'], '/srv/htdocs/', strlen( '/srv/htdocs/' ) ) )
)
) {
$url = photon_subsizes_get_subsize_url( $post_id, $data['file'], $data['width'], $data['height'] );
}
if ( false !== $url ) {
$data['url'] = $url;
}
return $data;
}
/**
* Filter `the_content` to add crop settings to photon subsize URLs
*
* @param string $content The content to be filtered
* @return string The resulting content with updated image URLs
*/
function photon_subsizes_filter_the_content( $content ) {
$img_tag_pattern =
'#' .
'(?P<head><(?:img|amp-img|amp-anim)[^>]*?\s+?src=["|\'])' .
'(?P<url>[^\s]+?)' .
'(?P<tail>["|\'].*?>)' .
'#is';
$match = array();
$match_offset = 0;
while (
1 === preg_match( $img_tag_pattern, $content, $match, PREG_OFFSET_CAPTURE, $match_offset )
) {
$original_offset = $match[0][1];
$original_length = strlen( $match[0][0] );
$head = $match['head'][0];
$url = $match['url'][0];
$tail = $match['tail'][0];
$new_img_tag = $head . photon_subsizes_map_url( $url ) . $tail;
$new_length = strlen( $new_img_tag );
$content = substr_replace( $content, $new_img_tag, $original_offset, $original_length );
$match_offset = $original_offset + $new_length;
}
return $content;
}
/**
* Map an image URL to a URL with crop settings query param (if needed)
*
* @param string $url The image URL
* @return string The image URL with a crop settings query param added
* if it refers to a cropped subsize
*/
function photon_subsizes_map_url( $url ) {
static $upload_dir = null;
if ( null === $upload_dir ) {
$upload_dir = wp_get_upload_dir();
$upload_dir['basedir'] = untrailingslashit( $upload_dir['basedir'] );
$upload_dir['baseurl'] = untrailingslashit( $upload_dir['baseurl'] );
}
if ( ! empty( $upload_dir['error'] ) ) {
return $url;
}
if ( ! photon_subsizes_string_starts_with( $url, $upload_dir['baseurl'] ) ) {
return $url;
}
if ( 1 === preg_match( '#[\?&]crop=#', $url ) ) {
return $url;
}
$upload_relative_url = substr( $url, strlen( $upload_dir['baseurl'] ) );
$url_path = parse_url( $upload_relative_url, PHP_URL_PATH );
if ( empty( $url_path ) ) {
return $url;
}
if ( file_exists( "{$upload_dir['basedir']}{$url_path}" ) ) {
return $url;
}
if ( ! photon_subsizes_has_supported_image_extension( $url_path ) ) {
return $url;
}
$pattern = '#^(?<base>.+)-(?<width>\d+)x(?<height>\d+)(?<ext>' . implode( '|', PHOTON_SUBSIZES_FILE_EXTENSIONS ) . ')$#i';
$match = array();
if ( 0 === preg_match( $pattern, $url_path, $match ) ) {
return $url;
}
if ( ! file_exists( "{$upload_dir['basedir']}{$match['base']}{$match['ext']}" ) ) {
return $url;
}
if ( ! photon_subsizes_is_cropped_subsize( $match['width'], $match['height'] ) ) {
return $url;
}
return add_query_arg( 'crop', '1', $url );
}
/**
* Get a subsize URL from an attachment post ID and a subsize filename
*
* @param int $attachment_post_id An attachment post ID
* @param string $subsize_filename The subsize filename
* @param int $width Subsize width
* @param int $height Subsize height
*
* @return string The subsize URL
*/
function photon_subsizes_get_subsize_url( $attachment_post_id, $subsize_filename, $width, $height ) {
$attachment_url = wp_get_attachment_url( $attachment_post_id );
if ( false === $attachment_url ) {
return false;
}
$base_url = dirname( $attachment_url );
$subsize_url = "{$base_url}/{$subsize_filename}";
$crop_settings = photon_subsizes_get_crop_settings();
$dimensions = "{$width}x{$height}";
if ( isset( $crop_settings[ $dimensions ] ) && $crop_settings[ $dimensions ] ) {
$subsize_url = add_query_arg( 'crop', '1', $subsize_url );
}
return $subsize_url;
}
/**
* Compile a map of subsize crop settings by <width>x<height> key
*
* @return array A map of subsize crop settings by <width>x<height> key
*/
function photon_subsizes_get_crop_settings() {
static $settings = null;
if ( null !== $settings ) {
return $settings;
}
$settings = array();
foreach ( wp_get_registered_image_subsizes() as $subsize ) {
if ( ! isset( $subsize['crop'] ) ) {
$crop_setting = false;
} else if ( is_array( $subsize['crop'] ) ) {
if (
2 === count( $subsize['crop'] ) &&
in_array( $subsize['crop'][0], array( 'left', 'center', 'right' ) ) &&
in_array( $subsize['crop'][1], array( 'top', 'center', 'bottom' ) )
) {
$crop_setting = $subsize['crop'];
} else {
// Default to cropped because it looks like there are crop settings
// even if they don't appear valid
$crop_setting = true;
}
} else {
$crop_setting = !! $subsize['crop'];
}
$settings[ "{$subsize['width']}x{$subsize['height']}" ] = $crop_setting;
}
return $settings;
}
/**
* Answers whether the given dimensions correspond to a cropped subsize
*
* @param int $width Subsize width
* @param int $height Subsize height
*
* @return boolean Whether the subsize should be cropped
*/
function photon_subsizes_is_cropped_subsize( $width, $height ) {
$settings = photon_subsizes_get_crop_settings();
$key = "{$width}x{$height}";
return isset( $settings[ $key ] ) && $settings[ $key ];
}
/**
* Answers whether a string starts with another string
*
* @param string $haystack String to examine
* @param string $needle String to search for
* @return boolean Whether the string starts with the specified string
*/
function photon_subsizes_string_starts_with( $haystack, $needle ) {
return strncmp( $haystack, $needle, strlen( $needle ) ) === 0;
}
/**
* Answers whether a string ends with another string
*
* @param string $haystack String to examine
* @param string $needle String to search for
* @return boolean Whether the string ends with the specified string
*/
function photon_subsizes_string_ends_with( $haystack, $needle ) {
return substr( $haystack, -strlen( $needle ) ) === $needle;
}