Say you've got a list of external images to output that may be huge and mongous. In my case it was images of houses up for rent from a big-ass XML feed. It's unwise to serve these images up directly to users given that huge payloads are generally frowned upon. What you need, then, is a function that takes an image URL and caches it locally as a resized variant.
Yeah, there are CDNs, plugins, and other such things that will do this sort of stuff for you, but what's the fun in buying into one of those when you could do it yourself for free? Also, why are we using WP's system instead of spinning up Imagick or a similar library? I dunno; maybe one's not available or you've forgotten how to use them and don't feel like looking them up. There's rarely one right answer when coding. 🤷♀️
function cache_em_up_yo(string $url) : string|false {
$info = pathinfo($url);
$width = apply_filters('cached_image_width', 1000);
$height = apply_filters('cached_image_height', 667);
$filename = sprintf('%s-%s.%s', $info['filename'], 'cached', strtolower($info['extension']));
$filepath = SOME_CACHE_DIR . "/{$filename}";
$fileurl = SOME_CACHE_URL . "/{$filename}";
if (file_exists($filepath)) {
return $fileurl;
}
$editor = wp_get_image_editor($url);
$resized = $editor->resize($width, $height, true);
$filename = $editor->generate_filename('cached', SOME_CACHE_DIR, $info['extension']);
$saved = $editor->save($filename);
// If resizing didn't work for some reason, we'll have to serve up the full-size thing.
if ($saved instanceof \WP_Error) {
return $url;
}
return SOME_CACHE_URL . "/{$saved['file']}";
}
What are the cache directory and URL constants mentioned above? That's up to you! It doesn't have to be constants; it could be member variables in some caching class you create. Do whatever makes sense for your particular situation. Here's a simple way to get them into your WP uploads directory separate from the date-based files:
define('CACHE_DIR', wp_get_upload_dir()['basedir'] . '/cache');
define('CACHE_URL', wp_get_upload_dir()['baseurl'] . '/cache');
Finally, note that there are cases where you might want to forcibly invalidate the cache every so often. If these are external images you don't control, they could change at any time while keeping the same file name, so simply checking that the cached version exists would have your version never update to the new one.
If you want to keep the theme of "doing it yourself" going, the function above could be modified to first check if the cached version of the requested image should be deleted. Its absence would then cause the incoming image to be cached anew. As with any coding problem, there are a few ways to do this, and a couple of them include:
- The cached image's creation or modification date could be checked and deleted if it's older than some number of days.
- A transient could be created per image cached. The cached image would be deleted once the transient expires.
The frequency of this check or the length of your transients is a matter of your situation. If you know, for instance, that there's no chance any given image would change within a week's time, you can limit any cache invalidating to weekly at best.