PluginProbe ʕ •ᴥ•ʔ
Robin Image Optimizer – Unlimited Image Optimization, WebP & AVIF / trunk
Robin Image Optimizer – Unlimited Image Optimization, WebP & AVIF vtrunk
2.0.5 trunk 1.3.7 1.4.0 1.4.1 1.4.2 1.4.6 1.5.0 1.5.3 1.5.6 1.5.8 1.6.5 1.6.6 1.6.9 1.7.0 1.7.4 1.8.1 1.8.2 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4
robin-image-optimizer / libs / addons / includes / classes / class.folder-image.php
robin-image-optimizer / libs / addons / includes / classes Last commit date
format 3 months ago helpers 5 months ago models 5 months ago webp 3 months ago class.backup.php 5 months ago class.custom-folders.php 5 months ago class.folder-image.php 5 months ago class.folder.php 5 months ago class.folders-list-table.php 5 months ago class.gallery-nextgen.php 5 months ago class.image-nextgen.php 5 months ago class.image-statistic-folders.php 5 months ago class.image-statistic-nextgen.php 5 months ago class.wpcli-optimize.php 5 months ago index.php 5 months ago
class.folder-image.php
495 lines
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * Класс для работы с custom folder image.
10 *
11 * @version 1.0
12 */
13 class WRIO_Folder_Image {
14
15 /**
16 * @var int номер картинки в таблице
17 */
18 private $id;
19
20 /**
21 * @var string путь к картинке относительно папки
22 */
23 private $path;
24
25 /**
26 * @var string УРЛ
27 */
28 private $url;
29
30 /**
31 * @var string уникальный идентификатор директории
32 */
33 private $folder_uid;
34
35 /**
36 * @var RIO_Process_Queue данные по оптимизации
37 */
38 private $optimization_data;
39
40 /**
41 * Инициализация картинки из custom folder
42 *
43 * @param int $image_id номер картинки в таблице folders
44 * @param array|false $image_data метаданные картинки
45 */
46 public function __construct( $image_id, $image_data = false ) {
47 $this->id = $image_id;
48
49 if ( $image_data instanceof RIO_Process_Queue ) {
50 $this->optimization_data = $image_data;
51 } else {
52 $this->optimization_data = $this->createOptimizationData();
53
54 if ( $image_data ) {
55 $this->optimization_data->configure( (array) $image_data );
56 } else {
57 $this->loadOptimizationData();
58 }
59 }
60
61 /**
62 * @var WRIO_CF_Image_Extra_Data $extra_data
63 */
64 $extra_data = $this->optimization_data->get_extra_data();
65 // Use get_home_path() to match how real_path_to_relative() calculates relative paths in class.folder.php
66 $base_path = is_main_site() ? get_home_path() : wp_upload_dir()['basedir'] . '/';
67 $this->path = wp_normalize_path( untrailingslashit( $base_path ) . $extra_data->get_file_path() );
68 $this->url = home_url( wp_normalize_path( $extra_data->get_file_path() ) );
69 $this->folder_uid = $this->optimization_data->get_item_hash_alternative();
70 }
71
72 /**
73 * Возвращает свойство аттачмента
74 *
75 * @param string $property имя свойства
76 *
77 * @return mixed
78 */
79 public function get( $property ) {
80 if ( isset( $this->$property ) ) {
81 return $this->$property;
82 }
83
84 return false;
85 }
86
87 /**
88 * Возвращает данные по оптимизации
89 *
90 * @return RIO_Process_Queue
91 */
92 public function getOptimizationData() {
93 if ( empty( $this->optimization_data ) ) {
94 $this->optimization_data = $this->createOptimizationData();
95 $this->optimization_data->load();
96 }
97
98 return $this->optimization_data;
99 }
100
101 /**
102 * Создаёт новый объект RIO_Process_Queue
103 *
104 * @return RIO_Process_Queue
105 */
106 public function createOptimizationData() {
107 return new RIO_Process_Queue(
108 [
109 'id' => $this->id,
110 'item_type' => 'cf_image',
111 ]
112 );
113 }
114
115
116 protected function loadOptimizationData() {
117 global $wpdb;
118
119 if ( empty( $this->optimization_data ) ) {
120 $this->optimization_data = $this->createOptimizationData();
121 }
122
123 $table_name = RIO_Process_Queue::table_name();
124 $sql = $wpdb->prepare(
125 "SELECT * FROM {$table_name} WHERE id = %d AND item_type = %s LIMIT 1;",
126 [
127 $this->id,
128 'cf_image',
129 ]
130 );
131
132 $row = $wpdb->get_row( $sql );
133
134 if ( ! empty( $row ) ) {
135 $this->optimization_data->configure( $row );
136 }
137 }
138
139 /**
140 * Проверка на оптимизацию изображения
141 *
142 * @return bool
143 */
144 public function isOptimized() {
145 $optimization_data = $this->getOptimizationData();
146 if ( empty( $optimization_data ) ) {
147 return false;
148 }
149 if ( $optimization_data->is_optimized() ) {
150 return true;
151 }
152
153 return false;
154 }
155
156 /**
157 * Check whether file exists or not.
158 *
159 * @return bool
160 */
161 public function isFileExists() {
162 if ( file_exists( $this->path ) ) {
163 return true;
164 }
165
166 return false;
167 }
168
169 /**
170 * Optimize folder image.
171 *
172 * @param string $optimization_level Level of optimization.
173 *
174 * @return array
175 */
176 public function optimize( $optimization_level = '' ) {
177 $is_image_backuped = $this->backup();
178
179 if ( is_wp_error( $is_image_backuped ) ) {
180
181 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to backup with message: %s. Skipping optimization of custom folder', $is_image_backuped->get_error_message() ) );
182
183 return [
184 'errors_count' => 1,
185 'original_size' => 0,
186 'optimized_size' => 0,
187 'optimized_count' => 0,
188 ];
189 }
190 // делаем рисайз
191 $image_processor = WIO_OptimizationTools::getImageProcessor();
192 if ( ! $optimization_level ) {
193 $optimization_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
194 }
195 if ( $optimization_level == 'custom' ) {
196 $custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
197 $optimization_level = intval( $custom_quality );
198 }
199 $optimization_data = $this->getOptimizationData();
200 $results = [];
201 $results['processing_level'] = $optimization_level;
202 $main_file_path = $this->path;
203 $main_file_url = $this->url;
204 clearstatcache(); // на всякий случай очистим кеш файловой статистики
205 $original_main_size = filesize( $main_file_path ); // оптимизированный размер только главной картинки
206
207 $optimized_img_data = $image_processor->process(
208 [
209 'image_url' => $main_file_url,
210 'image_path' => $main_file_path,
211 'quality' => $image_processor->quality( $optimization_level ),
212 'save_exif' => WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false ),
213 ]
214 );
215
216 if ( is_wp_error( $optimized_img_data ) ) {
217 $results['result_status'] = 'error';
218 /**
219 * @var $extra_data WRIO_CF_Image_Extra_Data
220 */
221 $extra_data = $optimization_data->get_extra_data();
222 $extra_data->set_error( 'optimization' );
223 $extra_data->set_error_msg( $optimized_img_data->get_error_message() );
224 $results['extra_data'] = $extra_data;
225 $optimization_data->configure( $results );
226 $optimization_data->save();
227
228 return [
229 'errors_count' => 1,
230 'original_size' => 0,
231 'optimized_size' => 0,
232 'optimized_count' => 0,
233 ];
234 }
235
236 // отложенная оптимизация
237 if ( isset( $optimized_img_data['status'] ) && $optimized_img_data['status'] == 'processing' ) {
238 $results['result_status'] = 'processing';
239 $results['is_backed_up'] = $is_image_backuped;
240 $results['original_size'] = 0;
241 $results['final_size'] = 0;
242
243 /**
244 * @var $extra_data WRIO_CF_Image_Extra_Data
245 */
246 $extra_data = $optimization_data->get_extra_data();
247 $extra_data->set_main_optimized_data( $optimized_img_data );
248 $results['extra_data'] = $extra_data;
249 $optimization_data->configure( $results );
250 $optimization_data->save();
251
252 return [
253 'processing' => 1,
254 'original_size' => 0,
255 'optimized_size' => 0,
256 ];
257 }
258
259 $this->replaceOriginalFile( $optimized_img_data );
260
261 // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
262 if ( ! $optimized_img_data['optimized_size'] ) {
263 clearstatcache();
264 $optimized_img_data['optimized_size'] = filesize( $main_file_path );
265 }
266 // при отрицательной оптимизации ставим значение оригинала
267 if ( $optimized_img_data['optimized_size'] > $original_main_size ) {
268 $optimized_img_data['optimized_size'] = $original_main_size;
269 }
270
271 $original_size = $original_main_size;
272 $optimized_size = $optimized_img_data['optimized_size'];
273
274 $results['result_status'] = 'success';
275 $results['final_size'] = $optimized_size;
276 $results['original_size'] = $original_size;
277 $results['is_backed_up'] = $is_image_backuped;
278
279 /**
280 * @var $extra_data WRIO_CF_Image_Extra_Data
281 */
282 $extra_data = $optimization_data->get_extra_data();
283 $extra_data->set_main_optimized_data( null );
284 $extra_data->set_error( null );
285 $extra_data->set_error_msg( null );
286 $results['extra_data'] = $extra_data;
287 $mime_type = '';
288 if ( function_exists( 'wp_get_image_mime' ) ) {
289 $mime_type = wp_get_image_mime( $main_file_path );
290 }
291 $results['original_mime_type'] = $mime_type;
292 $results['final_mime_type'] = $mime_type;
293 $optimization_data->configure( $results );
294 $optimization_data->save();
295
296 return [
297 'errors_count' => 0,
298 'original_size' => $original_size,
299 'optimized_size' => $optimized_size,
300 'optimized_count' => 1,
301 ];
302 }
303
304 /**
305 * Отложенная оптимизация аттачмента
306 *
307 * @return bool|array
308 */
309 public function deferredOptimization() {
310 $results = [
311 'original_size' => 0,
312 'optimized_size' => 0,
313 'optimized_count' => 0,
314 'processing' => 1,
315 ];
316
317 $image_processor = WIO_OptimizationTools::getImageProcessor();
318 $optimization_data = $this->getOptimizationData();
319
320 if ( $optimization_data->get_result_status() != 'processing' ) {
321 return false;
322 }
323 // проверяем главную картинку
324 /**
325 * @var $extra_data WRIO_CF_Image_Extra_Data
326 */
327 $extra_data = $optimization_data->get_extra_data();
328 $main_optimized_data = $extra_data->get_main_optimized_data();
329 $main_image_url = '';
330 if ( ! $main_optimized_data['optimized_img_url'] ) {
331 $main_image_url = $image_processor->checkDeferredOptimization( $main_optimized_data );
332 if ( $main_image_url ) {
333 $main_optimized_data['optimized_img_url'] = $main_image_url;
334 $extra_data->set_main_optimized_data( $main_optimized_data );
335 }
336 }
337
338 $thumbnails_processed = true; // для кастомны�
339 папок нет превьюшек, поэтому всегда true
340
341 // когда все файлы получены - со�
342 раняем и возвращаем результат
343 if ( $main_image_url && $thumbnails_processed ) {
344 $original_size = 0;
345 $optimized_size = 0;
346 $original_main_size = filesize( $this->get( 'path' ) );
347 $original_size = $original_size + $original_main_size;
348 $this->replaceOriginalFile(
349 [
350 'optimized_img_url' => $main_image_url,
351 ]
352 );
353 clearstatcache();
354 $optimized_main_size = filesize( $this->get( 'path' ) );
355
356 // при отрицательной оптимизации ставим значение оригинала
357 if ( $optimized_main_size > $original_main_size ) {
358 $optimized_main_size = $original_main_size;
359 }
360
361 $optimized_size = $optimized_size + $optimized_main_size;
362 clearstatcache();
363 $mime_type = '';
364
365 if ( function_exists( 'wp_get_image_mime' ) ) {
366 $mime_type = wp_get_image_mime( $this->get( 'path' ) );
367 }
368
369 $optimization_data->configure(
370 [
371 'final_size' => $optimized_size,
372 'original_size' => $original_size,
373 'result_status' => 'success',
374 'original_mime_type' => $mime_type,
375 'final_mime_type' => $mime_type,
376 ]
377 );
378 $extra_data->set_original_main_size( $original_main_size );
379
380 // удаляем промежуточные данные
381 $extra_data->set_main_optimized_data( null );
382 $extra_data->set_error( null );
383 $extra_data->set_error_msg( null );
384
385 $results['optimized_count'] = 1;
386 $results['original_size'] = $original_size;
387 $results['optimized_size'] = $optimized_size;
388 unset( $results['processing'] );
389 }
390
391 $optimization_data->set_extra_data( $extra_data );
392 $optimization_data->save();
393
394 return $results;
395 }
396
397 /**
398 * Заменяет оригинальный файл на оптимизированный
399 *
400 * @param array $optimized_img_data результат оптимизации ввиде массива данны�
401
402 */
403 public function replaceOriginalFile( $optimized_img_data ) {
404 $optimized_img_url = $optimized_img_data['optimized_img_url'];
405 if ( isset( $optimized_img_data['not_need_download'] ) and $optimized_img_data['not_need_download'] ) {
406 $optimized_file = $optimized_img_url;
407 } else {
408 $optimized_file = $this->remoteDownloadImage( $optimized_img_url );
409 }
410 if ( isset( $optimized_img_data['not_need_replace'] ) and $optimized_img_data['not_need_replace'] ) {
411 // если картинка уже оптимизирована и провайдер её не может уменьшить - он может вернуть положительный ответ, но без самой картинки. В таком случае ничего заменять не надо
412 return true;
413 }
414 if ( ! $optimized_file ) {
415 return false;
416 }
417 $path = $this->path;
418
419 if ( ! is_file( $path ) ) {
420 return false;
421 }
422
423 file_put_contents( $path, $optimized_file );
424
425 return true;
426 }
427
428 /**
429 * Загрузка картинки с удалённого сервера
430 *
431 * todo: RIO-18 можем ли мы создать универсальный метод для все�
432 внешни�
433 запросов, чтобы не дублировать код?
434 *
435 * @param string $url
436 *
437 * @return string
438 */
439 protected function remoteDownloadImage( $url ) {
440 if ( ! function_exists( 'curl_version' ) ) {
441 return file_get_contents( $url );
442 }
443
444 $ch = curl_init();
445 curl_setopt( $ch, CURLOPT_HEADER, 0 );
446 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
447 curl_setopt( $ch, CURLOPT_URL, $url );
448
449 $image_body = curl_exec( $ch );
450 $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
451 if ( $http_code != '200' ) {
452 $image_body = false;
453 }
454 curl_close( $ch );
455
456 return $image_body;
457 }
458
459 /**
460 * Делает резервную копию изображения
461 */
462 public function backup() {
463 $backup = WRIOP_Backup::get_instance();
464
465 return $backup->backupCFImage( $this );
466 }
467
468 /**
469 * Восстанавливает из резервной копии
470 */
471 public function restore() {
472 $backup = WRIOP_Backup::get_instance();
473 $restored = $backup->restoreCFImage( $this );
474
475 if ( is_wp_error( $restored ) ) {
476 return $restored;
477 }
478
479 $optimization_data = $this->getOptimizationData();
480 $optimization_data->set_result_status( 'unoptimized' );
481 $optimization_data->save();
482
483 /**
484 * Хук срабатывает после восстановления cf_image
485 *
486 * @since 1.2.0
487 *
488 * @param RIO_Process_Queue $optimization_data
489 */
490 do_action( 'wbcr/rio/cf_image_restored', $this->optimization_data );
491
492 return true;
493 }
494 }
495