Misagh Mi
Misagh Mi

Reputation: 81

WordPress Theme Function For Compress and Covert Uploading Images to WebP Format Using Imagick

I have written this function code for compressing and converting uploading images to WebP format using Imagick and adding them to media library and delete the original uploading file format.

I've written this function at WordPress Theme Functions (functions.php):

/*
 * Compress and convert to WebP for uploading images
 */
function compress_and_convert_images_to_webp($file) {
    // Check if file type is supported
    $supported_types = ['image/jpeg', 'image/jpg', 'image/png'];
    if (!in_array($file['type'], $supported_types)) {
        return $file;
    }

    // Get the path to the upload directory
    $wp_upload_dir = wp_upload_dir();

    // Set up the file paths
    $old_file_path = $file['file'];
    $file_name = basename($file['file']);
    $webp_file_path = $wp_upload_dir['path'] . '/' . pathinfo($file_name, PATHINFO_FILENAME) . '.webp';
    
    // Check if file is already a WebP image
    if (pathinfo($old_file_path, PATHINFO_EXTENSION) === 'webp') {
        return $file;
    }

    // Load the image using Imagick
    $image = new Imagick($old_file_path);

    // Compress the image
    $quality = 75; // Adjust this value to control the compression level
    $image->setImageCompressionQuality($quality);
    $image->stripImage(); // Remove all profiles and comments to reduce file size

    // Convert the image to WebP
    $image->setImageFormat('webp');
    $image->setOption('webp:lossless', 'false');
    $image->setOption('webp:method', '6'); // Adjust this value to control the compression level for WebP
    $image->writeImage($webp_file_path);
    
    // Allow WebP mime type
    add_filter('upload_mimes', function($mimes) {
        $mimes['webp'] = 'image/webp';
        return $mimes;
    });
    
    // Set file permissions to 0644
    chmod($webp_file_path, 0644);

    // Add the new WebP image to the media library
    $attachment_id = media_handle_upload(pathinfo($file_name, PATHINFO_FILENAME), 0, [
        'post_mime_type' => 'image/webp',
        'file' => $webp_file_path
    ]);
    
    if (is_wp_error($attachment_id)) {
        error_log("The Attachment ID Error is: " . $attachment_id->get_error_message());
    }

    // Delete the old image file
    unlink($old_file_path);

    // Update the attachment metadata with the WebP image URL
    update_post_meta($attachment_id, '_wp_attached_file', substr($webp_file_path, strlen($wp_upload_dir['basedir']) + 1));
    
    // Return the updated file information
    return [
        'file' => $webp_file_path,
        'url' => wp_get_attachment_url($attachment_id),
        'type' => 'image/webp',
    ];
}
add_filter('wp_handle_upload', 'compress_and_convert_images_to_webp');

This code works fine and compress and convert uploading image files and show them in media library and deletes uploading original format but it raise warnings every time I upload new file.

I've searched about warnings and they are about "$attachment_id" variable problem:

as you can see this variable uses "media_handle_upload" function for getting new attachment ID but it returns this error (WP_ERROR object):

"Specified file failed upload test.".

the warning messages are:

PHP Warning:  Trying to access array offset on value of type null in wp-admin/includes/file.php on line 906

PHP Deprecated:  is_uploaded_file(): Passing null to parameter #1 ($filename) of type string is deprecated in wp-admin/includes/file.php on line 906

PHP Warning:  Object of class WP_Error could not be converted to int in wp-includes/post.php on line 6590

What should I do?

Upvotes: 0

Views: 672

Answers (2)

Davidp98
Davidp98

Reputation: 21

Your code helped me identify that this is now a supported function in the WP Core (6.4 +). I don't know if it will help you but here is my code with which I achieve the same thing you were looking for.

add_filter('image_editor_output_format', function( $formats ) {

$formats['image/jpeg'] = 'image/webp';
$formats['image/png'] = 'image/webp';

return $formats;
});

Filter to set the image quality:

function your_function_name_76194055($quality) {

return 70; //Number between 1 - 100

}

add_filter('wp_editor_set_quality', 'your_function_name_76194055');

It is mandatory that the server has ImageMagick compiled with native WEBP support otherwise it will not work. I also made a function to save disk space deleting the original uploaded file and replacing it with the large file that WP generates, let me know and I'll share it.

Upvotes: 2

Misagh Mi
Misagh Mi

Reputation: 81

Finally I've found out the problem:

as I said before, this function works fine but has some warnings. To resolve the warnings I omitted the parts that related to media_handle_upload and update_post_meta.

The final code is:

/**
 * Compress and convert to WebP for uploading images
 */
function compress_and_convert_images_to_webp($file) {
    // Check if file type is supported
    $supported_types = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'];
    if (!in_array($file['type'], $supported_types)) {
        return $file;
    }

    // Get the path to the upload directory
    $wp_upload_dir = wp_upload_dir();

    // Set up the file paths
    $old_file_path = $file['file'];
    $file_name = basename($file['file']);
    $webp_file_path = $wp_upload_dir['path'] . '/' . pathinfo($file_name, PATHINFO_FILENAME) . '.webp';

    // Check if file is already a WebP image
    if (pathinfo($old_file_path, PATHINFO_EXTENSION) === 'webp') {
        return $file;
    }

    // Load the image using Imagick
    $image = new Imagick($old_file_path);

    // Compress the image
    $quality = 75; // Adjust this value to control the compression level
    $image->setImageCompressionQuality($quality);
    $image->stripImage(); // Remove all profiles and comments to reduce file size

    // Convert the image to WebP
    $image->setImageFormat('webp');
    $image->setOption('webp:lossless', 'false');
    $image->setOption('webp:method', '6'); // Adjust this value to control the compression level for WebP
    $image->writeImage($webp_file_path);

    // Delete the old image file
    unlink($old_file_path);

    // Return the updated file information
    return [
        'file' => $webp_file_path,
        'url' => $wp_upload_dir['url'] . '/' . basename($webp_file_path),
        'type' => 'image/webp',
    ];
}
add_filter('wp_handle_upload', 'compress_and_convert_images_to_webp');

Upvotes: 1

Related Questions