Files
dostavka_vodi/wp-content/plugins/robin-image-optimizer/includes/classes/class-rio-attachment.php
User A0264400 bbfa5766ae fix
2026-04-19 15:38:03 +03:00

1126 lines
35 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Класс для работы с WordPress attachment.
*
* @version 1.0
*/
class WIO_Attachment {
/**
* @var int
*/
private $id;
/**
* @var array meta-данные
*/
private $attachment_meta;
/**
* @var array массив с данными о папке uploads
*/
private $wp_upload_dir;
/**
* @var string
*/
private $url;
/**
* @var string
*/
private $path;
/**
* @var RIO_Process_Queue
*/
private $optimization_data;
/**
* Инициализация аттачмента
*
* @param int $attachment_id Номер аттачмента из медиабиблиотеки
* @param array|false $attachment_meta метаданные аттачмента. Ключи массива аналогичны функции wp_get_attachment_metadata
*/
public function __construct( $attachment_id, $attachment_meta = false ) {
$this->id = $attachment_id;
$this->wp_upload_dir = wp_upload_dir();
$this->attachment_meta = $attachment_meta;
if ( ! $attachment_meta ) {
// some meta can be missing due to: https://wordpress.stackexchange.com/q/330174/149161
$this->attachment_meta = wp_get_attachment_metadata( $this->id );
}
$this->set_paths();
}
/**
* @return bool
* @since 1.3.9
*/
public function isset_attachment_meta() {
return $this->attachment_meta && isset( $this->attachment_meta['file'] );
}
/**
* @since 1.3.9
*/
public function set_paths() {
if ( ! $this->isset_attachment_meta() ) {
return;
}
$this->url = trailingslashit( $this->wp_upload_dir['baseurl'] ) . $this->attachment_meta['file'];
$this->path = wp_normalize_path( trailingslashit( $this->wp_upload_dir['basedir'] ) . $this->attachment_meta['file'] );
}
/**
* Актуализирует мета данные аттачмента и загружает актуальные мета данные и данные по оптимизации из базы.
*/
public function reload( $attachment_meta = [] ) {
if ( empty( $attachment_meta ) ) {
$attachment_meta = wp_get_attachment_metadata( $this->id );
}
$this->attachment_meta = $attachment_meta;
$this->optimization_data = new RIO_Process_Queue(
[
'object_id' => $this->id,
'object_name' => '',
'item_type' => 'attachment',
]
);
$this->optimization_data->load();
$this->set_paths();
}
/**
* Fallback to get attachment meta it can be empty when WordPress failed to create it or invocation
* of method was produced too soon.
*
* @return bool
* @since 1.3.9
*/
public function regenerate_metadata() {
if ( $this->isset_attachment_meta() ) {
return true;
}
WRIO_Plugin::app()->logger->info( sprintf( 'Try regenerate metadata for attachment #%d', $this->id ) );
// Need to remove this filter, as it would start recursion
remove_filter( 'wp_generate_attachment_metadata', 'WRIO_Media_Library::optimize_after_upload' );
$file_path = get_attached_file( $this->id );
if ( empty( $file_path ) ) {
$attachment = get_post( $this->id );
if ( empty( $attachment ) || 'attachment' !== $attachment->post_type ) {
return false;
}
$file_path = wrio_url_to_abs_path( $attachment->guid );
}
if ( empty( $file_path ) || ! file_exists( $file_path ) ) {
WRIO_Plugin::app()->logger->info( sprintf( 'Failed regenerate attachment meta data. Attachment file (%s) doesn\'t exists!', $file_path ) );
return false;
}
if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
require ABSPATH . 'wp-admin/includes/image.php';
}
$attachment_meta = wp_generate_attachment_metadata( $this->id, $file_path );
if ( empty( $attachment_meta ) ) {
WRIO_Plugin::app()->logger->error( sprintf( 'Failed regenerate meta data for attachment file (%s).', $file_path ) );
return false;
}
WRIO_Plugin::app()->logger->debug( sprintf( 'Generated metadata: %s', var_export( $attachment_meta, true ) ) );
// Updating metadata in database
wp_update_attachment_metadata( $this->id, $attachment_meta );
$this->reload( $attachment_meta );
add_filter( 'wp_generate_attachment_metadata', 'WRIO_Media_Library::optimize_after_upload', 10, 2 );
WRIO_Plugin::app()->logger->info( sprintf( 'Finish regenerate metadata for attachment #%d!', $this->id ) );
return true;
}
/**
* Добавляем сообщение в лог файл.
*
* @param string $message Текст сообщения об ошибке.
*/
public function writeLog( $message ) {
$char = "\t-> ";
$nl = PHP_EOL;
$error = sprintf( 'Error to optimize attachment (ID: #%s). Message: "%s"', $this->id, trim( $message ) ) . $nl;
$error .= $char . sprintf( 'Attachment optimized? %s', ( $this->isOptimized() ? 'Yes' : 'No' ) ) . $nl;
$error .= $char . sprintf( 'Should be resized? %s', ( $this->isNeedResize() ? 'Yes' : 'No' ) ) . $nl;
$error .= $char . sprintf( 'Original size: %sx%s', $this->attachment_meta['width'], $this->attachment_meta['height'] ) . $nl;
$error .= $char . sprintf( 'Relative path: %s', $this->attachment_meta['file'] ) . $nl;
$error .= $char . sprintf( 'Server used: %s', wrio_is_license_activate() ? 'premium' : 'free' ) . $nl;
if ( ! empty( $this->attachment_meta['sizes'] ) ) {
$error .= $char . ' Additional sizes:' . $nl;
foreach ( $this->attachment_meta['sizes'] as $size_type => $size_info ) {
$error .= "\t" . $char . sprintf( 'Type: %s, size: %sx%s, MIME type: %s', $size_type, $size_info['width'], $size_info['height'], $size_info['mime-type'] ) . $nl;
}
}
WRIO_Plugin::app()->logger->error( $error );
}
/**
* Возвращает объект с информацией об оптимизации
*
* @return RIO_Process_Queue
*/
public function getOptimizationData() {
$this->optimization_data = new RIO_Process_Queue(
[
'object_id' => $this->id,
'object_name' => '',
'item_type' => 'attachment',
]
);
$this->optimization_data->load();
return $this->optimization_data;
}
/**
* Возвращает объект с информацией о конвертации в указанный формат
*
* @param string $format Format to get conversion data for ('webp' or 'avif')
*
* @return RIO_Process_Queue
*/
public function getConversionData( $format = 'webp' ) {
$optimization_data = new RIO_Process_Queue(
[
'object_id' => $this->id,
'object_name' => '',
'item_type' => $format,
]
);
$optimization_data->load();
return $optimization_data;
}
/**
* Read file size reliably within the current request.
* PHP caches stat() results; we clear cache for this path.
*
* @param mixed $file_path The file path.
*
* @return int
*/
protected function get_file_size( $file_path ) {
return wrio_get_file_size( $file_path );
}
/**
* Оптимизация аттачмента.
*
* @param string $optimization_level Уровень оптимизации изображения.
*
* @return array
*/
public function optimize( $optimization_level = '' ) {
$optimize_results = [
'original_size' => 0,
'optimized_size' => 0,
];
if ( empty( $optimization_level ) ) {
$optimization_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
}
if ( $optimization_level === 'custom' ) {
$custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
$optimization_level = intval( $custom_quality );
}
$optimization_data = $this->getOptimizationData();
$results = [
'original_size' => 0,
'final_size' => 0,
'original_mime_type' => '',
'final_mime_type' => '',
];
$results['processing_level'] = $optimization_level;
try {
// The path may be empty because no metadata has been created for the image.
// We should try to create image metadata again.
if ( ! $this->isset_attachment_meta() ) {
WRIO_Plugin::app()->logger->warning( sprintf( 'Attachment #%d doesn\'t have metadata.', $this->id ) );
$this->regenerate_metadata();
}
if ( empty( $this->path ) || ! file_exists( $this->path ) ) {
$results['result_status'] = 'error';
$error_message = __( 'Attachment cannot be optimized.', 'robin-image-optimizer' );
if ( empty( $this->path ) ) {
// translators: %d is the attachment ID
$error_message .= ' ' . sprintf( __( 'Attachment #%d doesn\'t have metadata, the image may be damaged.', 'robin-image-optimizer' ), $this->id );
} else {
// translators: %s is the file path
$error_message .= ' ' . sprintf( __( 'File "(%s)" doesn\'t exist', 'robin-image-optimizer' ), $this->path );
}
$extra_data = [
'error' => 'path',
'error_msg' => $error_message,
];
$results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
$optimization_data->configure( $results );
$optimization_data->save();
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to find original attachment #%s located in %s. Skipping optimization. This may be caused due to bug in %s function, which returns false for attachment meta', $this->id, empty( $this->path ) ? '*empty path*' : $this->path, 'wp_get_attachment_metadata()' ) );
return $optimize_results;
}
// сначала бекапим
$is_image_backuped = $this->backup();
if ( is_wp_error( $is_image_backuped ) ) {
$error_msg = $is_image_backuped->get_error_message();
$this->writeLog( $error_msg );
$results['result_status'] = 'error';
$extra_data = [
'error' => 'backup',
'error_msg' => 'Failed to backup',
];
$results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
$optimization_data->configure( $results );
$optimization_data->save();
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to make backup of original attachment #%s. Skipping optimization.', $this->id ) );
return $optimize_results;
}
$results['is_backed_up'] = $is_image_backuped;
$original_main_size = $this->get_file_size( $this->path );
// если файл большой - изменяем размер
if ( $this->isNeedResize() ) {
$this->resize();
}
$image_processor = WIO_OptimizationTools::getImageProcessor();
clearstatcache(); // на всякий случай очистим кеш файловой статистики
$optimized_img_data = $image_processor->process(
[
'image_url' => $this->get( 'url' ),
'image_path' => $this->get( 'path' ),
'quality' => $image_processor->quality( $optimization_level ),
'save_exif' => WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false ),
'is_thumb' => false,
]
);
// проверяем на ошибку
if ( is_wp_error( $optimized_img_data ) ) {
$error_msg = $optimized_img_data->get_error_message();
$this->writeLog( $error_msg );
$results['result_status'] = 'error';
$extra_data = [
'error' => 'optimization',
'error_msg' => $error_msg,
];
$results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
$optimization_data->configure( $results );
$optimization_data->save();
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to process (url: %s, path: %s, quality: %s) as error was returned: %s', $this->get( 'url' ), $this->get( 'path' ), $image_processor->quality( $optimization_level ), $error_msg ) );
return $optimize_results;
}
$results['original_mime_type'] = '';
$results['final_mime_type'] = '';
// отложенная оптимизация
if ( isset( $optimized_img_data['status'] ) && 'processing' === $optimized_img_data['status'] ) {
$results['result_status'] = 'processing';
$results['original_size'] = 0;
$results['final_size'] = 0;
$extra_data = [
'original_main_size' => $original_main_size,
'main_optimized_data' => $optimized_img_data,
'thumbnails_optimized_data' => $this->optimizeImageSizes(),
];
$results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
$optimization_data->configure( $results );
$optimization_data->save();
$optimize_results['processing'] = 1;
return $optimize_results;
}
// скачиваем и заменяем главную картинку
$image_downloaded = $this->replaceOriginalFile( $optimized_img_data );
// некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
if ( ! $optimized_img_data['optimized_size'] ) {
clearstatcache();
$optimized_img_data['optimized_size'] = $this->get_file_size( $this->get( 'path' ) );
}
// при отрицательной оптимизации ставим значение оригинала
if ( $optimized_img_data['optimized_size'] > $original_main_size ) {
$optimized_img_data['optimized_size'] = $original_main_size;
}
if ( $image_downloaded ) {
// просчитываем статистику
$optimize_results['original_size'] += $original_main_size;
$optimize_results['optimized_size'] += $optimized_img_data['optimized_size'];
$thumbnails_count = 0;
// оптимизируем дополнительные размеры
$optimized_img_sizes_data = $this->optimizeImageSizes();
// добавляем к статистике данные по оптимизации доп размеров
if ( ! empty( $optimized_img_sizes_data ) ) {
$optimize_results['original_size'] += $optimized_img_sizes_data['original_size'];
$optimize_results['optimized_size'] += $optimized_img_sizes_data['optimized_size'];
$thumbnails_count = $optimized_img_sizes_data['thumbnails_count'];
}
$results['result_status'] = 'success';
$results['final_size'] = $optimize_results['optimized_size'];
$results['original_size'] = $optimize_results['original_size'];
$extra_data = [
'thumbnails_count' => $thumbnails_count,
'original_main_size' => $original_main_size,
];
$results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
$mime_type = '';
if ( function_exists( 'wp_get_image_mime' ) ) {
$mime_type = wp_get_image_mime( $this->get( 'path' ) );
} else {
WRIO_Plugin::app()->logger->error( 'App is missing wp_get_image_mime() function, unable to get MIME type' );
}
$results['original_mime_type'] = $mime_type;
$results['final_mime_type'] = $mime_type;
$optimization_data->configure( $results );
} else {
$error_msg = 'Failed to get optimized image from remote server';
$this->writeLog( $error_msg );
$results['result_status'] = 'error';
$extra_data = [
'error' => 'download',
'error_msg' => $error_msg,
];
$results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
$optimization_data->configure( $results );
}
$optimization_data->save();
return $optimize_results;
} catch ( Throwable $throwable ) {
$this->mark_and_log_failure( $throwable, 'optimization', $optimization_level );
}
return $optimize_results;
}
/**
* Log and persist unexpected optimization failures.
*
* @param Throwable $throwable Exception or error that was thrown.
* @param string $context Processing context.
* @param string|int $processing_level Current processing level.
*
* @return void
*/
public function mark_and_log_failure( $throwable, $context = 'optimization', $processing_level = '' ) {
$error_message = sprintf(
'Unexpected %1$s failure for attachment #%2$d: %3$s in %4$s:%5$d',
$context,
$this->id,
$throwable->getMessage(),
$throwable->getFile(),
$throwable->getLine()
);
WRIO_Plugin::app()->logger->error( $error_message );
$optimization_data = $this->getOptimizationData();
$optimization_data->mark_as_error(
$error_message,
[
'processing_level' => $processing_level,
'original_size' => 0,
'final_size' => 0,
'original_mime_type' => '',
'final_mime_type' => '',
]
);
}
/**
* Log and persist unexpected format conversion failures without rewriting attachment optimization state.
*
* @param Throwable $throwable Exception or error that was thrown.
* @param string $format Target format.
* @param string $context Processing context.
*
* @return void
*/
public function mark_conversion_failure( $throwable, $format, $context = 'conversion' ) {
$error_message = sprintf(
'Unexpected %1$s failure for attachment #%2$d (%3$s): %4$s in %5$s:%6$d',
$context,
$this->id,
$format,
$throwable->getMessage(),
$throwable->getFile(),
$throwable->getLine()
);
WRIO_Plugin::app()->logger->error( $error_message );
$conversion_data = $this->getConversionData( $format );
if ( ! $conversion_data->get_id() ) {
return;
}
$conversion_data->mark_as_error( $error_message );
}
/**
* Отложенная оптимизация аттачмента
*
* @return bool|array
*/
public function deferredOptimization() {
$results = [
'original_size' => 0,
'optimized_size' => 0,
'optimized_count' => 0,
'processing' => 1,
];
$image_processor = WIO_OptimizationTools::getImageProcessor();
$optimization_data = $this->getOptimizationData();
if ( $optimization_data->get_result_status() !== 'processing' ) {
return false;
}
// проверяем главную картинку
/**
* @var RIO_Attachment_Extra_Data $extra_data
*/
$extra_data = $optimization_data->get_extra_data();
$main_optimized_data = $extra_data->get_main_optimized_data();
$main_image_url = '';
if ( ! $main_optimized_data['optimized_img_url'] ) {
$main_image_url = $image_processor->checkDeferredOptimization( $main_optimized_data );
if ( $main_image_url ) {
$main_optimized_data['optimized_img_url'] = $main_image_url;
$extra_data->set_main_optimized_data( $main_optimized_data );
}
}
$thumbnails_processed = true;
$thumbnails = (array) $extra_data->get_thumbnails_optimized_data();
$thumbnails = json_decode( json_encode( $thumbnails ), true ); // рекурсивная конвертация объекта в массив
if ( is_array( $thumbnails['thumbnails'] ) ) {
foreach ( $thumbnails['thumbnails'] as &$thumbnail_optimized_data ) {
if ( ! $thumbnail_optimized_data['optimized_img_url'] ) {
$thumbnail_image_url = $image_processor->checkDeferredOptimization( $thumbnail_optimized_data );
if ( $thumbnail_image_url ) {
$thumbnail_optimized_data['optimized_img_url'] = $thumbnail_image_url;
} else {
$thumbnails_processed = false;
}
}
}
$extra_data->set_thumbnails_optimized_data( $thumbnails );
}
// когда все файлы получены - сохраняем и возвращаем результат
if ( $main_image_url && $thumbnails_processed ) {
$original_size = 0;
$optimized_size = 0;
$thumbnails_count = 0;
$original_main_size = (int) $extra_data->get_original_main_size();
if ( $original_main_size <= 0 ) {
$original_main_size = $this->get_file_size( $this->get( 'path' ) );
}
$original_size = $original_size + $original_main_size;
$this->replaceOriginalFile(
[
'optimized_img_url' => $main_image_url,
]
);
clearstatcache();
$optimized_main_size = $this->get_file_size( $this->get( 'path' ) );
// при отрицательной оптимизации ставим значение оригинала
if ( $optimized_main_size > $original_main_size ) {
$optimized_main_size = $original_main_size;
}
$optimized_size = $optimized_size + $optimized_main_size;
if ( is_array( $thumbnails['thumbnails'] ) ) {
foreach ( $thumbnails['thumbnails'] as $thumbnail_size => $thumbnail ) {
$thumbnail_file = $this->getImageSizePath( $thumbnail_size );
$original_thumbnail_size = $this->get_file_size( $thumbnail_file );
$original_size = $original_size + $original_thumbnail_size;
$this->replaceOriginalFile(
[
'optimized_img_url' => $thumbnail['optimized_img_url'],
],
$thumbnail_size
);
clearstatcache();
$optimized_thumbnail_size = $this->get_file_size( $thumbnail_file );
// при отрицательной оптимизации ставим значение оригинала
if ( $optimized_thumbnail_size > $original_thumbnail_size ) {
$optimized_thumbnail_size = $original_thumbnail_size;
}
$optimized_size = $optimized_size + $optimized_thumbnail_size;
++$thumbnails_count;
}
}
$mime_type = '';
if ( function_exists( 'wp_get_image_mime' ) ) {
$mime_type = wp_get_image_mime( $this->get( 'path' ) );
}
$optimization_data->configure(
[
'final_size' => $optimized_size,
'original_size' => $original_size,
'result_status' => 'success',
'original_mime_type' => $mime_type,
'final_mime_type' => $mime_type,
]
);
$extra_data->set_original_main_size( $original_main_size );
$extra_data->set_thumbnails_count( $thumbnails_count );
// удаляем промежуточные данные
$extra_data->set_main_optimized_data( null );
$extra_data->set_thumbnails_optimized_data( null );
$extra_data->set_main_optimized_data( null );
$results['optimized_count'] = 1;
$results['original_size'] = $original_size;
$results['optimized_size'] = $optimized_size;
unset( $results['processing'] );
}
$optimization_data->set_extra_data( $extra_data );
$optimization_data->save();
return $results;
}
/**
* Метод проверяет, оптимизирован ли аттачмент
*
* @return bool
*/
public function isOptimized() {
$optimization_data = $this->getOptimizationData();
if ( $optimization_data->is_optimized() ) {
return true;
}
return false;
}
/**
* Возвращает все размеры аттачмента, которые нужно оптимизировать
*
* @return array|false
*/
public function getAllowedSizes() {
$allowed_sizes = WRIO_Plugin::app()->getPopulateOption( 'allowed_sizes_thumbnail', 'thumbnail,medium' );
if ( ! $allowed_sizes ) {
return false;
}
$allowed_sizes = explode( ',', $allowed_sizes );
return $allowed_sizes;
}
/**
* Оптимизация других размеров аттачмента.
*
* @return array
*/
public function optimizeImageSizes() {
$allowed_sizes = $this->getAllowedSizes();
if ( $allowed_sizes === false ) {
return [];
}
$image_processor = WIO_OptimizationTools::getImageProcessor();
$quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
if ( $quality === 'custom' ) {
$custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
$quality = intval( $custom_quality );
}
$exif = WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false );
$original_size = 0;
$optimized_size = 0;
$errors_count = 0;
$optimized_count = 0;
$thumbnails = [];
foreach ( $allowed_sizes as $image_size ) {
$url = $this->getImageSizeUrl( $image_size );
$path = $this->getImageSizePath( $image_size );
if ( ! $url || ! $path ) {
continue;
}
$original_file_size = 0;
if ( is_file( $path ) ) {
$original_file_size = $this->get_file_size( $path );
}
$optimized_img_data = $image_processor->process(
[
'image_url' => $url,
'image_path' => $path,
'quality' => $image_processor->quality( $quality ),
'save_exif' => $exif,
'is_thumb' => true,
]
);
// проверяем на ошибку
if ( is_wp_error( $optimized_img_data ) ) {
++$errors_count;
} else {
// скачиваем и заменяем картинку
$this->replaceOriginalFile( $optimized_img_data, $image_size );
// некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
if ( ! $optimized_img_data['optimized_size'] ) {
clearstatcache();
$optimized_img_data['optimized_size'] = $this->get_file_size( $path );
}
if ( ! $optimized_img_data['src_size'] ) {
$optimized_img_data['src_size'] = $original_file_size;
}
// при отрицательной оптимизации ставим значение оригинала
if ( $optimized_img_data['optimized_size'] > $original_file_size ) {
$optimized_img_data['optimized_size'] = $original_file_size;
}
$thumbnails[ $image_size ] = $optimized_img_data;
// просчитываем статистику
$original_size += $optimized_img_data['src_size'];
$optimized_size += $optimized_img_data['optimized_size'];
++$optimized_count;
}
}
return [
'errors_count' => $errors_count,
'original_size' => $original_size,
'optimized_size' => $optimized_size,
'thumbnails_count' => $optimized_count,
'thumbnails' => $thumbnails,
];
}
/**
* Возвращает путь.
*
* @param string $image_size Размер(thumbnail, medium ... )
*
* @return string
*/
public function getPath( $image_size = '' ) {
if ( empty( $image_size ) ) {
$path = $this->path;
} else {
$path = $this->getImageSizePath( $image_size );
}
return $path;
}
/**
* Заменяет оригинальный файл на оптимизированный.
*
* @param array $optimized_img_data Hезультат оптимизации ввиде массива данных.
* @param string $image_size Размер (thumbnail, medium ... )
*
* @return bool
*/
public function replaceOriginalFile( $optimized_img_data, $image_size = '' ) {
$optimized_img_url = $optimized_img_data['optimized_img_url'];
if ( isset( $optimized_img_data['not_need_download'] ) && (bool) $optimized_img_data['not_need_download'] ) {
$optimized_file = $optimized_img_url;
} else {
$optimized_file = $this->remoteDownloadImage( $optimized_img_url );
}
if ( empty( $optimized_file ) ) {
WRIO_Plugin::app()->logger->error( sprintf( 'Unable to replace original image with new as failed to download %s', $optimized_img_url ) );
return false;
}
if ( isset( $optimized_img_data['not_need_replace'] ) && $optimized_img_data['not_need_replace'] ) {
// если картинка уже оптимизирована и провайдер её не может уменьшить - он может вернуть положительный ответ, но без самой картинки. В таком случае ничего заменять не надо
return true;
}
$attachment_size_path = $this->getPath( $image_size );
if ( ! is_file( $attachment_size_path ) ) {
return false;
}
$bytes = @file_put_contents( $attachment_size_path, $optimized_file );
if ( $bytes === false ) {
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to put new image\'s %s content to %s as file_put_contents() failed', $optimized_img_url, $attachment_size_path ) );
return false;
}
return true;
}
/**
* Скачивание изображения с удалённого сервера
*
* @param string $url
*
* @return string|null Image content on success, NULL on failure.
*/
protected function remoteDownloadImage( $url ) {
$user_agent = wrio_get_user_agent();
if ( ! function_exists( 'curl_version' ) ) {
$context_options = [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
'http' => [
'header' => 'User-Agent: ' . $user_agent,
],
];
$content = @file_get_contents( $url, false, stream_context_create( $context_options ) );
if ( $content === false ) {
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to get content of "%s" using file_get_contents()', $url ) );
return null;
}
return $content;
}
$ch = curl_init();
curl_setopt( $ch, CURLOPT_HEADER, 0 );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
curl_setopt( $ch, CURLOPT_USERAGENT, $user_agent );
$image_body = curl_exec( $ch );
$http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
if ( $http_code !== 200 ) {
$image_body = false;
}
curl_close( $ch );
if ( $image_body === false ) {
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to get content of "%s" using curl_exec(). HTTP code: ' . $http_code, $url ) );
return null;
}
return $image_body;
}
/**
* Возвращает свойство аттачмента
*
* @param string $property имя свойства
*
* @return mixed
*/
public function get( $property ) {
if ( isset( $this->$property ) ) {
return $this->$property;
}
return false;
}
/**
* Возвращает URL изображения по указанному размеру
*
* @param string $size - размер изображения(thumbnail,medium,large...)
*
* @return string|null
*/
public function getImageSizeUrl( $size = 'thumbnail' ) {
if ( ! isset( $this->attachment_meta['sizes'][ $size ] ) ) {
return null;
}
$file = $this->attachment_meta['sizes'][ $size ]['file'];
$url = str_replace( wp_basename( $this->url ), $file, $this->url );
return $url;
}
/**
* Возвращает путь к изображению по указанному размеру.
*
* @param string $size Размер изображения (thumbnail, medium, large ...)
*
* @return string Путь до изображения.
*/
public function getImageSizePath( $size = 'thumbnail' ) {
if ( ! isset( $this->attachment_meta['sizes'][ $size ] ) ) {
return null;
}
$file = $this->attachment_meta['sizes'][ $size ]['file'];
$path = str_replace( wp_basename( $this->path ), $file, $this->path );
return $path;
}
/**
* Проверка необходимости делать изменение размера.
*
* @return bool
*/
protected function isNeedResize() {
$resize_large_images = WRIO_Plugin::app()->getPopulateOption( 'resize_larger', true );
if ( ! $resize_large_images ) {
return false;
}
$resize_larger_w = (int) WRIO_Plugin::app()->getPopulateOption( 'resize_larger_w', 1600 );
$resize_larger_h = (int) WRIO_Plugin::app()->getPopulateOption( 'resize_larger_h', 1600 );
if ( ! $resize_larger_w && ! $resize_larger_h ) {
return false;
}
// если ширина и высота установлены и > 0
if ( $this->attachment_meta['width'] >= $this->attachment_meta['height'] ) {
$larger_side = $this->attachment_meta['width'];
$resize_larger_side = $resize_larger_w;
} else {
$larger_side = $this->attachment_meta['height'];
$resize_larger_side = $resize_larger_h;
}
// если ширина 0, то рисайзим по высоте
if ( ! $resize_larger_w ) {
$resize_larger_side = $resize_larger_h;
$larger_side = $this->attachment_meta['height'];
}
// если высота 0, то рисайзим по ширине
if ( ! $resize_larger_h ) {
$resize_larger_side = $resize_larger_w;
$larger_side = $this->attachment_meta['width'];
}
// если большая сторона картинки меньше, чем задано в настройках, то не рисайзим.
if ( $larger_side <= $resize_larger_side ) {
return false;
}
return true;
}
/**
* Возвращает метаданные аттачмента
*
* @return array
*/
public function getMetaData() {
return $this->attachment_meta;
}
/**
* Изменяет размер изображения до заданного в настройках размера.
*
* @return bool
*/
protected function resize() {
$resize_larger_h = (int) WRIO_Plugin::app()->getPopulateOption( 'resize_larger_h', 1600 );
$resize_larger_w = (int) WRIO_Plugin::app()->getPopulateOption( 'resize_larger_w', 1600 );
$image = wp_get_image_editor( $this->path );
if ( is_wp_error( $image ) ) {
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to get image edit via wp_get_image_editor(), error: "%s"', $image->get_error_message() ) );
return false;
}
$current_size = $image->get_size();
$new_width = 0;
$new_height = 0;
// если обе стороны заданы
if ( $resize_larger_h && $resize_larger_w ) {
// определяем большую сторону и по ней маштабируем
if ( $current_size['width'] >= $current_size['height'] ) {
$new_width = $resize_larger_w;
$new_height = round( $current_size['height'] * $new_width / $current_size['width'] );
} else {
$new_height = $resize_larger_h;
$new_width = round( $current_size['width'] * $new_height / $current_size['height'] );
}
} else {
// если задана одна из сторон
if ( ! $resize_larger_w ) {
// если ширина 0, то рисайзим по высоте
$new_height = $resize_larger_h;
$new_width = round( $current_size['width'] * $new_height / $current_size['height'] );
}
if ( ! $resize_larger_h ) {
// если высота 0, то рисайзим по ширине
$new_width = $resize_larger_w;
$new_height = round( $current_size['height'] * $new_width / $current_size['width'] );
}
}
$nl = PHP_EOL;
$log_message = sprintf( "\tResize from: %sx%s to %sx%s", $current_size['width'], $current_size['height'], $new_width, $new_height ) . $nl;
$log_message .= sprintf( "\tLarger resize from %sx%s", $resize_larger_w, $resize_larger_h ) . $nl;
$log_message .= sprintf( "\tAbsolute path: %s", $this->path ) . $nl;
$resize_result = $image->resize( $new_width, $new_height, false );
if ( is_wp_error( $resize_result ) ) {
$this->writeLog( sprintf( 'Resize error: %s. Details: %s', $resize_result->get_error_messages(), $log_message ) );
return false;
}
$save_result = $image->save( $this->path );
if ( is_wp_error( $save_result ) ) {
$this->writeLog( sprintf( 'Failed to save resized error in db: %s, Details: %s', $save_result->get_error_messages(), $log_message ) );
return false;
}
$this->attachment_meta['width'] = $new_width;
$this->attachment_meta['height'] = $new_height;
$this->attachment_meta['old_width'] = $current_size['width'];
$this->attachment_meta['old_height'] = $current_size['height'];
wp_update_attachment_metadata( $this->id, $this->attachment_meta );
return true;
}
/**
* Делает резервную копию
*
* @return true|WP_Error
*/
protected function backup() {
$backup = WIO_Backup::get_instance();
return $backup->backupAttachment( $this );
}
/**
* Восстанавливает файлы из резервной копии
*
* @return true|WP_Error
*/
public function restore() {
$backup = WIO_Backup::get_instance();
return $backup->restoreAttachment( $this );
}
}