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; } }