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 / includes / classes / class-rio-backup.php
robin-image-optimizer / includes / classes Last commit date
models 4 months ago processing 4 months ago processors 4 months ago class-rio-attachment.php 4 months ago class-rio-backup.php 5 months ago class-rio-bulk-optimization.php 5 months ago class-rio-cron.php 6 months ago class-rio-image-query.php 4 months ago class-rio-image-statistic.php 4 months ago class-rio-media-library.php 4 months ago class-rio-multisite.php 6 months ago class-rio-optimization-orchestrator.php 4 months ago class-rio-optimization-tools.php 6 months ago class-rio-views.php 4 months ago class-wrio-license.php 6 months ago class-wrio-premium-provider.php 6 months ago class-wrio-support.php 6 months ago index.php 6 months ago
class-rio-backup.php
567 lines
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * Класс для работы с резервным копированием изображений.
10 *
11 * @version 1.0
12 */
13 class WIO_Backup {
14
15 const BACKUP_DIR_NAME = 'wio_backup';
16 const TEMP_DIR_NAME = 'temp';
17
18 /**
19 * The single instance of the class.
20 *
21 * @since 1.3.0
22 * @access protected
23 * @var object
24 */
25 protected static $_instance;
26
27 /**
28 * @var array Данные о папке uploads, возвращаемые функцией wp_upload_dir()
29 */
30 protected $wp_upload_dir;
31
32 /**
33 * @var string Путь к папке с резервными копиями изображений
34 */
35 private $backup_dir;
36
37 /**
38 * @since 1.3.0
39 * @var string
40 */
41 private $blog_backup_dir;
42
43 /**
44 * Инициализация бекапа
45 */
46 public function __construct() {
47 $this->wp_upload_dir = wp_upload_dir();
48 }
49
50 /**
51 * @since 1.3.0
52 *
53 * @return object|\static object Main instance.
54 */
55 public static function get_instance() {
56 if ( ! isset( static::$_instance ) ) {
57 static::$_instance = new static();
58 }
59
60 return static::$_instance;
61 }
62
63 /**
64 * Проверка возможности записи в папку uploads.
65 *
66 * @return bool
67 */
68 public function isUploadWritable() {
69 $upload_dir = $this->wp_upload_dir['basedir'];
70
71 if ( is_dir( $upload_dir ) && wp_is_writable( $upload_dir ) ) {
72 return true;
73 }
74
75 return false;
76 }
77
78 /**
79 * Проверка возможности записи в папку бекап.
80 *
81 * @return bool
82 */
83 public function isBackupWritable() {
84
85 $backup_dir = $this->getBackupDir();
86
87 if ( is_wp_error( $backup_dir ) || ! wp_is_writable( $backup_dir ) ) {
88 return false;
89 }
90
91 return true;
92 }
93
94 /**
95 * Путь к папке с бекапами
96 *
97 * @return string|WP_Error
98 */
99 public function getBackupDir() {
100 if ( $this->backup_dir ) {
101 return $this->backup_dir;
102 }
103
104 $backup_dir = wp_normalize_path( trailingslashit( $this->wp_upload_dir['basedir'] ) . self::BACKUP_DIR_NAME );
105
106 if ( ! is_dir( $backup_dir ) ) {
107 $backup_dir = $this->mkdir( $backup_dir );
108
109 if ( is_wp_error( $backup_dir ) ) {
110 return $backup_dir;
111 }
112 }
113
114 $this->backup_dir = apply_filters( 'wbcr/rio/backup/backup_dir', trailingslashit( $backup_dir ) );
115
116 return $this->backup_dir;
117 }
118
119 /**
120 * Путь к папке с бекапами блога.
121 *
122 * Используется в мультисайт режиме.
123 *
124 * @return string|WP_Error
125 */
126 public function getBlogBackupDir() {
127 if ( $this->blog_backup_dir ) {
128 return $this->blog_backup_dir;
129 }
130
131 $wp_upload_dir = wp_upload_dir();
132 $backup_dir = wp_normalize_path( trailingslashit( $wp_upload_dir['basedir'] ) . self::BACKUP_DIR_NAME );
133
134 if ( ! is_dir( $backup_dir ) ) {
135 $backup_dir = $this->mkdir( $backup_dir );
136
137 if ( is_wp_error( $backup_dir ) ) {
138 return $backup_dir;
139 }
140 }
141
142 $this->blog_backup_dir = trailingslashit( $backup_dir );
143
144 return $this->blog_backup_dir;
145 }
146
147 /**
148 * Очищает папку с резервными копиями
149 *
150 * @return bool
151 */
152 public function removeBackupDir() {
153 $backup_dir = $this->getBackupDir();
154
155 return wrio_rmdir( $backup_dir );
156 }
157
158 /**
159 * Очищает папку с резервными копиями блога
160 * Используется в мультисайт режиме
161 *
162 * @return bool
163 */
164 public function removeBlogBackupDir() {
165 $backup_dir = $this->getBlogBackupDir();
166
167 return wrio_rmdir( $backup_dir );
168 }
169
170 /**
171 * Получает путь к папке с резервными копиями
172 *
173 * @param array $attachment_meta метаданные аттачмента
174 *
175 * @return string
176 */
177 public function getAttachmentBackupDir( $attachment_meta ) {
178 $backup_dir = $this->getBackupDir();
179
180 // Get all subfolders in which the image is stored.
181 // This is necessary to create an alternate subfolders
182 // in directory where they are stored in backups.
183 $subfolders = dirname( $attachment_meta['file'] );
184
185 $backup_dir .= $subfolders;
186
187 if ( ! is_dir( $backup_dir ) ) {
188 $backup_dir = $this->mkdir( $backup_dir );
189
190 if ( is_wp_error( $backup_dir ) ) {
191 return $backup_dir;
192 }
193 }
194
195 return trailingslashit( $backup_dir );
196 }
197
198 /**
199 * Делаем резервную копию аттачмента
200 *
201 * @param WIO_Attachment $wio_attachment аттачмент
202 *
203 * @return bool|WP_Error
204 */
205 public function backupAttachment( WIO_Attachment $wio_attachment ) {
206 $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
207
208 if ( ! $backup_origin_images ) {
209 return false; // если бекап не требуется
210 }
211
212 $backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
213
214 if ( is_wp_error( $backup_dir ) ) {
215 return $backup_dir;
216 }
217
218 $full = $this->backupAttachmentSize( $wio_attachment );
219
220 if ( is_wp_error( $full ) ) {
221 return $full;
222 }
223
224 $allowed_sizes = $wio_attachment->getAllowedSizes();
225
226 if ( ! empty( $allowed_sizes ) ) {
227 foreach ( (array) $allowed_sizes as $image_size ) {
228 $size_backup = $this->backupAttachmentSize( $wio_attachment, $image_size );
229
230 if ( is_wp_error( $size_backup ) ) {
231 return $size_backup;
232 }
233 }
234 }
235
236 return true;
237 }
238
239 /**
240 * Восстанавливаем аттачмент из резервной копии
241 *
242 * @param WIO_Attachment $wio_attachment аттачмент
243 *
244 * @return bool|WP_Error
245 */
246 public function restoreAttachment( WIO_Attachment $wio_attachment ) {
247 $backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
248
249 if ( is_wp_error( $backup_dir ) ) {
250 return $backup_dir;
251 }
252
253 $restore_result = $this->restoreAttachmentSize( $wio_attachment );
254
255 if ( is_wp_error( $restore_result ) ) {
256 return $restore_result;
257 }
258
259 $attachment_meta = wp_get_attachment_metadata( $wio_attachment->get( 'id' ) );
260
261 if ( isset( $attachment_meta['old_width'] ) && isset( $attachment_meta['old_width'] ) ) {
262 $attachment_meta['width'] = $attachment_meta['old_width'];
263 $attachment_meta['height'] = $attachment_meta['old_height'];
264 wp_update_attachment_metadata( $wio_attachment->get( 'id' ), $attachment_meta );
265 }
266
267 $allowed_sizes = $wio_attachment->getAllowedSizes();
268
269 if ( $allowed_sizes ) {
270 foreach ( $allowed_sizes as $image_size ) {
271 $this->restoreAttachmentSize( $wio_attachment, $image_size );
272 }
273 }
274
275 return true;
276 }
277
278 /**
279 * Создает временное изображение с уникальным именем.
280 *
281 * Необ�
282 одимо для провайдеров, который кешируют изображения по имени файла,
283 * чтобы сбросить кеш, нужно отдать провайдеру изображение с другим именем.
284 *
285 * @since 1.1.2
286 *
287 * @param string $file_path путь к изображению
288 *
289 * @return array|WP_Error
290 */
291 public function createTempAttachment( $file_path ) {
292 if ( $this->isBackupWritable() ) {
293
294 $temp_dir = $this->getBackupDir() . self::TEMP_DIR_NAME . '/';
295 $temp_dir_url = trailingslashit( $this->wp_upload_dir['baseurl'] ) . self::BACKUP_DIR_NAME . '/' . self::TEMP_DIR_NAME . '/';
296
297 if ( ! is_dir( $temp_dir ) ) {
298 $temp_dir = $this->mkdir( $temp_dir );
299
300 if ( is_wp_error( $temp_dir ) ) {
301 return $temp_dir;
302 }
303 }
304
305 $temp_file_id = uniqid();
306 $file_name = pathinfo( $file_path, PATHINFO_FILENAME );
307 $file_extension = pathinfo( $file_path, PATHINFO_EXTENSION );
308 $new_file_name = $temp_file_id . '_' . md5( $file_name ) . '.' . $file_extension;
309
310 $temp_file_path = $temp_dir . $new_file_name;
311 $temp_file_url = $temp_dir_url . $new_file_name;
312
313 if ( is_file( $file_path ) ) {
314 if ( ! @copy( $file_path, $temp_file_path ) ) {
315 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap original file %s with %s as copy() failed.', $temp_file_path, $file_path ) );
316
317 return new WP_Error( 'copy_file_to_temp_dir_error', __( 'Could not copy the file to the temporary directory', 'robin-image-optimizer' ) );
318 }
319 }
320
321 WRIO_Plugin::app()->logger->info( sprintf( 'Creation of temporary attachment (%s) successfully completed!', $file_path ) );
322
323 return [
324 'id' => $temp_file_id,
325 'image_path' => $temp_file_path,
326 'image_url' => $temp_file_url,
327 ];
328 }
329
330 return new WP_Error( 'backup_writable_error', __( 'It is not possible to create a temporary file, the backup folder is not writable.', 'robin-image-optimizer' ) );
331 }
332
333 /**
334 * Резервное копирование файла аттачмента.
335 *
336 * @param WIO_Attachment $wio_attachment аттачмент
337 * @param string $image_size Размер(thumbnail, medium ... )
338 *
339 * @return bool|WP_Error
340 */
341 protected function backupAttachmentSize( WIO_Attachment $wio_attachment, $image_size = '' ) {
342 if ( $image_size ) {
343 $original_file = $wio_attachment->getImageSizePath( $image_size );
344 } else {
345 $original_file = $wio_attachment->get( 'path' );
346 }
347
348 $backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
349
350 // проверить запись в папку
351 if ( is_wp_error( $backup_dir ) ) {
352 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to create backup dir, error: %s', $backup_dir->get_error_message() ) );
353
354 return $backup_dir;
355 }
356
357 if ( ! $original_file ) {
358 // бывает такое, что размера превьюшки нет в базе данны�
359 .
360 // это не считается ошибкой, поэтому сразу пропускаем
361 return false;
362 }
363
364 $backup_file = $backup_dir . wp_basename( $original_file );
365
366 if ( is_file( $original_file ) ) {
367 if ( ! @copy( $original_file, $backup_file ) ) {
368 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to copy %s to %s as copy() failed', $original_file, $backup_file ) );
369 }
370 }
371
372 return true;
373 }
374
375 /**
376 * Восстановление файла аттачмента из резервной копии
377 *
378 * @param WIO_Attachment $wio_attachment аттачмент
379 * @param string|null $image_size Размер(thumbnail, medium ... )
380 *
381 * @return bool|WP_Error
382 */
383 protected function restoreAttachmentSize( WIO_Attachment $wio_attachment, $image_size = null ) {
384
385 if ( ! empty( $image_size ) ) {
386 $original_file = $wio_attachment->getImageSizePath( $image_size );
387 } else {
388 $original_file = $wio_attachment->get( 'path' );
389 }
390
391 $backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
392
393 if ( is_wp_error( $backup_dir ) ) {
394 return $backup_dir;
395 }
396
397 if ( empty( $original_file ) ) {
398 return false;
399 }
400
401 $backup_file = $backup_dir . wp_basename( $original_file );
402
403 if ( ! is_file( $backup_file ) ) {
404 WRIO_Plugin::app()->logger->error( sprintf( 'Unable to restore from a backup. There is no file, attachment id: %s, backup file: %s', $wio_attachment->get( 'id' ), $backup_file ) );
405
406 return false;
407 }
408
409 if ( ! @copy( $backup_file, $original_file ) ) {
410 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap %s with %s as copy() failed', $backup_file, $original_file ) );
411
412 return false;
413 }
414
415 if ( ! @unlink( $backup_file ) ) {
416 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to delete backup file %s as unlink() failed', $backup_file ) );
417
418 return false;
419 }
420
421 WRIO_Plugin::app()->logger->info( sprintf( 'Restored file: %s.', $backup_file ) );
422
423 return true;
424 }
425
426 /**
427 * Get the backup file path for an attachment size if it exists.
428 *
429 * @param int $attachment_id The attachment ID.
430 * @param string $size The image size (e.g., 'original', 'thumbnail', 'medium').
431 *
432 * @return string|null The backup file path if it exists, null otherwise.
433 * @since 1.0.0
434 */
435 public function getAttachmentBackupPath( $attachment_id, $size = 'original' ) {
436 $attachment_meta = wp_get_attachment_metadata( $attachment_id );
437
438 if ( empty( $attachment_meta ) || ! isset( $attachment_meta['file'] ) ) {
439 return null;
440 }
441
442 $backup_dir = $this->getAttachmentBackupDir( $attachment_meta );
443
444 if ( is_wp_error( $backup_dir ) ) {
445 return null;
446 }
447
448 // Get the filename based on size
449 if ( 'original' === $size ) {
450 $filename = wp_basename( $attachment_meta['file'] );
451 } elseif ( isset( $attachment_meta['sizes'][ $size ]['file'] ) ) {
452 $filename = $attachment_meta['sizes'][ $size ]['file'];
453 } else {
454 return null;
455 }
456
457 $backup_path = $backup_dir . $filename;
458
459 return file_exists( $backup_path ) ? $backup_path : null;
460 }
461
462 /**
463 * Удаляем резервные копии аттачмента
464 *
465 * @param int $attachment_id аттачмент id
466 *
467 * @return bool|WP_Error
468 */
469 public function removeAttachmentBackup( $attachment_id ) {
470 $attachment_meta = wp_get_attachment_metadata( $attachment_id );
471 $backup_dir = $this->getAttachmentBackupDir( $attachment_meta );
472
473 if ( is_wp_error( $backup_dir ) ) {
474 return $backup_dir;
475 }
476
477 $main_file_path = $backup_dir . wp_basename( $attachment_meta['file'] );
478
479 if ( ! file_exists( $main_file_path ) ) {
480 WRIO_Plugin::app()->logger->error( sprintf( "Failed to remove an attachment file. File (%s) isn't exists. Attachment #%s", $main_file_path, $attachment_id ) );
481 }
482
483 if ( ! @unlink( $main_file_path ) ) {
484 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to unlink a main file (%s) for attachment #%s', $main_file_path, $attachment_id ) );
485 }
486
487 if ( isset( $attachment_meta['sizes'] ) && is_array( $attachment_meta['sizes'] ) ) {
488 foreach ( $attachment_meta['sizes'] as $size ) {
489 $thumbnail_file_path = $backup_dir . $size['file'];
490
491 if ( ! file_exists( $thumbnail_file_path ) ) {
492 WRIO_Plugin::app()->logger->error( sprintf( "Failed to remove a thumbnail file. File (%s) isn't exists. Attachment #%s", $thumbnail_file_path, $attachment_id ) );
493 }
494
495 if ( ! @unlink( $thumbnail_file_path ) ) {
496 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to unlink thumbnail (%s) for attachment #%s', $thumbnail_file_path, $attachment_id ) );
497 }
498 }
499 }
500
501 return true;
502 }
503
504 /**
505 * alternateStorage
506 *
507 * @param array $servers
508 *
509 * @return array
510 */
511 public static function alternateStorage( $servers ) {
512
513 return $servers;
514 }
515
516 /**
517 * @since 1.3.0
518 *
519 * @param string $dir
520 *
521 * @return string|WP_Error
522 */
523 protected function mkdir( $dir ) {
524 WRIO_Plugin::app()->logger->info( sprintf( 'Try to create backup directory. Backup dir: (%s)', $dir ) );
525
526 if ( ! wp_mkdir_p( $dir ) ) {
527 WRIO_Plugin::app()->logger->error( sprintf( 'Unable to create backup directory (%s) as mkdir() failed', $dir ) );
528
529 return new WP_Error( 'mkdir_failed', sprintf( 'Unable to create backup folder (%s) as mkdir() failed.', $dir ) );
530 }
531
532 return $dir;
533 }
534
535 /**
536 * @since 1.3.0
537 *
538 * @param string $backup_file
539 * @param string $original_file
540 *
541 * @return bool
542 */
543 protected function restore_file( $backup_file, $original_file ) {
544 if ( is_file( $backup_file ) ) {
545 if ( ! @copy( $backup_file, $original_file ) ) {
546 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap original file (%s) with %s as copy() failed', $backup_file, $original_file ) );
547
548 return false;
549 }
550
551 if ( ! @unlink( $backup_file ) ) {
552 WRIO_Plugin::app()->logger->error( sprintf( 'Failed to delete backup file (%s) as unlink() failed', $backup_file ) );
553
554 return false;
555 }
556
557 WRIO_Plugin::app()->logger->info( sprintf( 'Restored file: %s.', $backup_file ) );
558
559 return true;
560 }
561
562 WRIO_Plugin::app()->logger->error( sprintf( 'Unable to restore from a backup. There is no file (%s).', $backup_file ) );
563
564 return false;
565 }
566 }
567