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.image-nextgen.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.image-nextgen.php
554 lines
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * Класс для работы с nextgen image
10 *
11 * @version 1.0
12 */
13 class WRIO_Image_Nextgen {
14
15 /**
16 * @var int номер картинки в таблице nextgen
17 */
18 private $id;
19
20 /**
21 * @var string путь к картинке
22 */
23 private $path;
24
25 /**
26 * @var string путь к миниатюре
27 */
28 private $thumbnail_path;
29
30 /**
31 * @var string имя файла
32 */
33 private $file;
34
35 /**
36 * @var string имя файла миниатюры
37 */
38 private $thumbnail_file;
39
40 /**
41 * @var string УРЛ
42 */
43 private $url;
44
45 /**
46 * @var string УРЛ миниатюры
47 */
48 private $thumbnail_url;
49
50 /**
51 * @var array мета данные картинки
52 */
53 private $meta;
54
55 /**
56 * @var array мета данные галереи
57 */
58 private $gallery_meta;
59
60 /**
61 * @var RIO_Process_Queue данные по оптимизации
62 */
63 private $optimization_data = null;
64
65 /**
66 * Инициализация картинки из nextgen gallery
67 *
68 * @param int $image_id номер картинки в таблице nextgen
69 * @param array $image_data метаданные картинки
70 */
71 public function __construct( $image_id, $image_data = false ) {
72 global $wpdb;
73
74 $this->id = $image_id;
75
76 if ( $image_data ) {
77 $this->meta = $image_data;
78 } else {
79 $this->meta = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}ngg_pictures WHERE pid = " . intval( $image_id ) );
80 }
81
82 $this->file = $this->meta->filename;
83 $this->thumbnail_file = 'thumbs-' . $this->meta->filename;
84
85 global $wpdb;
86 $this->gallery_meta = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}ngg_gallery WHERE gid = " . intval( $this->meta->galleryid ) );
87
88 $this->path = wp_normalize_path( ABSPATH . trailingslashit( $this->gallery_meta->path ) . $this->meta->filename );
89 $this->thumbnail_path = wp_normalize_path( ABSPATH . trailingslashit( $this->gallery_meta->path ) . 'thumbs/' . $this->thumbnail_file );
90 $this->url = site_url( trailingslashit( $this->gallery_meta->path ) . $this->meta->filename );
91 $this->thumbnail_url = site_url( trailingslashit( $this->gallery_meta->path ) . 'thumbs/' . $this->thumbnail_file );
92 }
93
94 /**
95 * Возвращает свойство аттачмента
96 *
97 * @param string $property имя свойства
98 *
99 * @return mixed
100 */
101 public function get( $property ) {
102 if ( isset( $this->$property ) ) {
103 return $this->$property;
104 }
105
106 return false;
107 }
108
109 /**
110 * Проверка на оптимизацию изображения
111 *
112 * @return bool
113 */
114 public function isOptimized() {
115 $optimization_data = $this->getOptimizationData();
116 if ( empty( $optimization_data ) ) {
117 return false;
118 }
119 if ( $optimization_data->is_optimized() ) {
120 return true;
121 }
122
123 return false;
124 }
125
126 /**
127 * Возвращает данные по оптимизации
128 *
129 * @return RIO_Process_Queue
130 */
131 public function getOptimizationData() {
132 if ( empty( $this->optimization_data ) ) {
133 $this->optimization_data = $this->createOptimizationData();
134 $this->optimization_data->load();
135 }
136
137 return $this->optimization_data;
138 }
139
140 /**
141 * Создаёт новый объект RIO_Process_Queue
142 *
143 * @return RIO_Process_Queue
144 */
145 public function createOptimizationData() {
146 return new RIO_Process_Queue(
147 [
148 'object_id' => $this->id,
149 'item_type' => 'nextgen',
150 ]
151 );
152 }
153
154 /**
155 * Оптимизирует изображения
156 *
157 * @param string $optimization_level качество
158 *
159 * @return array
160 */
161 public function optimize( $optimization_level = '' ) {
162 $is_image_backuped = $this->backup();
163
164 if ( is_wp_error( $is_image_backuped ) ) {
165 $error_msg = $is_image_backuped->get_error_message() . PHP_EOL;
166
167 return [
168 'errors_count' => 1,
169 'original_size' => 0,
170 'optimized_size' => 0,
171 'optimized_count' => 0,
172 ];
173 }
174
175 // делаем рисайз
176 $image_processor = WIO_OptimizationTools::getImageProcessor();
177
178 if ( ! $optimization_level ) {
179 $optimization_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
180 }
181
182 if ( $optimization_level == 'custom' ) {
183 $custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
184 $optimization_level = intval( $custom_quality );
185 }
186
187 $optimization_data = $this->createOptimizationData();
188 $results = [];
189 $results['processing_level'] = $optimization_level;
190 $results['original_mime_type'] = '';
191 $results['final_mime_type'] = '';
192
193 // $gallery_path = trailingslashit( $this->gallery_meta->path );
194 // $main_file_path = wp_normalize_path( ABSPATH . $gallery_path . $this->meta->filename );
195 // $main_file_url = home_url( $gallery_path . $this->meta->filename );
196
197 $main_file_path = $this->path;
198 $main_file_url = $this->url;
199
200 clearstatcache(); // на всякий случай очистим кеш файловой статистики
201 if ( ! file_exists( $main_file_path ) ) {
202 $results['result_status'] = 'error';
203 $results['original_size'] = 0;
204 $results['final_size'] = 0;
205 $extra_data = [
206 'error' => 'file',
207 'error_msg' => __( 'File not found', 'robin-image-optimizer' ),
208 ];
209 $results['extra_data'] = new WRIO_Nextgen_Extra_Data( $extra_data );
210 $optimization_data->configure( $results );
211 $optimization_data->save();
212
213 return [
214 'errors_count' => 1,
215 'original_size' => 0,
216 'optimized_size' => 0,
217 'optimized_count' => 0,
218 ];
219 }
220 $original_main_size = filesize( $main_file_path ); // оптимизированный размер только главной картинки
221
222 $optimized_img_data = $image_processor->process(
223 [
224 'image_url' => $main_file_url,
225 'image_path' => $main_file_path,
226 'quality' => $image_processor->quality( $optimization_level ),
227 'save_exif' => WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false ),
228 ]
229 );
230
231 if ( is_wp_error( $optimized_img_data ) ) {
232 $results['result_status'] = 'error';
233 $results['original_size'] = 0;
234 $results['final_size'] = 0;
235 $extra_data = [
236 'error' => 'optimization',
237 'error_msg' => $optimized_img_data->get_error_message(),
238 ];
239 $results['extra_data'] = new WRIO_Nextgen_Extra_Data( $extra_data );
240 $optimization_data->configure( $results );
241 $optimization_data->save();
242
243 return [
244 'errors_count' => 1,
245 'original_size' => 0,
246 'optimized_size' => 0,
247 'optimized_count' => 0,
248 ];
249 }
250
251 // оптимизируем thumbnail
252 $optimized_thumbnail_data = $image_processor->process(
253 [
254 'image_url' => $this->thumbnail_url,
255 'image_path' => $this->thumbnail_path,
256 'quality' => $image_processor->quality( $optimization_level ),
257 'save_exif' => WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false ),
258 ]
259 );
260
261 // отложенная оптимизация
262 if ( isset( $optimized_img_data['status'] ) && $optimized_img_data['status'] == 'processing' ) {
263 $results['result_status'] = 'processing';
264 $results['is_backed_up'] = $is_image_backuped;
265 $results['original_size'] = 0;
266 $results['final_size'] = 0;
267 $extra_data = [
268 'main_optimized_data' => $optimized_img_data,
269 'thumbnails_optimized_data' => $optimized_thumbnail_data,
270 'image_relative_path' => str_replace( untrailingslashit( ABSPATH ), '', $this->path ),
271 ];
272 $results['extra_data'] = new WRIO_Nextgen_Extra_Data( $extra_data );
273 $optimization_data->configure( $results );
274 $optimization_data->save();
275
276 return [
277 'processing' => 1,
278 'original_size' => 0,
279 'optimized_size' => 0,
280 ];
281 }
282
283 $this->replaceOriginalFile( $optimized_img_data );
284
285 // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
286 if ( ! $optimized_img_data['optimized_size'] ) {
287 clearstatcache();
288 $optimized_img_data['optimized_size'] = filesize( $main_file_path );
289 }
290
291 // при отрицательной оптимизации ставим значение оригинала
292 if ( $optimized_img_data['optimized_size'] > $original_main_size ) {
293 $optimized_img_data['optimized_size'] = $original_main_size;
294 }
295
296 $original_size = $original_main_size;
297 $optimized_size = $optimized_img_data['optimized_size'];
298 $optimized_main_size = $optimized_img_data['optimized_size'];
299
300 if ( ! is_wp_error( $optimized_thumbnail_data ) ) {
301 $original_size += filesize( $this->thumbnail_path );
302 $this->replaceOriginalFile( $optimized_thumbnail_data, 'thumbnail' );
303 // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
304 if ( ! $optimized_thumbnail_data['optimized_size'] ) {
305 clearstatcache();
306 $optimized_thumbnail_data['optimized_size'] = filesize( $this->thumbnail_path );
307 }
308 $optimized_size += $optimized_thumbnail_data['optimized_size'];
309 }
310 $results['result_status'] = 'success';
311 $results['final_size'] = $optimized_size;
312 $results['original_size'] = $original_size;
313 $results['is_backed_up'] = $is_image_backuped;
314 $extra_data = [
315 'original_main_size' => $original_main_size,
316 'optimized_main_size' => $optimized_main_size,
317 'image_relative_path' => str_replace( wp_normalize_path( untrailingslashit( ABSPATH ) ), '', $this->path ),
318 ];
319 $results['extra_data'] = new WRIO_Nextgen_Extra_Data( $extra_data );
320 $mime_type = '';
321 if ( function_exists( 'wp_get_image_mime' ) ) {
322 $mime_type = wp_get_image_mime( $main_file_path );
323 }
324 $results['original_mime_type'] = $mime_type;
325 $results['final_mime_type'] = $mime_type;
326 $optimization_data->configure( $results );
327 $optimization_data->save();
328
329 return [
330 'errors_count' => 0,
331 'original_size' => $original_size,
332 'optimized_size' => $optimized_size,
333 'optimized_count' => 1,
334 ];
335 }
336
337 /**
338 * Отложенная оптимизация аттачмента
339 *
340 * @return bool|array
341 */
342 public function deferredOptimization() {
343 $results = [
344 'original_size' => 0,
345 'optimized_size' => 0,
346 'optimized_count' => 0,
347 'processing' => 1,
348 ];
349 $image_processor = WIO_OptimizationTools::getImageProcessor();
350 $optimization_data = $this->getOptimizationData();
351 if ( $optimization_data->get_result_status() != 'processing' ) {
352 return false;
353 }
354 // проверяем главную картинку
355 /**
356 * @var WRIO_Nextgen_Extra_Data $extra_data
357 */
358 $extra_data = $optimization_data->get_extra_data();
359 $main_optimized_data = $extra_data->get_main_optimized_data();
360 $main_image_url = '';
361 if ( ! $main_optimized_data['optimized_img_url'] ) {
362 $main_image_url = $image_processor->checkDeferredOptimization( $main_optimized_data );
363 if ( $main_image_url ) {
364 $main_optimized_data['optimized_img_url'] = $main_image_url;
365 $extra_data->set_main_optimized_data( $main_optimized_data );
366 }
367 }
368
369 $thumbnails_processed = true;
370 $thumbnail_optimized_data = $extra_data->get_thumbnails_optimized_data();
371 if ( ! $thumbnail_optimized_data['optimized_img_url'] ) {
372 $thumbnail_image_url = $image_processor->checkDeferredOptimization( $thumbnail_optimized_data );
373 if ( $thumbnail_image_url ) {
374 $thumbnail_optimized_data['optimized_img_url'] = $thumbnail_image_url;
375 } else {
376 $thumbnails_processed = false;
377 }
378 }
379 $extra_data->set_thumbnails_optimized_data( $thumbnail_optimized_data );
380
381 // когда все файлы получены - со�
382 раняем и возвращаем результат
383 if ( $main_image_url && $thumbnails_processed ) {
384 $original_size = 0;
385 $optimized_size = 0;
386 $thumbnails_count = 0;
387 $original_main_size = filesize( $this->get( 'path' ) );
388 $original_size = $original_size + $original_main_size;
389 $this->replaceOriginalFile(
390 [
391 'optimized_img_url' => $main_image_url,
392 ]
393 );
394 clearstatcache();
395 $optimized_main_size = filesize( $this->get( 'path' ) );
396 // при отрицательной оптимизации ставим значение оригинала
397 if ( $optimized_main_size > $original_main_size ) {
398 $optimized_main_size = $original_main_size;
399 }
400 $optimized_size = $optimized_size + $optimized_main_size;
401 $thumbnail_file = $this->get( 'thumbnail_path' );
402 $original_size = $original_size + filesize( $thumbnail_file );
403 $this->replaceOriginalFile(
404 [
405 'optimized_img_url' => $thumbnail_optimized_data['optimized_img_url'],
406 ],
407 'thumbnail'
408 );
409 clearstatcache();
410 $optimized_size = $optimized_size + filesize( $thumbnail_file );
411 ++$thumbnails_count;
412 $mime_type = '';
413 if ( function_exists( 'wp_get_image_mime' ) ) {
414 $mime_type = wp_get_image_mime( $this->get( 'path' ) );
415 }
416 $optimization_data->configure(
417 [
418 'final_size' => $optimized_size,
419 'original_size' => $original_size,
420 'result_status' => 'success',
421 'original_mime_type' => $mime_type,
422 'final_mime_type' => $mime_type,
423 ]
424 );
425 $extra_data->set_original_main_size( $original_main_size );
426 // удаляем промежуточные данные
427 $extra_data->set_main_optimized_data( null );
428 $extra_data->set_thumbnails_optimized_data( null );
429
430 $results['optimized_count'] = 1;
431 $results['original_size'] = $original_size;
432 $results['optimized_size'] = $optimized_size;
433 unset( $results['processing'] );
434 }
435 $optimization_data->set_extra_data( $extra_data );
436 $optimization_data->save();
437
438 return $results;
439 }
440
441 /**
442 * Заменяет оригинальный файл на оптимизированный
443 *
444 * @param array $optimized_img_data результат оптимизации ввиде массива данны�
445
446 * @param string $image_size Размер(thumbnail, medium ... )
447 */
448 public function replaceOriginalFile( $optimized_img_data, $image_size = '' ) {
449 $optimized_img_url = $optimized_img_data['optimized_img_url'];
450 if ( isset( $optimized_img_data['not_need_download'] ) and $optimized_img_data['not_need_download'] ) {
451 $optimized_file = $optimized_img_url;
452 } else {
453 $optimized_file = $this->remoteDownloadImage( $optimized_img_url );
454 }
455 if ( isset( $optimized_img_data['not_need_replace'] ) and $optimized_img_data['not_need_replace'] ) {
456 // если картинка уже оптимизирована и провайдер её не может уменьшить - он может вернуть положительный ответ, но без самой картинки. В таком случае ничего заменять не надо
457 return true;
458 }
459 if ( ! $optimized_file ) {
460 return false;
461 }
462 $path = $this->path;
463 if ( $image_size == 'thumbnail' ) {
464 $path = $this->thumbnail_path;
465 }
466
467 if ( ! is_file( $path ) ) {
468 return false;
469 }
470
471 file_put_contents( $path, $optimized_file );
472
473 return true;
474 }
475
476 /**
477 * Загрузка картинки с удалённого сервера
478 *
479 * todo: RIO-18 можем ли мы создать универсальный метод для все�
480 внешни�
481 запросов, чтобы не дублировать код?
482 *
483 * @param string $url
484 *
485 * @return string
486 */
487 protected function remoteDownloadImage( $url ) {
488 if ( ! function_exists( 'curl_version' ) ) {
489 return file_get_contents( $url );
490 }
491
492 $ch = curl_init();
493 curl_setopt( $ch, CURLOPT_HEADER, 0 );
494 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
495 curl_setopt( $ch, CURLOPT_URL, $url );
496
497 $image_body = curl_exec( $ch );
498 $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
499 if ( $http_code != '200' ) {
500 $image_body = false;
501 }
502 curl_close( $ch );
503
504 return $image_body;
505 }
506
507 /**
508 * Делает резервную копию изображения
509 */
510 public function backup() {
511 $backup = WRIOP_Backup::get_instance();
512
513 return $backup->backupNextgen( $this );
514 }
515
516 /**
517 * Восстанавливает из резервной копии.
518 *
519 * @return bool|WP_Error
520 */
521 public function restore() {
522
523 $backup = WRIOP_Backup::get_instance();
524 $restored = $backup->restoreNextgen( $this );
525
526 if ( is_wp_error( $restored ) ) {
527 return $restored;
528 }
529
530 global $wpdb;
531
532 $io_db_table = RIO_Process_Queue::table_name();
533
534 $wpdb->delete(
535 $io_db_table,
536 [
537 'object_id' => $this->id,
538 'item_type' => 'nextgen',
539 ]
540 );
541
542 /**
543 * Хук срабатывает после восстановления nextgen image
544 *
545 * @param RIO_Process_Queue $optimization_data
546 *
547 * @since 1.2.0
548 */
549 do_action( 'wbcr/rio/nextgen_image_restored', $this->optimization_data );
550
551 return true;
552 }
553 }
554