565 lines
16 KiB
PHP
565 lines
16 KiB
PHP
<?php
|
||
|
||
// Exit if accessed directly
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Класс для работы с резервным копированием изображений.
|
||
*
|
||
* @version 1.0
|
||
*/
|
||
class WIO_Backup {
|
||
|
||
const BACKUP_DIR_NAME = 'wio_backup';
|
||
const TEMP_DIR_NAME = 'temp';
|
||
|
||
/**
|
||
* The single instance of the class.
|
||
*
|
||
* @since 1.3.0
|
||
* @access protected
|
||
* @var object
|
||
*/
|
||
protected static $_instance;
|
||
|
||
/**
|
||
* @var array Данные о папке uploads, возвращаемые функцией wp_upload_dir()
|
||
*/
|
||
protected $wp_upload_dir;
|
||
|
||
/**
|
||
* @var string Путь к папке с резервными копиями изображений
|
||
*/
|
||
private $backup_dir;
|
||
|
||
/**
|
||
* @since 1.3.0
|
||
* @var string
|
||
*/
|
||
private $blog_backup_dir;
|
||
|
||
/**
|
||
* Инициализация бекапа
|
||
*/
|
||
public function __construct() {
|
||
$this->wp_upload_dir = wp_upload_dir();
|
||
}
|
||
|
||
/**
|
||
* @since 1.3.0
|
||
*
|
||
* @return object|\static object Main instance.
|
||
*/
|
||
public static function get_instance() {
|
||
if ( ! isset( static::$_instance ) ) {
|
||
static::$_instance = new static();
|
||
}
|
||
|
||
return static::$_instance;
|
||
}
|
||
|
||
/**
|
||
* Проверка возможности записи в папку uploads.
|
||
*
|
||
* @return bool
|
||
*/
|
||
public function isUploadWritable() {
|
||
$upload_dir = $this->wp_upload_dir['basedir'];
|
||
|
||
if ( is_dir( $upload_dir ) && wp_is_writable( $upload_dir ) ) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Проверка возможности записи в папку бекап.
|
||
*
|
||
* @return bool
|
||
*/
|
||
public function isBackupWritable() {
|
||
|
||
$backup_dir = $this->getBackupDir();
|
||
|
||
if ( is_wp_error( $backup_dir ) || ! wp_is_writable( $backup_dir ) ) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Путь к папке с бекапами
|
||
*
|
||
* @return string|WP_Error
|
||
*/
|
||
public function getBackupDir() {
|
||
if ( $this->backup_dir ) {
|
||
return $this->backup_dir;
|
||
}
|
||
|
||
$backup_dir = wp_normalize_path( trailingslashit( $this->wp_upload_dir['basedir'] ) . self::BACKUP_DIR_NAME );
|
||
|
||
if ( ! is_dir( $backup_dir ) ) {
|
||
$backup_dir = $this->mkdir( $backup_dir );
|
||
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
return $backup_dir;
|
||
}
|
||
}
|
||
|
||
$this->backup_dir = apply_filters( 'wbcr/rio/backup/backup_dir', trailingslashit( $backup_dir ) );
|
||
|
||
return $this->backup_dir;
|
||
}
|
||
|
||
/**
|
||
* Путь к папке с бекапами блога.
|
||
*
|
||
* Используется в мультисайт режиме.
|
||
*
|
||
* @return string|WP_Error
|
||
*/
|
||
public function getBlogBackupDir() {
|
||
if ( $this->blog_backup_dir ) {
|
||
return $this->blog_backup_dir;
|
||
}
|
||
|
||
$wp_upload_dir = wp_upload_dir();
|
||
$backup_dir = wp_normalize_path( trailingslashit( $wp_upload_dir['basedir'] ) . self::BACKUP_DIR_NAME );
|
||
|
||
if ( ! is_dir( $backup_dir ) ) {
|
||
$backup_dir = $this->mkdir( $backup_dir );
|
||
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
return $backup_dir;
|
||
}
|
||
}
|
||
|
||
$this->blog_backup_dir = trailingslashit( $backup_dir );
|
||
|
||
return $this->blog_backup_dir;
|
||
}
|
||
|
||
/**
|
||
* Очищает папку с резервными копиями
|
||
*
|
||
* @return bool
|
||
*/
|
||
public function removeBackupDir() {
|
||
$backup_dir = $this->getBackupDir();
|
||
|
||
return wrio_rmdir( $backup_dir );
|
||
}
|
||
|
||
/**
|
||
* Очищает папку с резервными копиями блога
|
||
* Используется в мультисайт режиме
|
||
*
|
||
* @return bool
|
||
*/
|
||
public function removeBlogBackupDir() {
|
||
$backup_dir = $this->getBlogBackupDir();
|
||
|
||
return wrio_rmdir( $backup_dir );
|
||
}
|
||
|
||
/**
|
||
* Получает путь к папке с резервными копиями
|
||
*
|
||
* @param array $attachment_meta метаданные аттачмента
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getAttachmentBackupDir( $attachment_meta ) {
|
||
$backup_dir = $this->getBackupDir();
|
||
|
||
// Get all subfolders in which the image is stored.
|
||
// This is necessary to create an alternate subfolders
|
||
// in directory where they are stored in backups.
|
||
$subfolders = dirname( $attachment_meta['file'] );
|
||
|
||
$backup_dir .= $subfolders;
|
||
|
||
if ( ! is_dir( $backup_dir ) ) {
|
||
$backup_dir = $this->mkdir( $backup_dir );
|
||
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
return $backup_dir;
|
||
}
|
||
}
|
||
|
||
return trailingslashit( $backup_dir );
|
||
}
|
||
|
||
/**
|
||
* Делаем резервную копию аттачмента
|
||
*
|
||
* @param WIO_Attachment $wio_attachment аттачмент
|
||
*
|
||
* @return bool|WP_Error
|
||
*/
|
||
public function backupAttachment( WIO_Attachment $wio_attachment ) {
|
||
$backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
|
||
|
||
if ( ! $backup_origin_images ) {
|
||
return false; // если бекап не требуется
|
||
}
|
||
|
||
$backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
|
||
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
return $backup_dir;
|
||
}
|
||
|
||
$full = $this->backupAttachmentSize( $wio_attachment );
|
||
|
||
if ( is_wp_error( $full ) ) {
|
||
return $full;
|
||
}
|
||
|
||
$allowed_sizes = $wio_attachment->getAllowedSizes();
|
||
|
||
if ( ! empty( $allowed_sizes ) ) {
|
||
foreach ( (array) $allowed_sizes as $image_size ) {
|
||
$size_backup = $this->backupAttachmentSize( $wio_attachment, $image_size );
|
||
|
||
if ( is_wp_error( $size_backup ) ) {
|
||
return $size_backup;
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Восстанавливаем аттачмент из резервной копии
|
||
*
|
||
* @param WIO_Attachment $wio_attachment аттачмент
|
||
*
|
||
* @return bool|WP_Error
|
||
*/
|
||
public function restoreAttachment( WIO_Attachment $wio_attachment ) {
|
||
$backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
|
||
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
return $backup_dir;
|
||
}
|
||
|
||
$restore_result = $this->restoreAttachmentSize( $wio_attachment );
|
||
|
||
if ( is_wp_error( $restore_result ) ) {
|
||
return $restore_result;
|
||
}
|
||
|
||
$attachment_meta = wp_get_attachment_metadata( $wio_attachment->get( 'id' ) );
|
||
|
||
if ( isset( $attachment_meta['old_width'] ) && isset( $attachment_meta['old_width'] ) ) {
|
||
$attachment_meta['width'] = $attachment_meta['old_width'];
|
||
$attachment_meta['height'] = $attachment_meta['old_height'];
|
||
wp_update_attachment_metadata( $wio_attachment->get( 'id' ), $attachment_meta );
|
||
}
|
||
|
||
$allowed_sizes = $wio_attachment->getAllowedSizes();
|
||
|
||
if ( $allowed_sizes ) {
|
||
foreach ( $allowed_sizes as $image_size ) {
|
||
$this->restoreAttachmentSize( $wio_attachment, $image_size );
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Создает временное изображение с уникальным именем.
|
||
*
|
||
* Необходимо для провайдеров, который кешируют изображения по имени файла,
|
||
* чтобы сбросить кеш, нужно отдать провайдеру изображение с другим именем.
|
||
*
|
||
* @since 1.1.2
|
||
*
|
||
* @param string $file_path путь к изображению
|
||
*
|
||
* @return array|WP_Error
|
||
*/
|
||
public function createTempAttachment( $file_path ) {
|
||
if ( $this->isBackupWritable() ) {
|
||
|
||
$temp_dir = $this->getBackupDir() . self::TEMP_DIR_NAME . '/';
|
||
$temp_dir_url = trailingslashit( $this->wp_upload_dir['baseurl'] ) . self::BACKUP_DIR_NAME . '/' . self::TEMP_DIR_NAME . '/';
|
||
|
||
if ( ! is_dir( $temp_dir ) ) {
|
||
$temp_dir = $this->mkdir( $temp_dir );
|
||
|
||
if ( is_wp_error( $temp_dir ) ) {
|
||
return $temp_dir;
|
||
}
|
||
}
|
||
|
||
$temp_file_id = uniqid();
|
||
$file_name = pathinfo( $file_path, PATHINFO_FILENAME );
|
||
$file_extension = pathinfo( $file_path, PATHINFO_EXTENSION );
|
||
$new_file_name = $temp_file_id . '_' . md5( $file_name ) . '.' . $file_extension;
|
||
|
||
$temp_file_path = $temp_dir . $new_file_name;
|
||
$temp_file_url = $temp_dir_url . $new_file_name;
|
||
|
||
if ( is_file( $file_path ) ) {
|
||
if ( ! @copy( $file_path, $temp_file_path ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap original file %s with %s as copy() failed.', $temp_file_path, $file_path ) );
|
||
|
||
return new WP_Error( 'copy_file_to_temp_dir_error', __( 'Could not copy the file to the temporary directory', 'robin-image-optimizer' ) );
|
||
}
|
||
}
|
||
|
||
WRIO_Plugin::app()->logger->info( sprintf( 'Creation of temporary attachment (%s) successfully completed!', $file_path ) );
|
||
|
||
return [
|
||
'id' => $temp_file_id,
|
||
'image_path' => $temp_file_path,
|
||
'image_url' => $temp_file_url,
|
||
];
|
||
}
|
||
|
||
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' ) );
|
||
}
|
||
|
||
/**
|
||
* Резервное копирование файла аттачмента.
|
||
*
|
||
* @param WIO_Attachment $wio_attachment аттачмент
|
||
* @param string $image_size Размер(thumbnail, medium ... )
|
||
*
|
||
* @return bool|WP_Error
|
||
*/
|
||
protected function backupAttachmentSize( WIO_Attachment $wio_attachment, $image_size = '' ) {
|
||
if ( $image_size ) {
|
||
$original_file = $wio_attachment->getImageSizePath( $image_size );
|
||
} else {
|
||
$original_file = $wio_attachment->get( 'path' );
|
||
}
|
||
|
||
$backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
|
||
|
||
// проверить запись в папку
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to create backup dir, error: %s', $backup_dir->get_error_message() ) );
|
||
|
||
return $backup_dir;
|
||
}
|
||
|
||
if ( ! $original_file ) {
|
||
// бывает такое, что размера превьюшки нет в базе данных.
|
||
// это не считается ошибкой, поэтому сразу пропускаем
|
||
return false;
|
||
}
|
||
|
||
$backup_file = $backup_dir . wp_basename( $original_file );
|
||
|
||
if ( is_file( $original_file ) ) {
|
||
if ( ! @copy( $original_file, $backup_file ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to copy %s to %s as copy() failed', $original_file, $backup_file ) );
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Восстановление файла аттачмента из резервной копии
|
||
*
|
||
* @param WIO_Attachment $wio_attachment аттачмент
|
||
* @param string|null $image_size Размер(thumbnail, medium ... )
|
||
*
|
||
* @return bool|WP_Error
|
||
*/
|
||
protected function restoreAttachmentSize( WIO_Attachment $wio_attachment, $image_size = null ) {
|
||
|
||
if ( ! empty( $image_size ) ) {
|
||
$original_file = $wio_attachment->getImageSizePath( $image_size );
|
||
} else {
|
||
$original_file = $wio_attachment->get( 'path' );
|
||
}
|
||
|
||
$backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
|
||
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
return $backup_dir;
|
||
}
|
||
|
||
if ( empty( $original_file ) ) {
|
||
return false;
|
||
}
|
||
|
||
$backup_file = $backup_dir . wp_basename( $original_file );
|
||
|
||
if ( ! is_file( $backup_file ) ) {
|
||
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 ) );
|
||
|
||
return false;
|
||
}
|
||
|
||
if ( ! @copy( $backup_file, $original_file ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap %s with %s as copy() failed', $backup_file, $original_file ) );
|
||
|
||
return false;
|
||
}
|
||
|
||
if ( ! @unlink( $backup_file ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to delete backup file %s as unlink() failed', $backup_file ) );
|
||
|
||
return false;
|
||
}
|
||
|
||
WRIO_Plugin::app()->logger->info( sprintf( 'Restored file: %s.', $backup_file ) );
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Get the backup file path for an attachment size if it exists.
|
||
*
|
||
* @param int $attachment_id The attachment ID.
|
||
* @param string $size The image size (e.g., 'original', 'thumbnail', 'medium').
|
||
*
|
||
* @return string|null The backup file path if it exists, null otherwise.
|
||
* @since 1.0.0
|
||
*/
|
||
public function getAttachmentBackupPath( $attachment_id, $size = 'original' ) {
|
||
$attachment_meta = wp_get_attachment_metadata( $attachment_id );
|
||
|
||
if ( empty( $attachment_meta ) || ! isset( $attachment_meta['file'] ) ) {
|
||
return null;
|
||
}
|
||
|
||
$backup_dir = $this->getAttachmentBackupDir( $attachment_meta );
|
||
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
return null;
|
||
}
|
||
|
||
// Get the filename based on size
|
||
if ( 'original' === $size ) {
|
||
$filename = wp_basename( $attachment_meta['file'] );
|
||
} elseif ( isset( $attachment_meta['sizes'][ $size ]['file'] ) ) {
|
||
$filename = $attachment_meta['sizes'][ $size ]['file'];
|
||
} else {
|
||
return null;
|
||
}
|
||
|
||
$backup_path = $backup_dir . $filename;
|
||
|
||
return file_exists( $backup_path ) ? $backup_path : null;
|
||
}
|
||
|
||
/**
|
||
* Удаляем резервные копии аттачмента
|
||
*
|
||
* @param int $attachment_id аттачмент id
|
||
*
|
||
* @return bool|WP_Error
|
||
*/
|
||
public function removeAttachmentBackup( $attachment_id ) {
|
||
$attachment_meta = wp_get_attachment_metadata( $attachment_id );
|
||
$backup_dir = $this->getAttachmentBackupDir( $attachment_meta );
|
||
|
||
if ( is_wp_error( $backup_dir ) ) {
|
||
return $backup_dir;
|
||
}
|
||
|
||
$main_file_path = $backup_dir . wp_basename( $attachment_meta['file'] );
|
||
|
||
if ( ! file_exists( $main_file_path ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( "Failed to remove an attachment file. File (%s) isn't exists. Attachment #%s", $main_file_path, $attachment_id ) );
|
||
}
|
||
|
||
if ( ! @unlink( $main_file_path ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to unlink a main file (%s) for attachment #%s', $main_file_path, $attachment_id ) );
|
||
}
|
||
|
||
if ( isset( $attachment_meta['sizes'] ) && is_array( $attachment_meta['sizes'] ) ) {
|
||
foreach ( $attachment_meta['sizes'] as $size ) {
|
||
$thumbnail_file_path = $backup_dir . $size['file'];
|
||
|
||
if ( ! file_exists( $thumbnail_file_path ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( "Failed to remove a thumbnail file. File (%s) isn't exists. Attachment #%s", $thumbnail_file_path, $attachment_id ) );
|
||
}
|
||
|
||
if ( ! @unlink( $thumbnail_file_path ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to unlink thumbnail (%s) for attachment #%s', $thumbnail_file_path, $attachment_id ) );
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* alternateStorage
|
||
*
|
||
* @param array $servers
|
||
*
|
||
* @return array
|
||
*/
|
||
public static function alternateStorage( $servers ) {
|
||
|
||
return $servers;
|
||
}
|
||
|
||
/**
|
||
* @since 1.3.0
|
||
*
|
||
* @param string $dir
|
||
*
|
||
* @return string|WP_Error
|
||
*/
|
||
protected function mkdir( $dir ) {
|
||
WRIO_Plugin::app()->logger->info( sprintf( 'Try to create backup directory. Backup dir: (%s)', $dir ) );
|
||
|
||
if ( ! wp_mkdir_p( $dir ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Unable to create backup directory (%s) as mkdir() failed', $dir ) );
|
||
|
||
return new WP_Error( 'mkdir_failed', sprintf( 'Unable to create backup folder (%s) as mkdir() failed.', $dir ) );
|
||
}
|
||
|
||
return $dir;
|
||
}
|
||
|
||
/**
|
||
* @since 1.3.0
|
||
*
|
||
* @param string $backup_file
|
||
* @param string $original_file
|
||
*
|
||
* @return bool
|
||
*/
|
||
protected function restore_file( $backup_file, $original_file ) {
|
||
if ( is_file( $backup_file ) ) {
|
||
if ( ! @copy( $backup_file, $original_file ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap original file (%s) with %s as copy() failed', $backup_file, $original_file ) );
|
||
|
||
return false;
|
||
}
|
||
|
||
if ( ! @unlink( $backup_file ) ) {
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to delete backup file (%s) as unlink() failed', $backup_file ) );
|
||
|
||
return false;
|
||
}
|
||
|
||
WRIO_Plugin::app()->logger->info( sprintf( 'Restored file: %s.', $backup_file ) );
|
||
|
||
return true;
|
||
}
|
||
|
||
WRIO_Plugin::app()->logger->error( sprintf( 'Unable to restore from a backup. There is no file (%s).', $backup_file ) );
|
||
|
||
return false;
|
||
}
|
||
}
|