Last active
January 7, 2025 14:12
-
-
Save vardumper/926bb5938efbbf9f596460f5c06db187 to your computer and use it in GitHub Desktop.
Convert Wordpress User Uploads to WebP and update DB references
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types = 1); | |
use WebPConvert\WebPConvert; | |
set_time_limit(0); | |
chdir(dirname(__DIR__)); // one level up to project root | |
// check requirements | |
if (! is_file('vendor/autoload.php')) { | |
echo "File vendor/autoload.php not found. Is the path correct?"; | |
exit(); | |
} | |
require ('vendor/autoload.php'); | |
// Neither is CLI nor CLI-Server: Execution is forbidden (for HTTP). This is superuser stuff | |
if (! in_array(php_sapi_name(), [ | |
'cli', | |
'cli-server' | |
])) { | |
http_response_code(403); | |
header('Content-Type: text/plain'); | |
echo 'Forbidden.'; | |
exit(); | |
} | |
if (! extension_loaded('gd') && ! extension_loaded('imagick') && ! extension_loaded('gmagick')) { | |
echo "WebP is not supported. You need one at least GD or one of ImageMagick, Gmagick, cwebp, etc"; | |
exit(); | |
} | |
if (! class_exists('WebPConvert\WebPConvert')) { | |
echo "WebPConvert class not found. Run `composer require rosell-dk/webp-convert` to add it to your composer.json\n"; | |
exit(); | |
} | |
// recursive file search by pattern in a given folder | |
function rsearch(string $folder, string $pattern): array | |
{ | |
$dir = new RecursiveDirectoryIterator($folder); | |
$ite = new RecursiveIteratorIterator($dir); | |
$files = new RegexIterator($ite, $pattern, RegexIterator::GET_MATCH); | |
$fileList = array(); | |
foreach ($files as $file) { | |
$fileList = array_merge($fileList, $file); | |
} | |
return $fileList; | |
} | |
// find (or guess) wordpress path | |
function get_wordpress_path(): string | |
{ | |
if (! is_file('vendor/composer/installed.json')) { | |
throw new \Exception('Is your project based on composer?'); | |
} | |
// find | |
$installed = json_decode(file_get_contents('vendor/composer/installed.json'), true); | |
if (isset($installed['versions']['johnpbloch/wordpress-core']['install-path'])) { | |
return realpath($installed['versions']['johnpbloch/wordpress-core']['install-path']); | |
} | |
// or guess | |
if (is_dir('wp-admin')) { | |
return getcwd(); | |
} | |
if (is_dir('wordpress/wp-admin')) { | |
return getcwd() . '/wordpress'; | |
} | |
return '.'; | |
} | |
// WebP configuration | |
$webp_options = [ | |
'metadata' => 'none', | |
'encoding' => 'lossless', | |
'show-report' => true, | |
'preset' => 'photo', | |
'alpha-quality' => 100, | |
'default-quality' => 100, | |
'max-quality' => 100, | |
'quality' => 100 | |
]; | |
$wordpress_path = get_wordpress_path(); | |
// load wordpress, especially for $wpdb and functions | |
define('WP_USE_THEMES', false); | |
require_once $wordpress_path . '/wp-load.php'; | |
require_once $wordpress_path . '/wp-admin/includes/ajax-actions.php'; | |
$paths = [ | |
$wordpress_path . '/wp-content/uploads', | |
$wordpress_path . '/another/directory' | |
]; | |
echo "Scanning folders for images...\n"; | |
$matches = []; | |
foreach ($paths as $path) { | |
if (! is_dir($path)) { | |
printf("Path %s not found.\n", $path); | |
continue; | |
} | |
$files = rsearch($path, '/^.*\.(jpe?g|png|bmp|tiff)$/i'); | |
$matches = array_merge($matches, $files); | |
} | |
$matches = array_filter($matches, "is_file"); | |
printf("Found %s images.\n", count($matches)); | |
$existed = 0; | |
$converted = 0; | |
foreach ($matches as $file) { | |
$pathinfo = pathinfo($file); | |
if ( is_file($pathinfo['dirname'] . '/' . $pathinfo['filename'] . '.webp')) { | |
$existed ++; | |
continue; | |
} | |
if (! is_writeable($pathinfo['dirname'])) { | |
echo $pathinfo['dirname'] . " not writeable.\n"; | |
continue; | |
} | |
// create WebP version of image | |
WebPConvert::convert($file, $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '.webp', $webp_options); | |
$results = $wpdb->get_results(sprintf("SELECT ID, post_content, post_excerpt FROM {$wpdb->posts} WHERE (`post_content` LIKE '%s' OR `post_excerpt` LIKE '%s') AND `post_type` IN ('post','page','product','product_variation');", "%" . $wpdb->esc_like($pathinfo['basename']) . "%", "%" . $wpdb->esc_like($pathinfo['basename']) . "%"), ARRAY_A); | |
if (count($results)) { | |
$sql = sprintf("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, '%s', '%s'), post_excerpt = REPLACE(post_excerpt, '%s', '%s') WHERE (`post_content` LIKE '%s' OR `post_excerpt` LIKE '%s') AND `post_type` IN ('post','page','product','product_variation');", $pathinfo['basename'], $pathinfo['filename'] . '.webp', $pathinfo['basename'], $pathinfo['filename'] . '.webp', "%" . $wpdb->esc_like($pathinfo['basename']) . "%", "%" . $wpdb->esc_like($pathinfo['basename']) . "%"); | |
$wpdb->query($sql); | |
printf("Updated image references in %s posts.\n", count($results)); | |
} | |
$results = $wpdb->get_results(sprintf("SELECT `ID`, `guid`, `post_mime_type` FROM {$wpdb->posts} WHERE `guid` LIKE '%s' AND `post_type` = 'attachment';", "%" . $wpdb->esc_like($pathinfo['basename']) . "%"), ARRAY_A); | |
if (count($results)) { | |
$sql = sprintf("UPDATE {$wpdb->posts} SET `guid` = REPLACE(`guid`, '%s', '%s'), `post_mime_type` = 'image/webp' WHERE `guid` LIKE '%s' AND `post_type` = 'attachment';", $pathinfo['basename'], $pathinfo['filename'] . '.webp', '%' . $wpdb->esc_like($pathinfo['basename']) . '%'); | |
$wpdb->query($sql); | |
printf("Updated guid, post_mime_type in %s attachments.\n", count($results)); | |
} | |
$wpdb->query(sprintf("UPDATE {$wpdb->postmeta} SET `meta_value` = REPLACE(`meta_value`, '%s', '%s') WHERE `meta_value` LIKE '%s' AND `meta_key` = '_wp_attached_file';", $pathinfo['basename'], $pathinfo['filename'] . '.webp', "%" . $wpdb->esc_like($pathinfo['basename']) . "%")); | |
$_wp_attachment_metadata = $wpdb->get_row(sprintf("SELECT `post_id`, `meta_value` FROM {$wpdb->postmeta} WHERE `meta_value` LIKE '%s' AND `meta_key` = '_wp_attachment_metadata';", '%' . $wpdb->esc_like($pathinfo['basename']) . '%'), ARRAY_A); | |
if (! is_null($_wp_attachment_metadata)) { | |
$old = unserialize($_wp_attachment_metadata['meta_value']); | |
$new = $old; | |
$new['file'] = is_string($new['file']) ? str_replace($pathinfo['basename'], $pathinfo['filename'] . '.webp', $new['file']) : $new['file']; | |
foreach ($new['sizes'] as $sizename => $size) { | |
$new['sizes'][$sizename]['file'] = is_string($size['file']) ? str_replace($pathinfo['basename'], $pathinfo['filename'] . '.webp', $size['file']) : $size['file']; | |
if ($old['sizes'][$sizename]['file'] !== $new['sizes'][$sizename]['file']) { | |
$new['sizes'][$sizename]['mime-type'] = 'image/webp'; | |
} | |
} | |
$wpdb->query(sprintf("UPDATE {$wpdb->postmeta} SET `meta_value` = '%s' WHERE `post_id` = %s AND `meta_key` = '_wp_attachment_metadata';", $wpdb->escape(serialize($new)), $_wp_attachment_metadata['post_id'])); | |
} | |
$converted ++; | |
} | |
printf("Converted %s images and skipped %s that already existed.\n", $converted, $existed); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment