Because cats have 9 lives, but servers don't - so they need backup-restore! Complete backup solution with S3/MinIO support. - Full WordPress backup (files + database) - S3 / MinIO / S3-compatible storage backends - Scheduled automatic backups - Disaster recovery / one-click restore - Backup integrity validation - Cat-themed admin interface Includes build.sh and .distignore for WordPress-installable release ZIPs.
861 lines
27 KiB
PHP
861 lines
27 KiB
PHP
<?php
|
|
/**
|
|
* Backup Engine
|
|
*
|
|
* Core backup functionality with security-first approach
|
|
* Addresses all backup-related vulnerabilities found in XCloner
|
|
*
|
|
* @package TigerStyleLife9
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Backup engine class
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class TigerStyle_Life9_Backup_Engine {
|
|
|
|
/**
|
|
* Security instance
|
|
*
|
|
* @var TigerStyle_Life9_Security
|
|
*/
|
|
private $security;
|
|
|
|
/**
|
|
* File scanner instance
|
|
*
|
|
* @var TigerStyle_Life9_File_Scanner
|
|
*/
|
|
private $file_scanner;
|
|
|
|
/**
|
|
* Database backup instance
|
|
*
|
|
* @var TigerStyle_Life9_Database_Backup
|
|
*/
|
|
private $database_backup;
|
|
|
|
/**
|
|
* Storage manager instance
|
|
*
|
|
* @var TigerStyle_Life9_Storage_Manager
|
|
*/
|
|
private $storage_manager;
|
|
|
|
/**
|
|
* Current backup ID
|
|
*
|
|
* @var int
|
|
*/
|
|
private $backup_id;
|
|
|
|
/**
|
|
* Backup configuration
|
|
*
|
|
* @var array
|
|
*/
|
|
private $config;
|
|
|
|
/**
|
|
* Backup progress
|
|
*
|
|
* @var array
|
|
*/
|
|
private $progress;
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
$this->security = tigerstyle_life9()->get_security();
|
|
$this->file_scanner = new TigerStyle_Life9_File_Scanner();
|
|
$this->database_backup = new TigerStyle_Life9_Database_Backup();
|
|
$this->storage_manager = new TigerStyle_Life9_Storage_Manager();
|
|
|
|
$this->progress = [
|
|
'stage' => 'idle',
|
|
'progress' => 0,
|
|
'files_processed' => 0,
|
|
'total_files' => 0,
|
|
'current_file' => '',
|
|
'bytes_processed' => 0,
|
|
'total_bytes' => 0
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Start backup process
|
|
*
|
|
* @param array $config Backup configuration
|
|
* @return int|false Backup ID or false on failure
|
|
*/
|
|
public function start_backup($config) {
|
|
try {
|
|
// Validate configuration
|
|
$validator = new TigerStyle_Life9_Validator();
|
|
if (!$validator->validate_backup_config($config)) {
|
|
$this->log_error('Invalid backup configuration', $validator->get_errors());
|
|
return false;
|
|
}
|
|
|
|
$this->config = $config;
|
|
|
|
// Create backup record
|
|
$this->backup_id = $this->create_backup_record();
|
|
if (!$this->backup_id) {
|
|
return false;
|
|
}
|
|
|
|
// Log backup start
|
|
$this->security->log_security_event('backup_started', [
|
|
'backup_id' => $this->backup_id,
|
|
'backup_name' => $config['backup_name'] ?? 'Unnamed',
|
|
'backup_type' => $config['backup_type'] ?? 'full'
|
|
]);
|
|
|
|
// Execute backup asynchronously
|
|
wp_schedule_single_event(time(), 'tigerstyle_life9_execute_backup', [$this->backup_id, $config]);
|
|
|
|
return $this->backup_id;
|
|
|
|
} catch (Exception $e) {
|
|
$this->log_error('Backup start failed', ['error' => $e->getMessage()]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute backup process
|
|
*
|
|
* @param int $backup_id Backup ID
|
|
* @param array $config Backup configuration
|
|
*/
|
|
public function execute_backup($backup_id, $config) {
|
|
$this->backup_id = $backup_id;
|
|
$this->config = $config;
|
|
|
|
try {
|
|
$this->update_backup_status('running');
|
|
$this->log_info('Backup execution started');
|
|
|
|
// Create temporary working directory
|
|
$temp_dir = $this->create_temp_directory();
|
|
if (!$temp_dir) {
|
|
throw new Exception('Failed to create temporary directory');
|
|
}
|
|
|
|
$backup_parts = [];
|
|
|
|
// Stage 1: Database backup
|
|
if (!empty($config['include_database'])) {
|
|
$this->update_progress('database', 0);
|
|
$db_file = $this->backup_database($temp_dir);
|
|
if ($db_file) {
|
|
$backup_parts['database'] = $db_file;
|
|
$this->log_info('Database backup completed');
|
|
} else {
|
|
throw new Exception('Database backup failed');
|
|
}
|
|
$this->update_progress('database', 100);
|
|
}
|
|
|
|
// Stage 2: Files backup
|
|
if (!empty($config['include_files'])) {
|
|
$this->update_progress('files', 0);
|
|
$files_archive = $this->backup_files($temp_dir);
|
|
if ($files_archive) {
|
|
$backup_parts['files'] = $files_archive;
|
|
$this->log_info('Files backup completed');
|
|
} else {
|
|
throw new Exception('Files backup failed');
|
|
}
|
|
$this->update_progress('files', 100);
|
|
}
|
|
|
|
// Stage 3: Create final archive
|
|
$this->update_progress('archive', 0);
|
|
$final_archive = $this->create_final_archive($backup_parts, $temp_dir);
|
|
if (!$final_archive) {
|
|
throw new Exception('Failed to create final archive');
|
|
}
|
|
$this->update_progress('archive', 100);
|
|
|
|
// Stage 4: Storage and cleanup
|
|
$this->update_progress('storage', 0);
|
|
$stored_path = $this->store_backup($final_archive);
|
|
if (!$stored_path) {
|
|
throw new Exception('Failed to store backup');
|
|
}
|
|
|
|
// Generate checksum
|
|
$encryption = new TigerStyle_Life9_Encryption();
|
|
$checksum = $encryption->file_checksum($stored_path);
|
|
|
|
// Update backup record
|
|
$this->finalize_backup_record($stored_path, filesize($stored_path), $checksum);
|
|
|
|
// Cleanup temporary files
|
|
$this->cleanup_temp_directory($temp_dir);
|
|
|
|
$this->update_backup_status('completed');
|
|
$this->log_info('Backup completed successfully');
|
|
|
|
// Log completion
|
|
$this->security->log_security_event('backup_completed', [
|
|
'backup_id' => $this->backup_id,
|
|
'file_size' => filesize($stored_path),
|
|
'checksum' => $checksum
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
$this->log_error('Backup failed', ['error' => $e->getMessage()]);
|
|
$this->update_backup_status('failed');
|
|
|
|
// Cleanup on failure
|
|
if (isset($temp_dir)) {
|
|
$this->cleanup_temp_directory($temp_dir);
|
|
}
|
|
if (isset($stored_path) && file_exists($stored_path)) {
|
|
unlink($stored_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cancel backup process
|
|
*
|
|
* @param int $backup_id Backup ID
|
|
* @return bool Success status
|
|
*/
|
|
public function cancel_backup($backup_id) {
|
|
try {
|
|
global $wpdb;
|
|
|
|
$backup = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM {$wpdb->prefix}tigerstyle_life9_backups WHERE id = %d",
|
|
$backup_id
|
|
));
|
|
|
|
if (!$backup) {
|
|
return false;
|
|
}
|
|
|
|
if (!in_array($backup->status, ['pending', 'running'])) {
|
|
return false; // Cannot cancel completed/failed backups
|
|
}
|
|
|
|
// Update status
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'tigerstyle_life9_backups',
|
|
['status' => 'cancelled', 'completed_at' => current_time('mysql')],
|
|
['id' => $backup_id],
|
|
['%s', '%s'],
|
|
['%d']
|
|
);
|
|
|
|
// Log cancellation
|
|
$this->log_info('Backup cancelled by user', $backup_id);
|
|
|
|
$this->security->log_security_event('backup_cancelled', [
|
|
'backup_id' => $backup_id
|
|
]);
|
|
|
|
return true;
|
|
|
|
} catch (Exception $e) {
|
|
error_log('TigerStyle Life9: Cancel backup error - ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create backup record in database
|
|
*
|
|
* @return int|false Backup ID or false on failure
|
|
*/
|
|
private function create_backup_record() {
|
|
try {
|
|
global $wpdb;
|
|
|
|
$result = $wpdb->insert(
|
|
$wpdb->prefix . 'tigerstyle_life9_backups',
|
|
[
|
|
'name' => $this->config['backup_name'] ?? 'Backup ' . date('Y-m-d H:i:s'),
|
|
'status' => 'pending',
|
|
'created_at' => current_time('mysql'),
|
|
'backup_type' => $this->config['backup_type'] ?? 'full',
|
|
'includes_files' => !empty($this->config['include_files']) ? 1 : 0,
|
|
'includes_database' => !empty($this->config['include_database']) ? 1 : 0,
|
|
'compression' => $this->config['compression_method'] ?? 'zip',
|
|
'settings' => wp_json_encode($this->config)
|
|
],
|
|
['%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s']
|
|
);
|
|
|
|
return $result ? $wpdb->insert_id : false;
|
|
|
|
} catch (Exception $e) {
|
|
error_log('TigerStyle Life9: Create backup record error - ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Backup database
|
|
*
|
|
* @param string $temp_dir Temporary directory
|
|
* @return string|false Database backup file path or false on failure
|
|
*/
|
|
private function backup_database($temp_dir) {
|
|
try {
|
|
$this->log_info('Starting database backup');
|
|
|
|
$db_config = [
|
|
'include_tables' => $this->config['database_tables'] ?? [],
|
|
'exclude_tables' => $this->config['exclude_database_tables'] ?? [],
|
|
'add_drop_table' => true,
|
|
'add_if_not_exists' => false,
|
|
'disable_keys' => true,
|
|
'where_conditions' => $this->config['database_where'] ?? []
|
|
];
|
|
|
|
$db_file = $temp_dir . '/database.sql';
|
|
$success = $this->database_backup->export_database($db_file, $db_config);
|
|
|
|
if ($success && file_exists($db_file)) {
|
|
// Compress database file
|
|
$compressed_file = $temp_dir . '/database.sql.gz';
|
|
if ($this->compress_file($db_file, $compressed_file)) {
|
|
unlink($db_file); // Remove uncompressed version
|
|
return $compressed_file;
|
|
}
|
|
return $db_file;
|
|
}
|
|
|
|
return false;
|
|
|
|
} catch (Exception $e) {
|
|
$this->log_error('Database backup failed', ['error' => $e->getMessage()]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Backup files
|
|
*
|
|
* @param string $temp_dir Temporary directory
|
|
* @return string|false Files backup archive path or false on failure
|
|
*/
|
|
private function backup_files($temp_dir) {
|
|
try {
|
|
$this->log_info('Starting files backup');
|
|
|
|
// Scan files to backup
|
|
$scan_config = [
|
|
'include_paths' => $this->config['include_paths'] ?? [ABSPATH],
|
|
'exclude_patterns' => $this->get_exclude_patterns(),
|
|
'follow_symlinks' => false,
|
|
'max_file_size' => $this->get_max_file_size()
|
|
];
|
|
|
|
$files = $this->file_scanner->scan_files($scan_config);
|
|
|
|
if (empty($files)) {
|
|
$this->log_error('No files found to backup');
|
|
return false;
|
|
}
|
|
|
|
$this->progress['total_files'] = count($files);
|
|
$this->progress['total_bytes'] = array_sum(array_column($files, 'size'));
|
|
|
|
// Create files archive
|
|
$archive_file = $temp_dir . '/files.' . $this->get_compression_extension();
|
|
|
|
switch ($this->config['compression_method'] ?? 'zip') {
|
|
case 'zip':
|
|
$success = $this->create_zip_archive($files, $archive_file);
|
|
break;
|
|
|
|
case 'tar':
|
|
$success = $this->create_tar_archive($files, $archive_file);
|
|
break;
|
|
|
|
default:
|
|
$success = $this->create_zip_archive($files, $archive_file);
|
|
}
|
|
|
|
return $success ? $archive_file : false;
|
|
|
|
} catch (Exception $e) {
|
|
$this->log_error('Files backup failed', ['error' => $e->getMessage()]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create ZIP archive
|
|
*
|
|
* @param array $files List of files
|
|
* @param string $archive_path Archive file path
|
|
* @return bool Success status
|
|
*/
|
|
private function create_zip_archive($files, $archive_path) {
|
|
if (!class_exists('ZipArchive')) {
|
|
$this->log_error('ZipArchive class not available');
|
|
return false;
|
|
}
|
|
|
|
$zip = new ZipArchive();
|
|
$result = $zip->open($archive_path, ZipArchive::CREATE | ZipArchive::OVERWRITE);
|
|
|
|
if ($result !== true) {
|
|
$this->log_error('Failed to create ZIP archive', ['error_code' => $result]);
|
|
return false;
|
|
}
|
|
|
|
$processed = 0;
|
|
$base_path = rtrim(ABSPATH, '/');
|
|
|
|
foreach ($files as $file) {
|
|
if (!$this->security->validate_path($file['path'], ABSPATH)) {
|
|
$this->log_error('Invalid file path skipped', ['path' => $file['path']]);
|
|
continue;
|
|
}
|
|
|
|
if (!file_exists($file['path']) || !is_readable($file['path'])) {
|
|
$this->log_error('File not readable, skipped', ['path' => $file['path']]);
|
|
continue;
|
|
}
|
|
|
|
// Get relative path for archive
|
|
$relative_path = ltrim(str_replace($base_path, '', $file['path']), '/');
|
|
|
|
if ($file['type'] === 'directory') {
|
|
$zip->addEmptyDir($relative_path);
|
|
} else {
|
|
$zip->addFile($file['path'], $relative_path);
|
|
}
|
|
|
|
$processed++;
|
|
$this->progress['files_processed'] = $processed;
|
|
$this->progress['current_file'] = $relative_path;
|
|
$this->progress['progress'] = round(($processed / $this->progress['total_files']) * 100);
|
|
|
|
// Update progress periodically
|
|
if ($processed % 100 === 0) {
|
|
$this->update_progress('files', $this->progress['progress']);
|
|
}
|
|
}
|
|
|
|
$result = $zip->close();
|
|
|
|
if (!$result) {
|
|
$this->log_error('Failed to finalize ZIP archive');
|
|
return false;
|
|
}
|
|
|
|
$this->log_info('ZIP archive created successfully', [
|
|
'file_count' => $processed,
|
|
'archive_size' => filesize($archive_path)
|
|
]);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Create TAR archive
|
|
*
|
|
* @param array $files List of files
|
|
* @param string $archive_path Archive file path
|
|
* @return bool Success status
|
|
*/
|
|
private function create_tar_archive($files, $archive_path) {
|
|
try {
|
|
$tar_command = 'tar -czf ' . escapeshellarg($archive_path);
|
|
$base_path = rtrim(ABSPATH, '/');
|
|
|
|
// Create file list
|
|
$file_list = tempnam(sys_get_temp_dir(), 'tigerstyle_life9_files_');
|
|
$handle = fopen($file_list, 'w');
|
|
|
|
if (!$handle) {
|
|
throw new Exception('Failed to create file list');
|
|
}
|
|
|
|
foreach ($files as $file) {
|
|
if (!$this->security->validate_path($file['path'], ABSPATH)) {
|
|
continue;
|
|
}
|
|
|
|
$relative_path = ltrim(str_replace($base_path, '', $file['path']), '/');
|
|
fwrite($handle, $relative_path . "\n");
|
|
}
|
|
|
|
fclose($handle);
|
|
|
|
// Execute tar command
|
|
$tar_command .= ' -C ' . escapeshellarg($base_path) . ' -T ' . escapeshellarg($file_list);
|
|
|
|
exec($tar_command . ' 2>&1', $output, $return_code);
|
|
|
|
// Cleanup file list
|
|
unlink($file_list);
|
|
|
|
if ($return_code !== 0) {
|
|
$this->log_error('TAR command failed', [
|
|
'command' => $tar_command,
|
|
'output' => implode("\n", $output),
|
|
'return_code' => $return_code
|
|
]);
|
|
return false;
|
|
}
|
|
|
|
$this->log_info('TAR archive created successfully', [
|
|
'archive_size' => filesize($archive_path)
|
|
]);
|
|
|
|
return true;
|
|
|
|
} catch (Exception $e) {
|
|
$this->log_error('TAR archive creation failed', ['error' => $e->getMessage()]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create final backup archive
|
|
*
|
|
* @param array $backup_parts Individual backup parts
|
|
* @param string $temp_dir Temporary directory
|
|
* @return string|false Final archive path or false on failure
|
|
*/
|
|
private function create_final_archive($backup_parts, $temp_dir) {
|
|
try {
|
|
$final_archive = $temp_dir . '/backup_' . date('Y-m-d_H-i-s') . '.zip';
|
|
|
|
$zip = new ZipArchive();
|
|
$result = $zip->open($final_archive, ZipArchive::CREATE | ZipArchive::OVERWRITE);
|
|
|
|
if ($result !== true) {
|
|
$this->log_error('Failed to create final archive', ['error_code' => $result]);
|
|
return false;
|
|
}
|
|
|
|
// Add backup parts to final archive
|
|
foreach ($backup_parts as $type => $file_path) {
|
|
if (file_exists($file_path)) {
|
|
$zip->addFile($file_path, basename($file_path));
|
|
}
|
|
}
|
|
|
|
// Add backup manifest
|
|
$manifest = [
|
|
'backup_id' => $this->backup_id,
|
|
'created_at' => current_time('mysql'),
|
|
'wordpress_version' => get_bloginfo('version'),
|
|
'php_version' => PHP_VERSION,
|
|
'plugin_version' => TIGERSTYLE_LIFE9_VERSION,
|
|
'backup_type' => $this->config['backup_type'] ?? 'full',
|
|
'includes_files' => !empty($this->config['include_files']),
|
|
'includes_database' => !empty($this->config['include_database']),
|
|
'parts' => array_keys($backup_parts)
|
|
];
|
|
|
|
$zip->addFromString('backup-manifest.json', wp_json_encode($manifest, JSON_PRETTY_PRINT));
|
|
|
|
$result = $zip->close();
|
|
|
|
if (!$result) {
|
|
$this->log_error('Failed to finalize backup archive');
|
|
return false;
|
|
}
|
|
|
|
return $final_archive;
|
|
|
|
} catch (Exception $e) {
|
|
$this->log_error('Final archive creation failed', ['error' => $e->getMessage()]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Store backup in final location
|
|
*
|
|
* @param string $archive_path Temporary archive path
|
|
* @return string|false Final storage path or false on failure
|
|
*/
|
|
private function store_backup($archive_path) {
|
|
try {
|
|
$upload_dir = wp_upload_dir();
|
|
$backup_dir = $upload_dir['basedir'] . '/tigerstyle-life9/backups';
|
|
|
|
// Ensure backup directory exists
|
|
if (!file_exists($backup_dir)) {
|
|
wp_mkdir_p($backup_dir);
|
|
}
|
|
|
|
$filename = 'backup_' . $this->backup_id . '_' . date('Y-m-d_H-i-s') . '.zip';
|
|
$final_path = $backup_dir . '/' . $filename;
|
|
|
|
// Move archive to final location
|
|
if (!rename($archive_path, $final_path)) {
|
|
throw new Exception('Failed to move backup to final location');
|
|
}
|
|
|
|
// Set proper permissions
|
|
chmod($final_path, 0644);
|
|
|
|
$this->log_info('Backup stored successfully', [
|
|
'path' => $final_path,
|
|
'size' => filesize($final_path)
|
|
]);
|
|
|
|
return $final_path;
|
|
|
|
} catch (Exception $e) {
|
|
$this->log_error('Backup storage failed', ['error' => $e->getMessage()]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finalize backup record
|
|
*
|
|
* @param string $file_path Final backup file path
|
|
* @param int $file_size File size in bytes
|
|
* @param string $checksum File checksum
|
|
*/
|
|
private function finalize_backup_record($file_path, $file_size, $checksum) {
|
|
global $wpdb;
|
|
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'tigerstyle_life9_backups',
|
|
[
|
|
'file_path' => $file_path,
|
|
'file_size' => $file_size,
|
|
'hash' => $checksum,
|
|
'completed_at' => current_time('mysql')
|
|
],
|
|
['id' => $this->backup_id],
|
|
['%s', '%d', '%s', '%s'],
|
|
['%d']
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create temporary directory
|
|
*
|
|
* @return string|false Temporary directory path or false on failure
|
|
*/
|
|
private function create_temp_directory() {
|
|
$upload_dir = wp_upload_dir();
|
|
$temp_base = $upload_dir['basedir'] . '/tigerstyle-life9/temp';
|
|
|
|
if (!file_exists($temp_base)) {
|
|
wp_mkdir_p($temp_base);
|
|
}
|
|
|
|
$temp_dir = $temp_base . '/backup_' . $this->backup_id . '_' . time();
|
|
|
|
if (wp_mkdir_p($temp_dir)) {
|
|
return $temp_dir;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Cleanup temporary directory
|
|
*
|
|
* @param string $temp_dir Temporary directory path
|
|
*/
|
|
private function cleanup_temp_directory($temp_dir) {
|
|
if (file_exists($temp_dir)) {
|
|
$this->recursive_rmdir($temp_dir);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively remove directory
|
|
*
|
|
* @param string $dir Directory path
|
|
*/
|
|
private function recursive_rmdir($dir) {
|
|
if (is_dir($dir)) {
|
|
$objects = scandir($dir);
|
|
foreach ($objects as $object) {
|
|
if ($object != "." && $object != "..") {
|
|
if (is_dir($dir . "/" . $object)) {
|
|
$this->recursive_rmdir($dir . "/" . $object);
|
|
} else {
|
|
unlink($dir . "/" . $object);
|
|
}
|
|
}
|
|
}
|
|
rmdir($dir);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get exclude patterns
|
|
*
|
|
* @return array
|
|
*/
|
|
private function get_exclude_patterns() {
|
|
$default_patterns = [
|
|
'*.log',
|
|
'*.tmp',
|
|
'*~',
|
|
'.DS_Store',
|
|
'Thumbs.db',
|
|
'wp-content/cache/*',
|
|
'wp-content/backup/*',
|
|
'wp-content/uploads/tigerstyle-life9/*'
|
|
];
|
|
|
|
$custom_patterns = $this->config['exclude_patterns'] ?? [];
|
|
|
|
return array_merge($default_patterns, $custom_patterns);
|
|
}
|
|
|
|
/**
|
|
* Get maximum file size for backup
|
|
*
|
|
* @return int Maximum file size in bytes
|
|
*/
|
|
private function get_max_file_size() {
|
|
$max_size_mb = get_option('tigerstyle_life9_max_backup_size_mb', 1000);
|
|
return $max_size_mb * 1024 * 1024;
|
|
}
|
|
|
|
/**
|
|
* Get compression file extension
|
|
*
|
|
* @return string
|
|
*/
|
|
private function get_compression_extension() {
|
|
switch ($this->config['compression_method'] ?? 'zip') {
|
|
case 'tar':
|
|
return 'tar.gz';
|
|
case 'gzip':
|
|
return 'gz';
|
|
default:
|
|
return 'zip';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compress file using gzip
|
|
*
|
|
* @param string $source Source file path
|
|
* @param string $destination Destination file path
|
|
* @return bool Success status
|
|
*/
|
|
private function compress_file($source, $destination) {
|
|
if (!function_exists('gzencode')) {
|
|
return false;
|
|
}
|
|
|
|
$data = file_get_contents($source);
|
|
if ($data === false) {
|
|
return false;
|
|
}
|
|
|
|
$compressed = gzencode($data, 9);
|
|
if ($compressed === false) {
|
|
return false;
|
|
}
|
|
|
|
return file_put_contents($destination, $compressed) !== false;
|
|
}
|
|
|
|
/**
|
|
* Update backup status
|
|
*
|
|
* @param string $status New status
|
|
*/
|
|
private function update_backup_status($status) {
|
|
global $wpdb;
|
|
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'tigerstyle_life9_backups',
|
|
['status' => $status],
|
|
['id' => $this->backup_id],
|
|
['%s'],
|
|
['%d']
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Update backup progress
|
|
*
|
|
* @param string $stage Current stage
|
|
* @param int $progress Progress percentage
|
|
*/
|
|
private function update_progress($stage, $progress) {
|
|
$this->progress['stage'] = $stage;
|
|
$this->progress['progress'] = $progress;
|
|
|
|
// Store progress in database or cache for real-time updates
|
|
set_transient('tigerstyle_life9_backup_progress_' . $this->backup_id, $this->progress, 300);
|
|
}
|
|
|
|
/**
|
|
* Log info message
|
|
*
|
|
* @param string $message Log message
|
|
* @param array $context Additional context
|
|
*/
|
|
private function log_info($message, $context = []) {
|
|
$this->log_message('info', $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Log error message
|
|
*
|
|
* @param string $message Log message
|
|
* @param array $context Additional context
|
|
*/
|
|
private function log_error($message, $context = []) {
|
|
$this->log_message('error', $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Log message to database
|
|
*
|
|
* @param string $level Log level
|
|
* @param string $message Log message
|
|
* @param array $context Additional context
|
|
*/
|
|
private function log_message($level, $message, $context = []) {
|
|
global $wpdb;
|
|
|
|
$wpdb->insert(
|
|
$wpdb->prefix . 'tigerstyle_life9_logs',
|
|
[
|
|
'backup_id' => $this->backup_id ?? 0,
|
|
'level' => $level,
|
|
'message' => $message,
|
|
'context' => wp_json_encode($context),
|
|
'created_at' => current_time('mysql')
|
|
],
|
|
['%d', '%s', '%s', '%s', '%s']
|
|
);
|
|
|
|
// Also log to WordPress error log for debugging
|
|
error_log("TigerStyle Life9 [{$level}]: {$message}");
|
|
}
|
|
}
|
|
|
|
// Register backup execution hook
|
|
add_action('tigerstyle_life9_execute_backup', function($backup_id, $config) {
|
|
$backup_engine = new TigerStyle_Life9_Backup_Engine();
|
|
$backup_engine->execute_backup($backup_id, $config);
|
|
}, 10, 2); |