227 lines
6.4 KiB
PHP
227 lines
6.4 KiB
PHP
<?php
|
|
/**
|
|
* Image Optimizer Class
|
|
*
|
|
* Handles image optimization and WebP conversion
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class OptiCore_Image_Optimizer {
|
|
|
|
private static $instance = null;
|
|
|
|
public static function get_instance() {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
private function __construct() {
|
|
}
|
|
|
|
public function init() {
|
|
// Add WebP support
|
|
add_filter('the_content', array($this, 'convert_images_to_webp'), 999);
|
|
add_filter('post_thumbnail_html', array($this, 'convert_images_to_webp'), 999);
|
|
|
|
// Add responsive images
|
|
add_filter('wp_calculate_image_srcset', array($this, 'optimize_srcset'), 10, 5);
|
|
|
|
// Remove excessive image sizes
|
|
add_filter('intermediate_image_sizes_advanced', array($this, 'remove_unnecessary_image_sizes'));
|
|
|
|
// Add proper image dimensions
|
|
add_filter('the_content', array($this, 'add_image_dimensions'), 999);
|
|
}
|
|
|
|
/**
|
|
* Convert images to WebP if supported
|
|
*/
|
|
public function convert_images_to_webp($content) {
|
|
if (is_admin() || !$this->is_webp_supported()) {
|
|
return $content;
|
|
}
|
|
|
|
// Replace image sources with WebP versions if they exist
|
|
$content = preg_replace_callback(
|
|
'/<img([^>]+)src=[\'"]([^\'"]+)\.(jpg|jpeg|png)[\'"]([^>]*)>/i',
|
|
array($this, 'replace_with_webp'),
|
|
$content
|
|
);
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Replace image with WebP version
|
|
*/
|
|
private function replace_with_webp($matches) {
|
|
$full_tag = $matches[0];
|
|
$before = $matches[1];
|
|
$image_url = $matches[2];
|
|
$extension = $matches[3];
|
|
$after = $matches[4];
|
|
|
|
$webp_url = $image_url . '.webp';
|
|
|
|
// Check if WebP version exists
|
|
$upload_dir = wp_upload_dir();
|
|
$webp_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $webp_url);
|
|
|
|
if (file_exists($webp_path)) {
|
|
// Use picture element for better fallback
|
|
$picture = '<picture>';
|
|
$picture .= '<source srcset="' . esc_url($webp_url) . '" type="image/webp">';
|
|
$picture .= '<img' . $before . 'src="' . esc_url($image_url . '.' . $extension) . '"' . $after . '>';
|
|
$picture .= '</picture>';
|
|
|
|
return $picture;
|
|
}
|
|
|
|
return $full_tag;
|
|
}
|
|
|
|
/**
|
|
* Check if WebP is supported
|
|
*/
|
|
private function is_webp_supported() {
|
|
if (!isset($_SERVER['HTTP_ACCEPT'])) {
|
|
return false;
|
|
}
|
|
|
|
return strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false;
|
|
}
|
|
|
|
/**
|
|
* Optimize srcset
|
|
*/
|
|
public function optimize_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id) {
|
|
// Remove unnecessary sizes from srcset
|
|
$allowed_sizes = array('thumbnail', 'medium', 'large', 'full');
|
|
|
|
foreach ($sources as $width => $source) {
|
|
// Remove very similar sizes (within 10% difference)
|
|
foreach ($sources as $width2 => $source2) {
|
|
if ($width !== $width2 && abs($width - $width2) / $width < 0.1) {
|
|
unset($sources[$width2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $sources;
|
|
}
|
|
|
|
/**
|
|
* Remove unnecessary image sizes
|
|
*/
|
|
public function remove_unnecessary_image_sizes($sizes) {
|
|
// Remove sizes that are rarely used
|
|
$remove_sizes = array(
|
|
'medium_large',
|
|
'1536x1536',
|
|
'2048x2048',
|
|
);
|
|
|
|
foreach ($remove_sizes as $size) {
|
|
unset($sizes[$size]);
|
|
}
|
|
|
|
return $sizes;
|
|
}
|
|
|
|
/**
|
|
* Add image dimensions if missing
|
|
*/
|
|
public function add_image_dimensions($content) {
|
|
if (is_admin()) {
|
|
return $content;
|
|
}
|
|
|
|
// Add width and height attributes to images without them
|
|
$content = preg_replace_callback(
|
|
'/<img(?![^>]*(?:width|height))[^>]*src=[\'"]([^\'"]+)[\'"]([^>]*)>/i',
|
|
array($this, 'add_dimensions_to_image'),
|
|
$content
|
|
);
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Add dimensions to image tag
|
|
*/
|
|
private function add_dimensions_to_image($matches) {
|
|
$full_tag = $matches[0];
|
|
$image_url = $matches[1];
|
|
$after = $matches[2];
|
|
|
|
// Try to get image size
|
|
$upload_dir = wp_upload_dir();
|
|
$image_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $image_url);
|
|
|
|
if (file_exists($image_path)) {
|
|
$size = getimagesize($image_path);
|
|
if ($size) {
|
|
$full_tag = str_replace('<img', '<img width="' . $size[0] . '" height="' . $size[1] . '"', $full_tag);
|
|
}
|
|
}
|
|
|
|
return $full_tag;
|
|
}
|
|
|
|
/**
|
|
* Create WebP version of image
|
|
*/
|
|
public function create_webp_image($file_path) {
|
|
if (!file_exists($file_path)) {
|
|
return false;
|
|
}
|
|
|
|
$info = pathinfo($file_path);
|
|
$webp_path = $file_path . '.webp';
|
|
|
|
// Check if WebP already exists
|
|
if (file_exists($webp_path)) {
|
|
return $webp_path;
|
|
}
|
|
|
|
// Check if GD supports WebP
|
|
if (!function_exists('imagewebp')) {
|
|
return false;
|
|
}
|
|
|
|
$extension = strtolower($info['extension']);
|
|
$image = null;
|
|
|
|
// Load image based on type
|
|
switch ($extension) {
|
|
case 'jpg':
|
|
case 'jpeg':
|
|
$image = imagecreatefromjpeg($file_path);
|
|
break;
|
|
case 'png':
|
|
$image = imagecreatefrompng($file_path);
|
|
imagepalettetotruecolor($image);
|
|
imagealphablending($image, true);
|
|
imagesavealpha($image, true);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (!$image) {
|
|
return false;
|
|
}
|
|
|
|
// Convert to WebP
|
|
$result = imagewebp($image, $webp_path, 80);
|
|
imagedestroy($image);
|
|
|
|
return $result ? $webp_path : false;
|
|
}
|
|
}
|
|
|