<?php defined('ABSPATH') || exit; class WC_Regenerate_Images { protected static $background_process; protected static $regenerate_size; public static function init() { add_action('image_get_intermediate_size', array(__CLASS__, 'filter_image_get_intermediate_size'), 10, 3); add_filter('wp_generate_attachment_metadata', array(__CLASS__, 'add_uncropped_metadata')); add_filter('wp_get_attachment_image_src', array(__CLASS__, 'maybe_resize_image'), 10, 4); if (method_exists('Jetpack', 'is_module_active') && Jetpack::is_module_active('photon')) { return; } if (apply_filters('woocommerce_background_image_regeneration', true)) { include_once WC_ABSPATH . 'includes/class-wc-regenerate-images-request.php'; self::$background_process = new WC_Regenerate_Images_Request(); add_action('admin_init', array(__CLASS__, 'regenerating_notice')); add_action('woocommerce_hide_regenerating_thumbnails_notice', array(__CLASS__, 'dismiss_regenerating_notice')); if (!is_multisite()) { add_action('customize_save_after', array(__CLASS__, 'maybe_regenerate_images')); add_action('after_switch_theme', array(__CLASS__, 'maybe_regenerate_images')); } } } public static function filter_image_get_intermediate_size($data, $attachment_id, $size) { if (!is_string($size) || !in_array($size, apply_filters('woocommerce_image_sizes_to_resize', array('woocommerce_thumbnail', 'woocommerce_gallery_thumbnail', 'woocommerce_single', 'shop_thumbnail', 'shop_catalog', 'shop_single')), true)) { return $data; } if (!isset($data['width'], $data['height'])) { return $data; } if (!self::image_size_matches_settings($data, $size)) { if (method_exists('Jetpack', 'is_module_active') && Jetpack::is_module_active('photon')) { return false; } else { $size_data = wc_get_image_size($size); return image_get_intermediate_size($attachment_id, array(absint($size_data['width']), absint($size_data['height']))); } } return $data; } public static function add_uncropped_metadata($meta_data) { $size_data = wc_get_image_size('woocommerce_thumbnail'); if (isset($meta_data['sizes'], $meta_data['sizes']['woocommerce_thumbnail'])) { $meta_data['sizes']['woocommerce_thumbnail']['uncropped'] = empty($size_data['height']); } return $meta_data; } protected static function image_size_matches_settings($image, $size) { $target_size = wc_get_image_size($size); $uncropped = '' === $target_size['width'] || '' === $target_size['height']; if (!$uncropped) { $ratio_match = wp_image_matches_ratio($image['width'], $image['height'], $target_size['width'], $target_size['height']); if ($ratio_match && $target_size['width'] !== $image['width']) { return false; } if ($ratio_match && $target_size['height'] && $target_size['height'] !== $image['height']) { return false; } } if ($uncropped && empty($image['uncropped'])) { return false; } return true; } public static function regenerating_notice() { if (!self::$background_process->is_running()) { WC_Admin_Notices::add_notice('regenerating_thumbnails'); } else { WC_Admin_Notices::remove_notice('regenerating_thumbnails'); } } public static function dismiss_regenerating_notice() { if (self::$background_process) { self::$background_process->kill_process(); $log = wc_get_logger(); $log->info(__('Cancelled product image regeneration job.', 'woocommerce'), array('source' => 'wc-image-regeneration')); } WC_Admin_Notices::remove_notice('regenerating_thumbnails'); } public static function maybe_regenerate_images() { $size_hash = md5(wp_json_encode(array(wc_get_image_size('thumbnail'), wc_get_image_size('single'), wc_get_image_size('gallery_thumbnail')))); if (update_option('woocommerce_maybe_regenerate_images_hash', $size_hash)) { self::queue_image_regeneration(); } } public static function maybe_resize_image($image, $attachment_id, $size, $icon) { if (!apply_filters('woocommerce_resize_images', true)) { return $image; } if (!$image || !in_array($size, apply_filters('woocommerce_image_sizes_to_resize', array('woocommerce_thumbnail', 'woocommerce_gallery_thumbnail', 'woocommerce_single', 'shop_thumbnail', 'shop_catalog', 'shop_single')), true)) { return $image; } $target_size = wc_get_image_size($size); $image_width = $image[1]; $image_height = $image[2]; $ratio_match = false; $target_uncropped = '' === $target_size['width'] || '' === $target_size['height'] || !$target_size['crop']; if ($target_uncropped) { $full_size = self::get_full_size_image_dimensions($attachment_id); if (!$full_size || !$full_size['width'] || !$full_size['height']) { return $image; } $ratio_match = wp_image_matches_ratio($image_width, $image_height, $full_size['width'], $full_size['height']); } else { $ratio_match = wp_image_matches_ratio($image_width, $image_height, $target_size['width'], $target_size['height']); } if (!$ratio_match) { $full_size = self::get_full_size_image_dimensions($attachment_id); if (!$full_size) { return $image; } if ($image_width === $target_size['width'] && $full_size['height'] < $target_size['height']) { return $image; } if ($image_height === $target_size['height'] && $full_size['width'] < $target_size['width']) { return $image; } if ($full_size['height'] < $target_size['height'] && $full_size['width'] < $target_size['width']) { return $image; } return self::resize_and_return_image($attachment_id, $image, $size, $icon); } return $image; } private static function get_full_size_image_dimensions($attachment_id) { $imagedata = wp_get_attachment_metadata($attachment_id); if (!$imagedata) { return array(); } if (!isset($imagedata['file']) && isset($imagedata['sizes']['full'])) { $imagedata['height'] = $imagedata['sizes']['full']['height']; $imagedata['width'] = $imagedata['sizes']['full']['width']; } return array('width' => $imagedata['width'], 'height' => $imagedata['height']); } public static function is_regeneratable($attachment) { if ('site-icon' === get_post_meta(is_object($attachment) ? $attachment->ID : $attachment, '_wp_attachment_context', true)) { return false; } if (wp_attachment_is_image($attachment)) { return true; } return false; } public static function adjust_intermediate_image_sizes($sizes) { return array(self::$regenerate_size); } private static function get_image($fullsizepath, $thumbnail_width, $thumbnail_height, $crop) { list($fullsize_width, $fullsize_height) = getimagesize($fullsizepath); $dimensions = image_resize_dimensions($fullsize_width, $fullsize_height, $thumbnail_width, $thumbnail_height, $crop); $editor = wp_get_image_editor($fullsizepath); if (is_wp_error($editor)) { return false; } if (!$dimensions || !is_array($dimensions)) { return false; } list(, , , , $dst_w, $dst_h) = $dimensions; $suffix = "{$dst_w}x{$dst_h}"; $file_ext = strtolower(pathinfo($fullsizepath, PATHINFO_EXTENSION)); return array('filename' => $editor->generate_filename($suffix, null, $file_ext), 'width' => $dst_w, 'height' => $dst_h); } private static function resize_and_return_image($attachment_id, $image, $size, $icon) { if (!self::is_regeneratable($attachment_id)) { return $image; } $fullsizepath = get_attached_file($attachment_id); if (false === $fullsizepath || is_wp_error($fullsizepath) || !file_exists($fullsizepath)) { return $image; } if (!function_exists('wp_crop_image')) { include ABSPATH . 'wp-admin/includes/image.php'; } self::$regenerate_size = is_customize_preview() ? $size . '_preview' : $size; if (is_customize_preview()) { $image_size = wc_get_image_size($size); add_image_size(self::$regenerate_size, absint($image_size['width']), absint($image_size['height']), $image_size['crop']); $thumbnail = self::get_image($fullsizepath, absint($image_size['width']), absint($image_size['height']), $image_size['crop']); if ($thumbnail && file_exists($thumbnail['filename'])) { $wp_uploads = wp_upload_dir(null, false); $wp_uploads_dir = $wp_uploads['basedir']; $wp_uploads_url = $wp_uploads['baseurl']; return array(0 => str_replace($wp_uploads_dir, $wp_uploads_url, $thumbnail['filename']), 1 => $thumbnail['width'], 2 => $thumbnail['height']); } } $metadata = wp_get_attachment_metadata($attachment_id); if (!is_array($metadata)) { $metadata = array(); } add_filter('intermediate_image_sizes', array(__CLASS__, 'adjust_intermediate_image_sizes')); $new_metadata = wp_generate_attachment_metadata($attachment_id, $fullsizepath); remove_filter('intermediate_image_sizes', array(__CLASS__, 'adjust_intermediate_image_sizes')); if (is_wp_error($new_metadata) || empty($new_metadata)) { return $image; } if (isset($new_metadata['sizes'][self::$regenerate_size])) { $metadata['sizes'][self::$regenerate_size] = $new_metadata['sizes'][self::$regenerate_size]; wp_update_attachment_metadata($attachment_id, $metadata); } $new_image = self::unfiltered_image_downsize($attachment_id, self::$regenerate_size); return $new_image ? $new_image : $image; } private static function unfiltered_image_downsize($attachment_id, $size) { remove_action('image_get_intermediate_size', array(__CLASS__, 'filter_image_get_intermediate_size'), 10, 3); $return = image_downsize($attachment_id, $size); add_action('image_get_intermediate_size', array(__CLASS__, 'filter_image_get_intermediate_size'), 10, 3); return $return; } public static function queue_image_regeneration() { global $wpdb; self::$background_process->kill_process(); $images = $wpdb->get_results("SELECT ID\n\t\t\tFROM {$wpdb->posts}\n\t\t\tWHERE post_type = 'attachment'\n\t\t\tAND post_mime_type LIKE 'image/%'\n\t\t\tORDER BY ID DESC"); foreach ($images as $image) { self::$background_process->push_to_queue(array('attachment_id' => $image->ID)); } self::$background_process->save()->dispatch(); } } add_action('init', array('WC_Regenerate_Images', 'init'));