<?php declare( strict_types=1 );
namespace KadenceWP\KadenceStarterTemplates\Image_Downloader;
use KadenceWP\KadenceStarterTemplates\Psr\Log\LoggerInterface;
use KadenceWP\KadenceStarterTemplates\StellarWP\ProphecyMonorepo\ImageDownloader\FileNameProcessor;
use KadenceWP\KadenceStarterTemplates\StellarWP\ProphecyMonorepo\ImageDownloader\Models\DownloadedImage;
use KadenceWP\KadenceStarterTemplates\Traits\Image_Size_Trait;
use RuntimeException;
use WP_Error;
use WP_Image_Editor;
/**
* Normally the WP_Image_Editor will create all the different thumbnail sizes on the server,
* however, we simply return the data of the images that have already been downloaded with
* the concurrent image downloader.
*/
final class Image_Editor extends WP_Image_Editor {
use Image_Size_Trait;
/**
* The collection of all downloaded images.
*
* @var array<int, DownloadedImage[]>
*/
private $images = [];
/**
* The current DownloadedImage being processed.
*
* @var DownloadedImage
*/
private $image;
/**
* The current Pexels ID being processed.
*
* @var int
*/
private $id;
/**
* @var LoggerInterface
*/
private $logger;
/**
* Tests if this Image Editor is supported.
*
* @param array $args
*
* @return bool
*/
public static function test( $args = [] ): bool {
return true;
}
/**
* We support all mime types because this editor is only loaded
* when the Image Downloader is executed.
*
* @param string $mime_type
*
* @return true
*/
public static function supports_mime_type( $mime_type ): bool {
return true;
}
/**
* This is always called first, so basically an init method.
*
* @inheritDoc
*/
public function load() {
if ( $this->file === null ) {
return new WP_Error( 'error_loading_image', __( 'File cannot be null.' ) );
}
$this->logger = kadence_starter_templates()->get( LoggerInterface::class );
// Fetch the currently downloaded images from the Importer.
$this->images = kadence_starter_templates()->get( WordPress_Importer::class )->images();
// Find the image WordPress is currently processing by matching the file name.
foreach ( $this->images as $id => $images ) {
// Grab the scaled image, or fallback to the largest size.
$scaled_key = array_search( FileNameProcessor::SCALED_SIZE, array_column( $images, 'size' ), true );
if ( $scaled_key !== false ) {
$scaled = $images[ $scaled_key ] ?? end( $images );
} else {
$scaled = end( $images );
}
if ( $this->file !== $scaled->file ) {
continue;
}
$this->image = $scaled;
$this->id = $id;
break;
}
if ( $this->image === null ) {
$this->logger->error( 'Cannot find downloaded file', [
'file' => $this->file,
] );
return new WP_Error( 'error_loading_image', __( 'Cannot find downloaded file.' ), $this->file );
}
return true;
}
/**
* @inheritDoc
*
* @return array{path: string, file: string, width: int, height: int, mime-type: string, filesize: int}
*/
public function save( $destfilename = null, $mime_type = null ): array {
$image_size = wp_getimagesize( $this->image->file );
return [
'path' => $this->image->file,
'file' => wp_basename( $this->image->file ),
'width' => $image_size[0],
'height' => $image_size[1],
'mime-type' => $image_size['mime'],
'filesize' => wp_filesize( $this->image->file ),
];
}
/**
* Our images are already resized, just return true.
*
* @inheritDoc
*/
public function resize( $max_w, $max_h, $crop = false ) {
return true;
}
/**
* Create multiple smaller images from a single source.
*
* Attempts to create all sub-sizes and returns the meta data at the end.
*
* As of 5.3.0 the preferred way to do this is with `make_subsize()`. It creates
* the new images one at a time and allows for the meta data to be saved after
* each new image is created.
*
* @param array<string, array{width?: int, height?: int, crop?: bool}> $sizes
*
* @return array<string,array{path: string, file: string, width: int, height: int, mime-type: string, filesize: int}> An array of resized images' metadata by size.
*
* @throws \InvalidArgumentException
*/
public function multi_resize( $sizes ) {
$this->logger->debug( 'Using old multi_resize method' );
$metadata = [];
foreach ( $sizes as $size => $size_data ) {
$meta = $this->make_subsize( $size_data );
if ( ! is_wp_error( $meta ) ) {
$metadata[ $size ] = $meta;
continue;
}
$this->logger->error( 'Unable to make image subsize', [
'file' => $this->image->file,
'errors' => $meta->get_error_messages(),
] );
}
return $metadata;
}
/**
* Our images are already cropped, just return true.
*
* @inheritDoc
*/
public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
return true;
}
/**
* @inheritDoc
*/
public function rotate( $angle ) {
throw new RuntimeException( 'method not implemented' );
}
/**
* @inheritDoc
*/
public function flip( $horz, $vert ) {
throw new RuntimeException( 'method not implemented' );
}
/**
* @inheritDoc
*/
public function stream( $mime_type = null ) {
throw new RuntimeException( 'method not implemented' );
}
/**
* Find our already made sub-sized images in our image collection.
*
* @param array{width?: int, height?: int, crop?: bool} $size_data
*
* @return WP_Error|array{path: string, file: string, width: int, height: int, mime-type: string, filesize: int}
* @throws \InvalidArgumentException
*/
public function make_subsize( $size_data ) {
if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
$this->logger->error( 'Cannot resize the image. Both width and height are not set.', [
'file' => $this->image->file,
] );
return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) );
}
if ( ! isset( $size_data['width'] ) ) {
$size_data['width'] = null;
}
if ( ! isset( $size_data['height'] ) ) {
$size_data['height'] = null;
}
if ( ! isset( $size_data['crop'] ) ) {
$size_data['crop'] = false;
}
$existing_sizes = $this->get_image_sizes();
$thumbnail_id = '';
// Find the thumbnail name based on the requested dimensions.
foreach ( $existing_sizes as $existing_size ) {
if ( $existing_size['width'] === $size_data['width'] && $existing_size['height'] === $size_data['height'] ) {
$thumbnail_id = $existing_size['id'];
break;
}
}
if ( strlen( $thumbnail_id ) === 0 ) {
$this->logger->error( 'Could not find thumbnail size', [
'file' => $this->image->file,
'requested_width' => $size_data['width'],
'requested_height' => $size_data['height'],
] );
return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image.' ) );
}
// Find the matching file for the requested thumbnail size and get its metadata.
foreach ( $this->images[ $this->id ] as $key => $image ) {
if ( $image->size !== $thumbnail_id ) {
continue;
}
$original = $this->image;
$this->image = $image;
$saved = $this->save();
$this->image = $original;
unset( $this->images[ $this->id ][ $key ] );
return $saved;
}
$this->logger->error( 'Cannot match image to size data', [
'file' => $this->image->file,
'file_max_width' => $this->image->width,
'file_max_height' => $this->image->height,
'requested_width' => $size_data['width'],
'requested_height' => $size_data['height'],
] );
return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image.' ) );
}
}